tutorial

How to Monitor ZeroTier Networks and Services with Vigilmon

ZeroTier makes software-defined networking feel like magic — devices join a virtual LAN with a 16-digit network ID and start talking as if they're on the sam...

ZeroTier makes software-defined networking feel like magic — devices join a virtual LAN with a 16-digit network ID and start talking as if they're on the same switch, regardless of where they physically are. But that simplicity can mask a lot of moving parts: a ZeroTier controller, background daemons on every node, and optional self-hosted infrastructure that can all fail independently.

This tutorial walks you through monitoring ZeroTier infrastructure and the services that run on top of it using Vigilmon.


What can go wrong in a ZeroTier deployment

ZeroTier has a layered architecture, and each layer has its own failure modes:

  • ZeroTier One daemon — the background service on each node. If it crashes, the node loses its virtual IP and becomes unreachable over the ZeroTier network, with no error message on the surface.
  • ZeroTier Central (or self-hosted controller) — authorizes new nodes and distributes network configuration. If the controller is unreachable, nodes can't join and new authorizations fail. Existing nodes continue working with cached config, but you won't know the control plane is down until someone tries to add a node.
  • Moon (self-hosted root) — if you run your own ZeroTier planet/moon for geographic optimization, a downed moon causes connectivity degradation for all nodes peered through it.
  • Application services — ZeroTier connectivity working doesn't mean your services are working. A database on 10.147.20.x might be unreachable because the app process crashed, not because ZeroTier failed.

External monitoring covers all of these layers — not just whether the ZeroTier daemon is running, but whether your services are actually responding.


What you'll need


Step 1: Add health endpoints to services behind ZeroTier

Give Vigilmon something to check on each critical service in your ZeroTier network.

Python / Flask:

from flask import Flask, jsonify
app = Flask(__name__)

@app.get("/health")
def health():
    return jsonify({"status": "ok", "service": "api"})

Node.js / Express:

app.get('/health', (_req, res) => {
  res.json({ status: 'ok' });
});

Rust / Axum:

async fn health() -> impl IntoResponse {
    Json(serde_json::json!({"status": "ok"}))
}

Bind the health endpoint to the ZeroTier interface if you want it only reachable over the virtual LAN:

# ZeroTier assigns a managed IP like 10.147.20.x
# Bind your service to that IP so it's only reachable over ZeroTier
./myapp --listen-addr 10.147.20.5:8080

Step 2: Deploy a monitoring probe inside the ZeroTier network

Vigilmon's global probes reach public endpoints. For ZeroTier-only services, deploy an internal probe on a node that belongs to the network:

#!/bin/bash
# zerotier-probe.sh — cron job on any ZeroTier node

HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"

SERVICES=(
  "http://10.147.20.5:8080/health"
  "http://10.147.20.10:5432"   # TCP-like check for PostgreSQL
)

all_ok=true

for url in "${SERVICES[@]}"; do
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url" 2>/dev/null)
  if [ "$code" != "200" ]; then
    echo "$(date): $url returned $code"
    all_ok=false
  fi
done

if $all_ok; then
  # All services OK — ping the heartbeat so Vigilmon knows we're healthy
  curl -s "$HEARTBEAT_URL" --max-time 10
fi

Add to crontab:

* * * * * /opt/scripts/zerotier-probe.sh >> /var/log/zerotier-probe.log 2>&1

In Vigilmon, create a Heartbeat monitor with a 5-minute interval. If the probe stops pinging — because a service is down or the probe node lost its ZeroTier connection — Vigilmon fires an alert.


Step 3: Monitor a self-hosted ZeroTier controller

If you run your own controller (using the ZeroTier Network Controller API), monitor it as an HTTP endpoint.

The controller exposes a local API on port 9993. If you've fronted it with nginx for external access:

server {
    listen 443 ssl;
    server_name zt-controller.example.com;

    location /controller/status {
        proxy_pass http://127.0.0.1:9993/controller;
        proxy_set_header X-ZT1-Auth $zt_auth_header;
    }

    location /controller/health {
        # Lightweight unauthenticated check — returns 200 if controller is alive
        return 200 '{"status":"ok"}';
        add_header Content-Type application/json;
    }
}

