Tailscale makes zero-config mesh VPN almost invisible — nodes come up, get IPs, and start talking without firewall rules or key exchange ceremonies. That invisibility is great for developer experience, but it creates a blind spot: when something breaks, there's nothing obvious to look at.
This tutorial shows you how to monitor Tailscale-connected services, control plane reachability, and background agents with Vigilmon so incidents surface before they impact your team.
The Tailscale monitoring problem
Tailscale shifts the networking layer from infrastructure you manage to a SaaS control plane you depend on. That introduces failure modes that aren't covered by traditional infrastructure monitoring:
- Tailscale daemon crashes — if
tailscaleddies on a node, that node silently vanishes from the mesh. Traffic that was routing through it starts failing with no DNS or routing error — just timeouts. - DERP relay fallback — when two nodes can't establish a direct connection, Tailscale routes through a DERP relay server. DERP traffic is slower and adds latency; without monitoring you won't know when your supposedly-direct connections started relaying.
- Key expiry — by default, Tailscale device keys expire after 180 days. An expired key drops the device from the network. Without an alert, you discover this when SSH stops working at 3 AM.
- Service availability — Tailscale gives your services stable IPs (100.x.x.x) and optionally exposes them as Magic DNS names. The VPN connectivity might be fine while the service process itself is down.
Vigilmon monitors the things that matter: whether your services are actually responding, and whether the infrastructure jobs around your VPN are running.
What you'll need
- Tailscale installed on your nodes (tailscale.com)
- Services accessible via Tailscale IP or Magic DNS
- A free Vigilmon account
Step 1: Add a health endpoint to services behind Tailscale
Services inside your tailnet need a /health route for Vigilmon to poll. Here are quick snippets for common runtimes:
Node.js / Express:
app.get('/health', (req, res) => {
res.json({ status: 'ok', node: process.env.HOSTNAME });
});
Python / FastAPI:
@app.get("/health")
async def health():
return {"status": "ok"}
Go:
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"status":"ok"}`)
})
Bash (for shell-based services):
# Simple HTTP health server using netcat
while true; do
echo -e "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{\"status\":\"ok\"}" | \
nc -l -p 8080 -q 1
done
Step 2: Set up a Vigilmon monitoring node inside your tailnet
Vigilmon's external probes check public endpoints. For services that only exist inside your tailnet, you need a probe that can reach them.
Run a lightweight monitoring agent on any node in your tailnet. The agent polls your internal services and calls the Vigilmon push API when they're unreachable:
#!/bin/bash
# tailscale-health-probe.sh — runs as a cron job on any tailnet node
SERVICES=(
"http://100.64.0.10:8080/health"
"http://internal-api.example.ts.net/health"
)
VIGILMON_INCIDENT_URL="https://vigilmon.online/api/push/YOUR_PUSH_KEY"
for url in "${SERVICES[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url")
if [ "$status" != "200" ]; then
curl -s -X POST "$VIGILMON_INCIDENT_URL" \
-H "Content-Type: application/json" \
-d "{\"event\":\"down\",\"url\":\"$url\",\"status\":$status}"
fi
done
Add this to crontab:
* * * * * /opt/scripts/tailscale-health-probe.sh >> /var/log/tailscale-probe.log 2>&1
Step 3: Monitor Tailscale-exposed public services
If you use Tailscale Funnel to expose services publicly, monitor them like any public HTTPS endpoint:
- Log in to vigilmon.online and click Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your Funnel endpoint, e.g.
https://my-service.example.ts.net - Set check interval to 1 minute
- Expected status code:
200 - Save the monitor
Tailscale Funnel goes through Tailscale's edge — if your node's tailscaled crashes, Funnel stops working too. This monitor catches both your application being down and the Tailscale daemon failing.
Step 4: Send heartbeats from Tailscale connectivity checks
A connectivity heartbeat confirms that two specific nodes can reach each other — more targeted than just "is the daemon running."
Add this script to run on a schedule from a node that acts as a connectivity sentinel:
#!/bin/bash
# tailscale-connectivity-check.sh
TARGET_NODE="100.64.0.20" # Replace with your critical node's Tailscale IP
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"
# Test ICMP reachability via tailscale ping
if tailscale ping --c 3 "$TARGET_NODE" > /dev/null 2>&1; then
curl -s "$HEARTBEAT_URL" --max-time 10
else
echo "$(date): Node $TARGET_NODE unreachable" >> /var/log/tailscale-connectivity.log
fi
Set up the heartbeat in Vigilmon:
- Go to Monitors → New Monitor → Heartbeat
- Name it
Tailscale Node: production-db - Set the expected interval to 5 minutes (matching your cron schedule)
- Copy the heartbeat URL into the script
If the connectivity check stops reporting in — because the sentinel can't reach the target node, or because the sentinel itself lost its tailnet connection — Vigilmon fires an alert.
Step 5: Monitor Tailscale key expiry with a scheduled check
Tailscale key expiry silently drops nodes from your network. Add a pre-expiry alert:
#!/bin/bash
# tailscale-key-expiry-check.sh
# Run daily from each node
HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_KEY_EXPIRY_HEARTBEAT"
EXPIRY_THRESHOLD_DAYS=30 # Alert 30 days before expiry
# Get key expiry from tailscale status
expiry=$(tailscale status --json | jq -r '.Self.KeyExpiry')
if [ "$expiry" = "null" ] || [ -z "$expiry" ]; then
# Keys don't expire (ephemeral node or disabled expiry)
curl -s "$HEARTBEAT_URL"
exit 0
fi
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_remaining=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_remaining" -gt "$EXPIRY_THRESHOLD_DAYS" ]; then
# Key is valid for more than threshold days — signal heartbeat
curl -s "$HEARTBEAT_URL"
else
# Key expires soon — don't signal; heartbeat will fire the alert
echo "$(date): Key expires in $days_remaining days" >> /var/log/tailscale-expiry.log
fi
Set the heartbeat interval to 25 hours so Vigilmon alerts within a day if the check stops running or if the key is within 30 days of expiry.
Step 6: Configure alert channels
Route Tailscale alerts to the right people:
- Go to Alert Channels → Add Channel
- Add a Slack channel for
#networking-alerts(use a Slack webhook URL) - Add email for on-call engineers
- Assign channels to all Tailscale monitors
For key expiry alerts specifically, email is usually sufficient — it's a calendar-style reminder, not an emergency. For connectivity down or Funnel endpoint down, use Slack or PagerDuty.
Step 7: Create a network status page
If internal teams depend on tailnet-accessible services, a status page gives them self-service visibility:
- Go to Status Pages → New Status Page
- Name it
Internal Network - Add your monitors
- Share the URL in
#infraor your team wiki
Full monitoring setup at a glance
| Monitor | Type | What it checks | |---------|------|----------------| | Funnel public endpoint | HTTP | Service down, daemon crash | | Internal service probe | Heartbeat | Service unreachable from inside tailnet | | Node-to-node connectivity | Heartbeat | Node dropped from mesh | | Key expiry | Heartbeat | Device key expiring within 30 days | | Daily connectivity sentinel | Heartbeat | Sentinel node itself healthy |
What to do when alerts fire
- Funnel endpoint down — SSH to the node and run
tailscale status; restarttailscaledwithsudo systemctl restart tailscaled - Node-to-node heartbeat missed — run
tailscale ping <target>from the sentinel; check ACLs in the Tailscale admin console - Key expiry — log in to login.tailscale.com, find the device, click Disable key expiry or re-authenticate the node
Next steps
- SSL expiry monitoring — if you expose services via Funnel over HTTPS, add an SSL monitor in Vigilmon to catch certificate issues
- Multi-region probes — Vigilmon probes from multiple global regions; for Funnel endpoints this confirms Tailscale's edge is reachable worldwide, not just from your office
Get started for free at vigilmon.online.