How to Install ZeroTier on Ubuntu 24.04 VPS: Software-Defined VPN Mesh Network
ZeroTier turns a fleet of servers, laptops, phones and IoT devices into a single flat Layer 2 network, no matter where they live. It punches through NAT, encrypts every packet, and behaves like a virtual Ethernet switch that happens to span the internet. This guide walks you through installing ZeroTier on an Ubuntu 24.04 VPS, joining a network, authorizing nodes, configuring managed routes and flow rules, bridging to a physical LAN, and optionally running your own moon (self-hosted root server) so you never depend on ZeroTier's infrastructure.
Looking for a fast, cheap host for your mesh controller or moon? The CloudCore Starter plan gives you enough vCPU, RAM and unmetered bandwidth to run a ZeroTier moon serving dozens of peers at a flat monthly price.
Table of Contents
What is ZeroTier?
ZeroTier is an open-source software-defined networking (SDN) platform that creates peer-to-peer virtual Ethernet networks across the internet. Unlike traditional hub-and-spoke VPNs, where every packet traverses a central server, ZeroTier nodes discover each other via planetary root servers and then communicate directly, encrypted end to end with Curve25519 and Salsa20/Poly1305.
Each virtual network has a 16-character network ID and behaves like a giant Layer 2 switch. Devices see each other as if they were plugged into the same Ethernet hub, regardless of whether they sit behind carrier-grade NAT, a home router or a corporate firewall. You can build:
- Flat site-to-site networks that merge multiple data centers and offices.
- Remote access VPNs for developers connecting to private infrastructure.
- IoT fleets where thousands of ARM devices share a single IP range.
- Gaming and LAN-party mesh networks with low-latency direct peer connections.
- Kubernetes overlay networks and cross-cloud service meshes.
Why Self-Host a Mesh VPN Instead of Using a Commercial VPN?
Commercial VPN services like NordVPN, ExpressVPN or Mullvad solve one specific problem: privacy from your local ISP and geo-unblocking. They do not solve the problems that ZeroTier solves.
- Private addressing across sites. Commercial VPNs drop you on a shared public IP with hundreds of other users. ZeroTier gives every device its own stable private IP that other authorized members can reach directly.
- Service-to-service connectivity. You cannot SSH from your laptop in a cafe to a Postgres instance on a Contabo VPS via NordVPN. With ZeroTier, both machines share a managed private subnet.
- No bandwidth throttling. Commercial VPNs throttle torrenting, streaming and sustained transfers. A self-hosted mesh moves data at your VPS's full uplink.
- Flat, predictable cost. A ZeroTier controller on a CloudCore Starter VPS costs the same whether you connect 2 or 200 devices. Commercial VPNs charge per seat.
- End-to-end encryption you control. ZeroTier traffic is encrypted between the endpoints themselves. Even ZeroTier Inc. cannot decrypt your packets. With a self-hosted moon, they do not even see metadata.
- Programmable flow rules. You can write firewall rules that apply across every member of the network, including devices on hostile Wi-Fi.
- Compliance. For GDPR, HIPAA or SOC 2 workloads, hosting your own overlay on known EU or US VPS providers gives you a defensible audit trail no commercial VPN can match.
ZeroTier vs Tailscale vs Headscale vs WireGuard
Mesh and overlay VPNs are a crowded space in 2026. Here is how ZeroTier compares to the other three most popular options.
| Feature | ZeroTier | Tailscale | Headscale | Plain WireGuard |
|---|---|---|---|---|
| Transport | UDP + TCP fallback | WireGuard (UDP) | WireGuard (UDP) | UDP |
| Layer | Layer 2 (Ethernet) | Layer 3 (IP) | Layer 3 (IP) | Layer 3 (IP) |
| Control plane | my.zerotier.com (free 25) or self-hosted moon | Hosted SaaS (free 100) | Self-hosted, open source | Manual config |
| Peer-to-peer holepunch | Yes | Yes (DERP relay fallback) | Yes | No (manual) |
| IPv6 | Built-in RFC 4193 + 6PLANE | Supported | Supported | Manual |
| Flow rules / ACLs | Yes, in web UI and Rules Engine language | Yes, HuJSON ACLs | Yes, HuJSON ACLs | iptables per peer |
| Bridging to physical LAN | Yes (Layer 2 native) | Subnet router (Layer 3) | Subnet router (Layer 3) | Requires NAT / routing |
| License | BSL 1.1 (FOSS after 2 years) | BSD (client) + proprietary (control plane) | BSD | GPLv2 |
| Best for | Mixed fleets, IoT, gaming, L2 bridging | Small teams wanting polish | Self-hosted Tailscale | Point-to-point tunnels |
Pick Tailscale when you want the smoothest SSO and MagicDNS experience and you accept a proprietary control plane. See how to install Tailscale on Ubuntu.
Pick Headscale when you love the Tailscale client UX but want a fully self-hosted coordinator with no SaaS dependency. See how to install Headscale on Ubuntu.
Pick plain WireGuard when you have 2 or 3 fixed peers and want the absolute minimum attack surface. See how to install WireGuard on Ubuntu.
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access.
- SSH access (PuTTY on Windows, native terminal on macOS/Linux).
- A free account at my.zerotier.com.
- At least 1 vCPU, 1 GB RAM and 20 GB disk for a controller, moon or gateway node.
- UDP port 9993 outbound allowed (ZeroTier uses this for peer discovery and data).
Recommended Plan: CloudCore Starter>
ZeroTier is extremely lightweight. The ZeroTier One daemon typically uses 20-60 MB of RAM and negligible CPU. For a mesh gateway, moon, or bridging node, the CloudCore Starter plan is more than enough:>
- 2 vCPU cores
- 4 GB RAM
- 80 GB NVMe SSD
- Unmetered bandwidth
- Public IPv4 and IPv6>
The generous bandwidth allowance is the key metric: if the VPS acts as a moon or a bridge, every relayed packet counts toward your monthly quota.
Connect to your server via SSH:
ssh root@your-server-ipStep 1: Update System Packages
Refresh the package index and upgrade existing packages. This guarantees that curl, gpg and kernel modules are current.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl gnupg ca-certificatesExpected output (abbreviated):
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Reading package lists... Done
Building dependency tree... Done
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.If the kernel was updated, reboot before continuing:
sudo rebootStep 2: Install ZeroTier via the Official curl Installer
ZeroTier provides a signed installer script that adds the official APT repository, imports the GPG key and installs the zerotier-one package.
curl -s https://install.zerotier.com | sudo bashExpected output:
* ZeroTier One Quick Install for Unix-like Systems
* Detected Linux distribution: ubuntu (noble) * Adding zerotier-release.asc to /etc/apt/trusted.gpg.d * Installing zerotier-one package... ... Setting up zerotier-one (1.14.0) ... Created symlink /etc/systemd/system/multi-user.target.wants/zerotier-one.service * Success! ZeroTier One is installed and running. * Your ZeroTier address is: a1b2c3d4e5
The installer performs these actions:
[email protected] as a trusted APT signing key.deb http://download.zerotier.com/debian/noble noble main to /etc/apt/sources.list.d/zerotier.list.zerotier-one via apt.zerotier-one.service systemd unit./var/lib/zerotier-one/identity.public).Verify the install and note your node's address:
sudo zerotier-cli infoExpected output:
200 info a1b2c3d4e5 1.14.0 ONLINEONLINE means the node has reached the planetary root servers. Write down the 10-character address (a1b2c3d4e5 in this example) — you will need it in Step 5.
Step 3: Create a Network at my.zerotier.com
Open https://my.zerotier.com in a browser, sign in (or sign up — the free tier covers 25 authorized members), then click Create A Network.
The new network appears with a 16-character Network ID such as abcdef0123456789. Click the network card to open its settings. The essentials to configure now:
- Name — for example
office-mesh. - Access Control — leave as Private (default). Private means every new joiner must be manually authorized; Public means anyone with the Network ID joins automatically.
- IPv4 Auto-Assign — pick one of the preset ranges (10.147.17.0/24 is a common default) or define a custom range under Advanced.
- IPv6 Auto-Assign — tick ZeroTier RFC4193 and/or ZeroTier 6PLANE (we will discuss these in Step 9).
- Multicast Recipient Limit — leave at 32 unless you run very chatty mDNS/Bonjour services.
Step 4: Join the Network from Your VPS
Back on the VPS, join the network you just created. Replace abcdef0123456789 with your 16-character Network ID.
sudo zerotier-cli join abcdef0123456789Expected output:
200 join OKCheck the join status:
sudo zerotier-cli listnetworksExpected output:
200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
200 listnetworks abcdef0123456789 ? 8a:2f:... ACCESS_DENIED PRIVATE ztabc12def -ACCESS_DENIED is expected at this stage — the network is private, and we have not authorized this node yet. Do not panic.
Step 5: Authorize the Node
Return to my.zerotier.com, open your network, and scroll to the Members section. The VPS node appears with the 10-character address from Step 2 and a red/grey Auth checkbox.
vps-contabo-nl) and a Description if you want.10.147.17.10 instead of the auto-assigned one.Back on the VPS, re-check the network status:
sudo zerotier-cli listnetworksExpected output:
200 listnetworks abcdef0123456789 office-mesh 8a:2f:... OK PRIVATE ztabc12def 10.147.17.10/24OK means you are authorized and an IP is assigned. Confirm the virtual interface:
ip addr show ztabc12defExpected output:
4: ztabc12def: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2800 qdisc fq_codel state UNKNOWN
link/ether 8a:2f:c1:02:3d:7e brd ff:ff:ff:ff:ff:ff
inet 10.147.17.10/24 brd 10.147.17.255 scope global ztabc12def
inet6 fd80:56c2:e21c:0:0199:93a1:b2c3:d4e5/88 scope globalFrom any other authorized device (your laptop, another VPS), ping the new node:
ping 10.147.17.10You now have a working mesh. The next steps transform it from a flat network into a production-grade overlay.
Step 6: Configure Managed Routes
Managed routes let ZeroTier push routing table entries to every member automatically. Use cases:
- Advertise a physical subnet (e.g.
192.168.50.0/24) that lives behind one of the ZeroTier nodes so every other member can reach it. - Create a default-route gateway (
0.0.0.0/0) so remote users tunnel all traffic through a chosen node. - Segment the ZeroTier network itself into multiple subnets.
Example A: Advertise a physical LAN behind your VPS
Suppose your VPS also has a private interface on 192.168.50.0/24 (perhaps a Contabo private network or a WireGuard tunnel to an office). You want every ZeroTier member to reach those hosts via the VPS.
On my.zerotier.com -> your network -> Advanced -> Managed Routes, add:
- Destination:
192.168.50.0/24 - (Via):
10.147.17.10(the VPS's ZeroTier IP)
sudo tee /etc/sysctl.d/99-zerotier-forward.conf > /dev/null <<EOF
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl --systemAdd an iptables rule so return traffic from the LAN is source-NAT'd onto the ZeroTier interface (only if the LAN does not already route the ZeroTier range):
sudo iptables -t nat -A POSTROUTING -o ztabc12def -j MASQUERADE
sudo iptables -A FORWARD -i ztabc12def -o eth1 -j ACCEPT
sudo iptables -A FORWARD -i eth1 -o ztabc12def -m state --state RELATED,ESTABLISHED -j ACCEPTPersist the rules:
sudo apt install -y iptables-persistent
sudo netfilter-persistent saveExample B: Full-tunnel exit gateway
To route all internet traffic from a remote laptop through the VPS:
- Add a managed route
0.0.0.0/0via10.147.17.10. - On remote clients, tick Allow Default Route in their ZeroTier client settings.
- Enable
net.ipv4.ip_forward=1and the MASQUERADE rule above on the VPS.
Step 7: Write Flow Rules for Network Segmentation
ZeroTier's Rules Engine applies a global firewall to every packet on the virtual network. Rules are written in a small declarative DSL in the Flow Rules panel on my.zerotier.com.
The default ruleset is:
# Drop TCP SYN,!ACK packets (i.e. new connections) not allowed by capabilityAllow anything else.
drop not ethertype ipv4 and not ethertype arp and not ethertype ipv6 ; accept;
Here is a more restrictive example that allows only SSH and HTTPS between members, with a dedicated "admins" tag that can reach everything:
# Define tags
tag admin
id 1000
enum 0 no
enum 1 yes
default no
;Drop non-IP traffic other than ARP and IPv6 neighbor discovery
drop
not ethertype ipv4
and not ethertype arp
and not ethertype ipv6
;Admins bypass everything
accept
tand admin 1
;Allow SSH (tcp/22) and HTTPS (tcp/443) between members
accept ipprotocol tcp and dport 22;
accept ipprotocol tcp and dport 443;Allow ICMP ping
accept ipprotocol icmp;Drop everything else
drop;Paste this into the Flow Rules editor and click Save. Then in the Members list, tick the admin tag on any laptop you want to grant bypass access to. The rules apply within a few seconds across every member, with no client-side configuration needed.
See the official Rules Engine reference for the full grammar (capabilities, match conditions, redirect, tee for traffic mirroring, etc.).
Step 8: Bridge ZeroTier to a Physical LAN (Optional)
Because ZeroTier is Layer 2, you can bridge its interface to a physical NIC and make the overlay a transparent extension of an on-prem Ethernet segment — something impossible with Layer 3 VPNs like WireGuard or Tailscale.
Mark the bridging node as a bridge on my.zerotier.com -> Members -> tick Allow Ethernet Bridging for the VPS.
Install bridge-utils and create a Linux bridge that joins eth1 (the physical LAN) and the ZeroTier interface:
sudo apt install -y bridge-utils
sudo ip link add name br0 type bridge
sudo ip link set eth1 master br0
sudo ip link set ztabc12def master br0
sudo ip link set br0 upRemove the IP from eth1 and reassign it to br0 so the host still has network access:
sudo ip addr flush dev eth1
sudo ip addr add 192.168.50.10/24 dev br0
sudo ip route add default via 192.168.50.1To make this persistent across reboots, use netplan (Ubuntu's default):
# /etc/netplan/01-zerotier-bridge.yaml
network:
version: 2
renderer: networkd
ethernets:
eth1:
dhcp4: no
bridges:
br0:
interfaces: [eth1]
addresses: [192.168.50.10/24]
routes:
- to: default
via: 192.168.50.1
parameters:
stp: false
forward-delay: 0Then sudo netplan apply. Re-add the ZeroTier interface to the bridge on boot using a systemd drop-in or a /etc/networkd-dispatcher/routable.d/ hook, since ZeroTier creates the zt* device after netplan runs.
Every device on 192.168.50.0/24 can now talk to every authorized ZeroTier member as if they shared a switch. Add managed route 192.168.50.0/24 via 10.147.17.10 so remote members know how to find the LAN.
Step 9: IPv6 with RFC 4193 and 6PLANE
ZeroTier supports two automatic IPv6 addressing schemes, both derivable from the network ID and node address without DHCPv6.
- RFC 4193 — generates a unique local address (ULA) in the
fd00::/8range, specificallyfd<8-byte network ID>::<6-byte node address>. This gives every node a globally unique but non-publicly-routable IPv6 address. Perfect for service-to-service traffic where you want stable IPv6 without running DHCPv6. - 6PLANE — a custom scheme in
fc00::/8designed for entire IPv6 subnets behind each node. Each node owns a/80, which means a single ZeroTier member can expose an unlimited number of Docker containers, VMs or LXC guests each with their own globally-unique-within-the-mesh IPv6 address.
sudo zerotier-cli get abcdef0123456789 ip6Expected output:
fd80:56c2:e21c:0:0199:93a1:b2c3:d4e5
fcab:cdef:0123:4567:89a1:b2c3:d4e5::1The first is the RFC 4193 address; the second is the 6PLANE /80. You can now ping6 between members with no further configuration:
ping6 fd80:56c2:e21c:0:0199:93a1:b2c3:d4e5RFC 4193 is the sane default for almost every deployment. Enable 6PLANE only when you genuinely need per-container global IPv6.
Step 10: Run Your Own Moon (Self-Hosted Root Server)
By default, every ZeroTier node connects to planetary roots run by ZeroTier Inc. Peer discovery and NAT traversal bootstrap through those roots. Once peers find each other, traffic is direct — but the metadata of "who is online and where" passes through planetary roots.
A moon is a self-hosted root server that you own. Nodes configured to orbit your moon use it for peer discovery instead of (or in addition to) planetary roots. Reasons to run a moon:
- Air-gapped or restricted networks where planetary roots are unreachable.
- Regulatory requirements forbidding any third-party infrastructure in the connection path.
- Lower latency peer discovery when all your members are regional.
- Resilience against planetary root outages or geopolitical blocks.
Create the moon configuration
On the VPS you want to use as a moon (pick one with a stable public IP and good uplink — the CloudCore Starter is ideal):
cd /var/lib/zerotier-one
sudo zerotier-idtool initmoon identity.public > moon.jsonEdit moon.json and set the stableEndpoints to the VPS's public IP and the ZeroTier port (default 9993):
"stableEndpoints": [ "203.0.113.55/9993", "[2001:db8::55]/9993" ]Generate the signed moon file:
sudo zerotier-idtool genmoon moon.jsonThis produces a file named like 000000deadbeef00.moon. Move it into the moons.d directory so this VPS acts as the moon itself:
sudo mkdir -p /var/lib/zerotier-one/moons.d
sudo mv 000000deadbeef00.moon /var/lib/zerotier-one/moons.d/
sudo systemctl restart zerotier-oneOrbit the moon from other nodes
Copy the .moon file to every other node (via scp, a config management tool, or bake it into your cloud-init). Then on each node:
sudo cp 000000deadbeef00.moon /var/lib/zerotier-one/moons.d/
sudo systemctl restart zerotier-oneAlternatively, orbit a moon by ID without the file:
sudo zerotier-cli orbit deadbeef00 deadbeef00The first argument is the world ID (the moon's ZeroTier address), the second is the seed (usually the same value). List orbited moons:
sudo zerotier-cli listmoonsPeers now discover each other through your moon. Open UDP 9993 inbound on the moon's firewall so other nodes can reach it.
Hardening the VPS Firewall
Regardless of whether you run UFW, nftables or cloud firewall rules, expose exactly one UDP port:
sudo ufw allow 9993/udp comment 'ZeroTier'
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableOptional but recommended:
- Fail2ban for the SSH port.
- Disable password SSH and use keys only.
- Restrict sudo to a non-root user.
- On the Ethernet-bridged node, ensure your physical interface has no direct public IP — attackers should only see port 9993/udp.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
zerotier-cli info shows OFFLINE | UDP 9993 blocked outbound | Allow UDP 9993 egress at firewall and hosting panel. Fall back to TCP: add "tcpFallbackRelay":"204.80.128.1/443" to /var/lib/zerotier-one/local.conf. |
Network status stuck on REQUESTING_CONFIGURATION | Node not yet authorized | Authorize it at my.zerotier.com -> Members or wait a few seconds after authorizing. |
| Node joins but cannot ping peers | Host firewall on the peer blocks ZeroTier interface traffic | On the peer: sudo ufw allow in on ztabc12def. |
Ping works but ssh does not | Flow rules block tcp/22, or host's own firewall | Inspect Flow Rules editor; add accept ipprotocol tcp and dport 22; |
| Bridged LAN reachable from VPS but not from remote members | Missing managed route on my.zerotier.com | Add 192.168.x.y/24 via <bridge-node-ZT-IP> in Managed Routes. |
| High latency between two nodes that should be direct | NAT traversal failed; traffic is relayed via roots | Check sudo zerotier-cli peers — RELAY means indirect. Forward UDP 9993 to one endpoint, or run a moon in between. |
orbit command says 400 error | Moon file not in moons.d and ID format wrong | Use only hex digits for world ID, or copy the .moon file instead. |
| After reboot, bridge loses ZeroTier interface | zt* devices appear after netplan | Add a systemd drop-in or networkd-dispatcher hook to ip link set ztX master br0 after zerotier-one.service starts. |
Inspecting peer state
sudo zerotier-cli peersExpected output columns: <ztaddr> <version> <role> <latency> <link> <lastTX> <lastRX> <path>. DIRECT is what you want. RELAY means traffic is going through a planetary root or moon.
Logs
sudo journalctl -u zerotier-one -fFAQ
Is ZeroTier free?
Yes, for up to 25 authorized members per network on the public controller at my.zerotier.com. Above that, paid plans start at a flat monthly price per controller. If you self-host the controller (via ztncui or ZeroTier's own enterprise controller), there are no member limits — you pay only for the VPS.
Does ZeroTier work behind carrier-grade NAT?
Yes. ZeroTier's UDP hole-punching succeeds in the large majority of CGNAT and double-NAT scenarios, because the planetary roots (or your moon) broker the initial exchange. If both endpoints are behind symmetric NAT with random port assignment, traffic relays through the nearest root until a direct path opens. Running your own moon in a well-connected data center significantly improves the hit rate.
Can I run ZeroTier in Docker or Kubernetes?
Yes. The official image is zerotier/zerotier. Run it with --cap-add=NET_ADMIN --device=/dev/net/tun and mount /var/lib/zerotier-one as a volume. For Kubernetes, the zerotier-kubernetes operator joins pods to a network and maps services to ZeroTier IPs. This is a common pattern for multi-cluster service mesh across clouds.
How is ZeroTier different from a regular VPN?
A regular VPN is a tunnel: one client, one server, hub-and-spoke. A mesh VPN like ZeroTier is a network: every authorized device can talk to every other device directly, without all traffic passing through a central concentrator. For two peers behind NAT, the traffic is genuinely point-to-point encrypted, not relayed.
Is the Business Source License (BSL) a problem?
ZeroTier One, the client and controller, is licensed under BSL 1.1 which converts to Apache 2.0 after 2 years. You can use, modify and self-host it freely for almost any purpose. The BSL restriction is narrowly scoped to prevent a competitor from launching a commercial SaaS identical to ZeroTier Central. Running a moon, a self-hosted controller, or embedding ZeroTier in your product is explicitly allowed.
Can I use ZeroTier and WireGuard together?
Absolutely. Many deployments use WireGuard for a high-throughput point-to-point tunnel between two data centers and ZeroTier for the end-user mesh on top. ZeroTier handles the dynamic membership and ACLs; WireGuard handles the backbone. See our WireGuard install guide for the complementary setup.
How do I monitor a ZeroTier network?
Scrape /var/lib/zerotier-one/peers.d or poll the local JSON API at http://127.0.0.1:9993/peer using the auth token at /var/lib/zerotier-one/authtoken.secret. Feed the metrics into Prometheus via zerotier-exporter or a simple shell script. Alert on peer state transitions, offline members, or high relayed-traffic ratios.
Next Steps
Your mesh is live. Here is where to go from here:
- Build a site-to-site mesh across providers — spin up a second ZeroTier node on an OVH or Hetzner VPS, join the same network, and suddenly your fleet is multi-cloud with shared private addressing.
- Deploy ztncui for a self-hosted controller — replace my.zerotier.com entirely with ztncui, a web UI on top of the local controller. Gives you unlimited members and keeps network metadata off ZeroTier Inc.'s servers.
- Compare with Tailscale — read our guide to installing Tailscale on Ubuntu to benchmark polish, MagicDNS and SSO against ZeroTier's approach.
- Go fully self-hosted with Headscale — if you want Tailscale's UX without the SaaS, see how to install Headscale on Ubuntu.
- Fall back to plain WireGuard for fixed peers — when you only need a tunnel between two static IPs, ZeroTier is overkill. The WireGuard guide shows the lean alternative.
- Read the official docs — the ZeroTier documentation portal covers the Rules Engine grammar, API, self-hosted controller, and planetary root architecture in depth.
Launch Your ZeroTier Mesh on CloudCore Starter>
The moon, the controller and the bridge gateway all run comfortably on our entry plan with room to spare.>
- 2 vCPU, 4 GB RAM, 80 GB NVMe
- Unmetered bandwidth
- Public IPv4 + IPv6 (needed for stable moon endpoints)
- Deploys in 60 seconds on Ubuntu 24.04 LTS>
Deploy a CloudCore Starter VPS and have your mesh live in under half an hour.