Set up comprehensive monitoring for your Tailscale mesh VPN network using Prometheus metrics collection and Grafana dashboards to track node connectivity, traffic patterns, and performance across your distributed infrastructure.
Prerequisites
- Root or sudo access
- Internet connectivity
- Basic knowledge of systemd services
- Familiarity with VPN concepts
What this solves
Tailscale creates secure mesh VPN networks between your devices, but monitoring network health, connection status, and traffic patterns requires visibility tools. This tutorial configures Prometheus to collect Tailscale metrics and creates Grafana dashboards for real-time monitoring of your mesh network performance, node connectivity, and traffic analytics.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all components.
sudo apt update && sudo apt upgrade -yInstall Tailscale mesh VPN
Install Tailscale using the official installation script which automatically configures the package repository and installs the client.
curl -fsSL https://tailscale.com/install.sh | shStart Tailscale and authenticate with your account. This command will display a URL for device authentication.
sudo tailscale upVerify the Tailscale connection is active and note your assigned IP address.
tailscale status
tailscale ipInstall Prometheus
Install Prometheus for metrics collection and monitoring. Create a dedicated user for running Prometheus securely.
sudo apt install -y prometheus prometheus-node-exporterCreate directories for Prometheus data and configuration with proper ownership.
sudo mkdir -p /var/lib/prometheus
sudo chown prometheus:prometheus /var/lib/prometheusInstall Tailscale Prometheus exporter
Download and install the Tailscale Prometheus exporter to collect mesh VPN metrics. This provides detailed statistics about node connections, traffic, and network health.
wget https://github.com/tailscale/tailscale/releases/latest/download/tailscale-prometheus-exporter_linux_amd64.tar.gz
tar -xzf tailscale-prometheus-exporter_linux_amd64.tar.gz
sudo mv tailscale-prometheus-exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/tailscale-prometheus-exporterCreate a systemd service file for the Tailscale exporter to run automatically.
[Unit]
Description=Tailscale Prometheus Exporter
After=network.target tailscaled.service
Requires=tailscaled.service
[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/tailscale-prometheus-exporter --listen-addr=127.0.0.1:9090
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetConfigure Prometheus for Tailscale monitoring
Configure Prometheus to scrape metrics from the Tailscale exporter and system node exporter for comprehensive monitoring.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "tailscale_alerts.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
- job_name: 'tailscale'
static_configs:
- targets: ['localhost:9090']
scrape_interval: 30s
metrics_path: '/metrics'
- job_name: 'tailscale-nodes'
tailscale_sd_configs:
- url: "https://api.tailscale.com/api/v2/tailnet/"
refresh_interval: 60sCreate alerting rules for Tailscale network monitoring to detect connectivity issues and performance problems.
groups:
- name: tailscale
rules:
- alert: TailscaleNodeDown
expr: up{job="tailscale"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Tailscale node is down"
description: "Tailscale node {{ $labels.instance }} has been down for more than 2 minutes."
- alert: TailscaleHighLatency
expr: tailscale_node_latency_seconds > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High Tailscale latency detected"
description: "Tailscale node {{ $labels.node }} has latency {{ $value }}s for more than 5 minutes."
- alert: TailscaleConnectionLoss
expr: increase(tailscale_connection_failures_total[10m]) > 3
for: 1m
labels:
severity: critical
annotations:
summary: "Frequent Tailscale connection failures"
description: "Node {{ $labels.instance }} has {{ $value }} connection failures in the last 10 minutes."Install and configure Grafana
Install Grafana for creating dashboards and visualizing Tailscale network metrics.
sudo apt install -y software-properties-common
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafanaConfigure Grafana with security settings and enable anonymous access for dashboards if needed.
[server]
http_port = 3000
root_url = http://localhost:3000
[security]
admin_user = admin
admin_password = your_secure_password_here
secret_key = your_secret_key_here
[auth.anonymous]
enabled = false
[dashboards]
default_home_dashboard_path = /var/lib/grafana/dashboards/tailscale-overview.jsonEnable and start services
Enable and start all monitoring services to begin collecting Tailscale metrics.
sudo systemctl daemon-reload
sudo systemctl enable --now tailscale-exporter
sudo systemctl enable --now prometheus
sudo systemctl enable --now grafana-serverVerify all services are running correctly.
sudo systemctl status tailscale-exporter
sudo systemctl status prometheus
sudo systemctl status grafana-serverConfigure Grafana data source
Access Grafana web interface and configure Prometheus as a data source for Tailscale metrics.
curl -X POST http://admin:your_secure_password_here@localhost:3000/api/datasources \
-H "Content-Type: application/json" \
-d '{
"name": "Prometheus",
"type": "prometheus",
"url": "http://localhost:9090",
"access": "proxy",
"isDefault": true
}'Create Tailscale dashboard
Create a comprehensive Grafana dashboard for monitoring Tailscale mesh network performance and connectivity.
{
"dashboard": {
"id": null,
"title": "Tailscale Network Overview",
"tags": ["tailscale", "vpn", "networking"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Connected Nodes",
"type": "stat",
"targets": [
{
"expr": "count(up{job=\"tailscale\"} == 1)",
"legendFormat": "Active Nodes"
}
],
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"steps": [
{"color": "red", "value": 0},
{"color": "yellow", "value": 1},
{"color": "green", "value": 2}
]
}
}
},
"gridPos": {"h": 8, "w": 6, "x": 0, "y": 0}
},
{
"id": 2,
"title": "Network Traffic",
"type": "graph",
"targets": [
{
"expr": "rate(tailscale_tx_bytes_total[5m])",
"legendFormat": "TX - {{instance}}"
},
{
"expr": "rate(tailscale_rx_bytes_total[5m])",
"legendFormat": "RX - {{instance}}"
}
],
"yAxes": [
{
"unit": "Bps",
"min": 0
}
],
"gridPos": {"h": 8, "w": 18, "x": 6, "y": 0}
},
{
"id": 3,
"title": "Node Latency",
"type": "graph",
"targets": [
{
"expr": "tailscale_node_latency_seconds * 1000",
"legendFormat": "{{node}}"
}
],
"yAxes": [
{
"unit": "ms",
"min": 0
}
],
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 8}
},
{
"id": 4,
"title": "Connection Status",
"type": "table",
"targets": [
{
"expr": "tailscale_node_info",
"format": "table",
"instant": true
}
],
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {},
"indexByName": {},
"renameByName": {
"node": "Node",
"online": "Status",
"last_seen": "Last Seen"
}
}
}
],
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 8}
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"refresh": "30s"
}
}Import the dashboard into Grafana using the API.
curl -X POST http://admin:your_secure_password_here@localhost:3000/api/dashboards/db \
-H "Content-Type: application/json" \
-d @/var/lib/grafana/dashboards/tailscale-overview.jsonConfigure firewall rules
Configure firewall rules to allow access to Grafana and secure the monitoring endpoints. This tutorial shows how to use Linux firewall configuration for production environments.
sudo ufw allow 3000/tcp comment 'Grafana'
sudo ufw allow from 203.0.113.0/24 to any port 9090 comment 'Prometheus internal'
sudo ufw enableSet up alerting notifications
Configure Alertmanager for sending notifications when Tailscale network issues occur. This complements the Prometheus alerting setup you might already have.
sudo apt install -y prometheus-alertmanagerglobal:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@example.com'
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'tailscale-alerts'
receivers:
- name: 'tailscale-alerts'
email_configs:
- to: 'admin@example.com'
subject: 'Tailscale Alert: {{ .GroupLabels.alertname }}'
body: |
{{ range .Alerts }}
Alert: {{ .Annotations.summary }}
Description: {{ .Annotations.description }}
Instance: {{ .Labels.instance }}
{{ end }}Enable and start Alertmanager for notification handling.
sudo systemctl enable --now prometheus-alertmanagerVerify your setup
Check that all monitoring components are working correctly and collecting Tailscale metrics.
curl http://localhost:9090/metrics | grep tailscale
curl http://localhost:9090/api/v1/query?query=up{job="tailscale"}
sudo systemctl status tailscale-exporter prometheus grafana-serverAccess Grafana web interface and verify the Tailscale dashboard displays network data.
curl -s http://localhost:3000/api/health
echo "Access Grafana at: http://your-server-ip:3000"
echo "Default login: admin / your_secure_password_here"Test Tailscale connectivity and verify metrics are being collected.
tailscale status
tailscale ping $(tailscale ip -4)
curl http://localhost:9100/metrics | grep node_networkCommon issues
| Symptom | Cause | Fix |
|---|---|---|
| Tailscale exporter not starting | Tailscale daemon not running | sudo systemctl start tailscaled |
| No metrics in Prometheus | Firewall blocking exporter port | Check firewall rules and allow port 9100 |
| Grafana dashboard empty | Prometheus data source misconfigured | Verify Prometheus URL in Grafana settings |
| High memory usage | Prometheus retention too long | Adjust --storage.tsdb.retention.time in service file |
| Alertmanager not sending emails | SMTP configuration incorrect | Test SMTP settings and verify email credentials |
| Permission denied errors | Wrong file ownership | sudo chown prometheus:prometheus /var/lib/prometheus |
Next steps
- Implement Tailscale OAuth integration with identity providers for enterprise authentication
- Configure Tailscale with Kubernetes cluster networking integration
- Set up Tailscale subnet routing and exit nodes for advanced networking
- Implement network monitoring with SNMP and BGP metrics using FRRouting and Prometheus
- Configure backup monitoring with Prometheus and Grafana for automated infrastructure oversight
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Error handling
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Cleaning up...${NC}"
systemctl stop tailscale-prometheus-exporter 2>/dev/null || true
systemctl disable tailscale-prometheus-exporter 2>/dev/null || true
rm -f /etc/systemd/system/tailscale-prometheus-exporter.service
rm -f /usr/local/bin/tailscale-prometheus-exporter
}
trap cleanup ERR
# Usage message
usage() {
echo "Usage: $0"
echo "Configure Tailscale monitoring with Prometheus and Grafana"
echo "Run as root or with sudo privileges"
exit 1
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root or with sudo${NC}"
exit 1
fi
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PROMETHEUS_PKG="prometheus prometheus-node-exporter"
PROMETHEUS_CONFIG="/etc/prometheus/prometheus.yml"
PROMETHEUS_RULES_DIR="/etc/prometheus/rules"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PROMETHEUS_PKG="prometheus2 node_exporter"
PROMETHEUS_CONFIG="/etc/prometheus/prometheus.yml"
PROMETHEUS_RULES_DIR="/etc/prometheus/rules"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PROMETHEUS_PKG="prometheus2 node_exporter"
PROMETHEUS_CONFIG="/etc/prometheus/prometheus.yml"
PROMETHEUS_RULES_DIR="/etc/prometheus/rules"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot determine distribution${NC}"
exit 1
fi
echo -e "${GREEN}Configuring Tailscale monitoring with Prometheus${NC}"
# Step 1: Update system packages
echo -e "${YELLOW}[1/8] Updating system packages...${NC}"
if [ "$PKG_MGR" = "apt" ]; then
apt update && apt upgrade -y
else
$PKG_INSTALL update
fi
# Step 2: Install Tailscale
echo -e "${YELLOW}[2/8] Installing Tailscale...${NC}"
if ! command -v tailscale &> /dev/null; then
curl -fsSL https://tailscale.com/install.sh | sh
echo -e "${GREEN}Tailscale installed. Run 'sudo tailscale up' to authenticate.${NC}"
else
echo -e "${GREEN}Tailscale already installed${NC}"
fi
# Step 3: Install Prometheus
echo -e "${YELLOW}[3/8] Installing Prometheus and Node Exporter...${NC}"
$PKG_INSTALL $PROMETHEUS_PKG
# Create prometheus directories
mkdir -p /var/lib/prometheus
mkdir -p "$PROMETHEUS_RULES_DIR"
chown prometheus:prometheus /var/lib/prometheus
# Step 4: Install Tailscale Prometheus exporter
echo -e "${YELLOW}[4/8] Installing Tailscale Prometheus exporter...${NC}"
cd /tmp
wget -q https://github.com/tailscale/tailscale/releases/latest/download/tailscale-prometheus-exporter_linux_amd64.tar.gz
tar -xzf tailscale-prometheus-exporter_linux_amd64.tar.gz
mv tailscale-prometheus-exporter /usr/local/bin/
chmod 755 /usr/local/bin/tailscale-prometheus-exporter
chown root:root /usr/local/bin/tailscale-prometheus-exporter
# Step 5: Create systemd service for Tailscale exporter
echo -e "${YELLOW}[5/8] Creating Tailscale exporter systemd service...${NC}"
cat > /etc/systemd/system/tailscale-prometheus-exporter.service << 'EOF'
[Unit]
Description=Tailscale Prometheus Exporter
After=network.target tailscaled.service
Requires=tailscaled.service
[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/tailscale-prometheus-exporter --listen-addr=127.0.0.1:9091
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable tailscale-prometheus-exporter
# Step 6: Configure Prometheus
echo -e "${YELLOW}[6/8] Configuring Prometheus...${NC}"
cp "$PROMETHEUS_CONFIG" "$PROMETHEUS_CONFIG.backup"
cat > "$PROMETHEUS_CONFIG" << 'EOF'
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "/etc/prometheus/rules/tailscale_alerts.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
- job_name: 'tailscale'
static_configs:
- targets: ['localhost:9091']
scrape_interval: 30s
metrics_path: '/metrics'
EOF
# Step 7: Create alerting rules
echo -e "${YELLOW}[7/8] Creating Tailscale alerting rules...${NC}"
cat > "$PROMETHEUS_RULES_DIR/tailscale_alerts.yml" << 'EOF'
groups:
- name: tailscale
rules:
- alert: TailscaleNodeDown
expr: up{job="tailscale"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Tailscale node is down"
description: "Tailscale node {{ $labels.instance }} has been down for more than 2 minutes."
- alert: TailscaleHighLatency
expr: tailscale_node_latency_seconds > 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High Tailscale latency detected"
description: "Tailscale node {{ $labels.node }} has latency {{ $value }}s for more than 5 minutes."
- alert: TailscaleConnectionLoss
expr: increase(tailscale_connection_failures_total[10m]) > 3
for: 1m
labels:
severity: warning
annotations:
summary: "Tailscale connection failures detected"
description: "Node {{ $labels.node }} has experienced {{ $value }} connection failures in the last 10 minutes."
EOF
chown prometheus:prometheus "$PROMETHEUS_CONFIG"
chown -R prometheus:prometheus "$PROMETHEUS_RULES_DIR"
# Step 8: Start and enable services
echo -e "${YELLOW}[8/8] Starting and enabling services...${NC}"
systemctl enable prometheus node_exporter
systemctl restart prometheus node_exporter
# Wait for tailscale to be available before starting exporter
if systemctl is-active tailscaled &> /dev/null; then
systemctl start tailscale-prometheus-exporter
else
echo -e "${YELLOW}Tailscaled not running. Exporter will start when Tailscale is configured.${NC}"
fi
# Configure firewall if needed
if command -v ufw &> /dev/null && ufw status | grep -q "Status: active"; then
ufw allow 9090/tcp comment "Prometheus"
ufw allow 9100/tcp comment "Node Exporter"
elif command -v firewall-cmd &> /dev/null && systemctl is-active firewalld &> /dev/null; then
firewall-cmd --permanent --add-port=9090/tcp
firewall-cmd --permanent --add-port=9100/tcp
firewall-cmd --reload
fi
# Verification
echo -e "${YELLOW}Verifying installation...${NC}"
sleep 3
if systemctl is-active prometheus &> /dev/null; then
echo -e "${GREEN}✓ Prometheus is running${NC}"
else
echo -e "${RED}✗ Prometheus failed to start${NC}"
fi
if systemctl is-active node_exporter &> /dev/null; then
echo -e "${GREEN}✓ Node Exporter is running${NC}"
else
echo -e "${RED}✗ Node Exporter failed to start${NC}"
fi
if [ -f /usr/local/bin/tailscale-prometheus-exporter ]; then
echo -e "${GREEN}✓ Tailscale Prometheus exporter installed${NC}"
else
echo -e "${RED}✗ Tailscale Prometheus exporter installation failed${NC}"
fi
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Configure Tailscale: sudo tailscale up"
echo "2. Start Tailscale exporter: sudo systemctl start tailscale-prometheus-exporter"
echo "3. Access Prometheus at: http://localhost:9090"
echo "4. Install Grafana to visualize metrics"
Review the script before running. Execute with: bash install.sh