Introduction
ZeroTier creates a virtual Ethernet network across the internet. Every device on your ZeroTier network can reach every other device as if they were on the same LAN โ regardless of NAT, firewalls, or geographic location. Unlike WireGuard (point-to-point) or OpenVPN (hub-and-spoke), ZeroTier is a true mesh: devices connect directly to each other.
ZeroTier vs WireGuard:
WireGuard: You configure each peer explicitly. Fast, simple, manual.
ZeroTier: Devices auto-discover each other. Easier to manage at scale.
WireGuard: ~950 Mbps throughput
ZeroTier: ~300-600 Mbps throughput (more overhead for mesh management)
WireGuard: Best for: performance-critical, small number of peers
ZeroTier: Best for: many devices, IoT, dynamic membership
Quick Start
Step 1: Create a Network
- Sign up at my.zerotier.com
- Click “Create A Network”
- Note your 16-character Network ID (e.g.,
8056c2e21c000001)
Step 2: Install ZeroTier
# Linux (Ubuntu/Debian)
curl -s https://install.zerotier.com | sudo bash
# macOS
brew install zerotier
# Windows: download from zerotier.com/download
# Verify
sudo zerotier-cli status
# 200 info <your-node-id> 1.14.0 ONLINE
Step 3: Join Your Network
# Join the network
sudo zerotier-cli join 8056c2e21c000001
# Check status
sudo zerotier-cli listnetworks
# 200 listnetworks 8056c2e21c000001 <name> <mac> OK PRIVATE zt0 10.147.17.x/16
Step 4: Authorize the Device
In ZeroTier Central, go to your network โ Members โ check the box next to your device to authorize it.
# Verify you got an IP
ip addr show zt0
# inet 10.147.17.123/16 brd 10.147.255.255 scope global zt0
# Test connectivity to another device
ping 10.147.17.100
Network Configuration
Managed Routes
Add routes so devices can reach subnets behind each other:
In ZeroTier Central โ Network โ Managed Routes:
Route: 192.168.1.0/24 Via: 10.147.17.10
(This tells all ZeroTier devices: to reach 192.168.1.0/24, go through 10.147.17.10)
On the gateway device (10.147.17.10), enable IP forwarding:
# Enable forwarding
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
# Add NAT if needed (for internet access through gateway)
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i zt0 -j ACCEPT
sudo iptables -A FORWARD -o zt0 -j ACCEPT
Access Control Rules
ZeroTier uses flow rules to control traffic between members:
# In ZeroTier Central โ Network โ Flow Rules
# Default: allow all traffic
accept;
# More restrictive: only allow specific ports
# Block all by default
drop;
# Allow SSH from management subnet only
match ethertype ipv4 and ip4.src 10.147.17.0/24 and dport 22;
accept;
# Allow HTTP/HTTPS
match ethertype ipv4 and dport 80;
accept;
match ethertype ipv4 and dport 443;
accept;
# Allow ICMP (ping)
match ethertype ipv4 and ipprotocol icmp;
accept;
ZeroTier CLI Reference
# Status
sudo zerotier-cli status
sudo zerotier-cli info
# Networks
sudo zerotier-cli listnetworks
sudo zerotier-cli join <network-id>
sudo zerotier-cli leave <network-id>
# Peers
sudo zerotier-cli listpeers
# Shows: node-id, latency, version, role (LEAF/PLANET/MOON)
# Network details
sudo zerotier-cli get <network-id> name
sudo zerotier-cli get <network-id> status
# Disconnect/reconnect
sudo systemctl restart zerotier-one
Self-Hosted Controller
For privacy or compliance, run your own ZeroTier controller:
# Install ZeroTier (includes controller)
curl -s https://install.zerotier.com | sudo bash
# The controller API runs on port 9993
# Get your auth token
sudo cat /var/lib/zerotier-one/authtoken.secret
# Create a network via API
curl -X POST "http://localhost:9993/controller/network/$(cat /var/lib/zerotier-one/identity.public | cut -d: -f1)______" \
-H "X-ZT1-Auth: $(sudo cat /var/lib/zerotier-one/authtoken.secret)" \
-d '{"name": "my-private-network"}'
# Response includes network ID
# {"id":"8056c2e21c000001","name":"my-private-network",...}
ZeroTierOne Controller API
import httpx
class ZeroTierController:
def __init__(self, host: str = "localhost", port: int = 9993):
self.base_url = f"http://{host}:{port}"
with open("/var/lib/zerotier-one/authtoken.secret") as f:
self.token = f.read().strip()
self.headers = {"X-ZT1-Auth": self.token}
def get_node_id(self) -> str:
resp = httpx.get(f"{self.base_url}/status", headers=self.headers)
return resp.json()["address"]
def create_network(self, name: str, subnet: str = "10.147.0.0/16") -> dict:
node_id = self.get_node_id()
network_id = f"{node_id}______"
resp = httpx.post(
f"{self.base_url}/controller/network/{network_id}",
headers=self.headers,
json={
"name": name,
"private": True,
"v4AssignMode": {"zt": True},
"ipAssignmentPools": [{"ipRangeStart": "10.147.0.1", "ipRangeEnd": "10.147.255.254"}],
"routes": [{"target": subnet, "via": None}],
}
)
return resp.json()
def list_members(self, network_id: str) -> list:
resp = httpx.get(
f"{self.base_url}/controller/network/{network_id}/member",
headers=self.headers
)
return resp.json()
def authorize_member(self, network_id: str, member_id: str) -> dict:
resp = httpx.post(
f"{self.base_url}/controller/network/{network_id}/member/{member_id}",
headers=self.headers,
json={"authorized": True}
)
return resp.json()
def deauthorize_member(self, network_id: str, member_id: str) -> dict:
resp = httpx.post(
f"{self.base_url}/controller/network/{network_id}/member/{member_id}",
headers=self.headers,
json={"authorized": False}
)
return resp.json()
# Usage
controller = ZeroTierController()
network = controller.create_network("production-mesh")
print(f"Network ID: {network['id']}")
Moon (Private Root Server)
For better performance and privacy, run a Moon (private relay/root server):
# On a server with a public IP
sudo zerotier-cli orbit <moon-id> <moon-id>
# Generate moon config
cd /var/lib/zerotier-one
sudo zerotier-idtool initmoon identity.public > moon.json
# Edit moon.json to add your server's public IP
# "stableEndpoints": ["203.0.113.1/9993"]
# Generate the moon file
sudo zerotier-idtool genmoon moon.json
# Copy the .moon file to all clients
# On each client:
sudo mkdir -p /var/lib/zerotier-one/moons.d
sudo cp <moon-id>.moon /var/lib/zerotier-one/moons.d/
sudo systemctl restart zerotier-one
# Verify moon is connected
sudo zerotier-cli listpeers | grep MOON
Common Use Cases
IoT Device Management
# On each IoT device (Raspberry Pi, etc.)
curl -s https://install.zerotier.com | sudo bash
sudo zerotier-cli join YOUR_NETWORK_ID
# Auto-authorize new devices via API (for automated provisioning)
# Run this on your management server when a new device joins
# Auto-authorize new ZeroTier members
import time
def auto_authorize_new_members(network_id: str, controller: ZeroTierController):
"""Automatically authorize new members as they join."""
known_members = set()
while True:
members = controller.list_members(network_id)
for member_id, member_data in members.items():
if member_id not in known_members:
known_members.add(member_id)
if not member_data.get("authorized"):
controller.authorize_member(network_id, member_id)
print(f"Auto-authorized new member: {member_id}")
time.sleep(30)
Multi-Site Office Network
Office A (192.168.1.0/24) โโZeroTierโโโ All offices
Office B (192.168.2.0/24) โโZeroTierโโโ All offices
Cloud VPC (10.0.0.0/16) โโZeroTierโโโ All offices
ZeroTier Network: 10.147.0.0/16
Office A gateway: 10.147.0.1
Office B gateway: 10.147.0.2
Cloud gateway: 10.147.0.3
Managed Routes in ZeroTier Central:
192.168.1.0/24 via 10.147.0.1
192.168.2.0/24 via 10.147.0.2
10.0.0.0/16 via 10.147.0.3
Monitoring
# Check peer connectivity and latency
sudo zerotier-cli listpeers
# 200 listpeers
# <id> <ip:port> <latency> <version> <role>
# abc123def456 203.0.113.1/9993 12 1.14.0 LEAF
# Check if direct connection or relayed
# latency < 50ms = likely direct
# latency > 100ms = likely relayed through PLANET/MOON
# Network interface stats
ip -s link show zt0
# Watch traffic
sudo tcpdump -i zt0 -n
Troubleshooting
# Device not getting IP
sudo zerotier-cli listnetworks
# Check status: should be "OK" not "ACCESS_DENIED"
# โ Go to ZeroTier Central and authorize the device
# Can't reach other devices
sudo zerotier-cli listpeers
# Check if peers show up with low latency (direct) or high latency (relayed)
# High latency (relayed traffic)
# โ Deploy a Moon server closer to your devices
# โ Check if UDP port 9993 is blocked by firewall
# Allow ZeroTier through firewall
sudo ufw allow 9993/udp
# Check ZeroTier service
sudo systemctl status zerotier-one
sudo journalctl -u zerotier-one -f
Comments