In Vigilmon:

  1. Go to Monitors → New Monitor → HTTP / HTTPS
  2. URL: https://zt-controller.example.com/controller/health
  3. Expected status: 200
  4. Expected body contains: "status":"ok"
  5. Name it ZeroTier Controller

Step 4: Monitor ZeroTier daemon health with a heartbeat

The ZeroTier daemon itself doesn't expose an HTTP health endpoint, but you can wrap it in a shell-based heartbeat:

#!/bin/bash
# zerotier-daemon-check.sh

HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_DAEMON_HEARTBEAT"

# Check daemon status
if zerotier-cli info > /dev/null 2>&1; then
  # Daemon is responding — also check we're actually in the network
  status_json=$(zerotier-cli -j listnetworks 2>/dev/null)
  joined_count=$(echo "$status_json" | jq 'length' 2>/dev/null || echo "0")
  
  if [ "$joined_count" -gt "0" ]; then
    curl -s "$HEARTBEAT_URL" --max-time 10
  else
    echo "$(date): ZeroTier daemon running but no networks joined" >> /var/log/zerotier-health.log
  fi
else
  echo "$(date): ZeroTier daemon not responding" >> /var/log/zerotier-health.log
fi

Set up the heartbeat:

  1. Go to Monitors → New Monitor → Heartbeat
  2. Name it ZeroTier Daemon: <hostname>
  3. Interval: 10 minutes
  4. Add to crontab on each critical node:
    */10 * * * * /opt/scripts/zerotier-daemon-check.sh
    

Deploy this on every node that must stay in the ZeroTier network — particularly gateways and services that other nodes depend on.


Step 5: Monitor ZeroTier's public infrastructure (optional)

If you use ZeroTier Central (the managed controller at my.zerotier.com), you depend on Zerotier Inc.'s uptime for new node authorizations. You can monitor the ZeroTier API status endpoint:

  1. Add an HTTP monitor targeting https://my.zerotier.com/api/v1/status
  2. Expected status: 200
  3. Name it ZeroTier Central API

This won't give you alerts for every ZeroTier issue, but it will tell you if the control plane is completely unreachable — so you know whether to escalate to ZeroTier support or keep investigating locally.


Step 6: Configure alert channels

Set up notification routing for ZeroTier alerts:

  1. Go to Alert Channels → Add Channel
  2. Add a Slack webhook for #networking-ops
  3. Add Email for your on-call list
  4. Assign channels to all ZeroTier monitors

For the controller and daemon heartbeats, configure a 5-minute escalation — if a heartbeat is missed by more than 5 minutes, send to PagerDuty or wake someone up.


Step 7: Create a status page

If your team relies on ZeroTier-connected services, a status page reduces "is the VPN down?" interruptions:

  1. Go to Status Pages → New Status Page
  2. Name it Internal Network Status
  3. Add monitors: Controller, daemon heartbeats, service probes
  4. Publish and share the URL in your team wiki

Monitoring coverage summary

| Monitor | Type | Trigger | |---------|------|---------| | ZeroTier Controller | HTTP | Controller API unreachable | | Internal service probe | Heartbeat | Service down or ZeroTier connectivity lost | | Daemon per-node | Heartbeat | ZeroTier daemon crashed or network left | | ZeroTier Central API | HTTP | Managed controller unreachable |


Troubleshooting when alerts fire

  • Controller HTTP down — restart the ZeroTier controller: systemctl restart zerotier-one; check logs at /var/log/syslog | grep zerotier
  • Daemon heartbeat missed — SSH to the node, run zerotier-cli info; if it hangs, restart with systemctl restart zerotier-one
  • Node not in network — run zerotier-cli listnetworks; if the network shows ACCESS_DENIED, re-authorize the node in the controller admin UI
  • Internal probe heartbeat missed — SSH to the probe node first; if it's reachable, check which specific service URL failed in the probe log

Next steps

  • Multi-network monitoring — if you operate multiple ZeroTier networks (dev, staging, prod), create separate heartbeat monitors for each network's sentinel node
  • SSL expiry — if your self-hosted controller is fronted by nginx with TLS, add an SSL monitor in Vigilmon

Get started for free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →