ZeroTier creates virtual Ethernet networks that span servers, laptops and phones, with peer-to-peer encrypted links and automatic NAT traversal. Membership, IP addresses and routes are managed centrally in ZeroTier Central, while traffic flows directly between devices whenever possible. In this tutorial you will install ZeroTier on Ubuntu 24.04, create a private network, join and authorize nodes, and use one server as a router to reach a private subnet.
Prerequisites
To follow this tutorial, you will need:
- At least one server running Ubuntu 24.04 LTS, for example a CubePath VPS, with a non-root user that has
sudoprivileges. - A second device to test with (another server, or a laptop with the ZeroTier client).
- A ZeroTier Central account at
my.zerotier.com. - Outbound UDP 9993 allowed. ZeroTier falls back to relays when UDP is blocked, but performance is worse.
Step 1 - Installing ZeroTier
ZeroTier distributes its Linux packages through an install script that adds the official APT repository and signing key. The script itself is GPG-signed, so import ZeroTier's public key and verify it before running anything:
curl -fsSL 'https://raw.githubusercontent.com/zerotier/ZeroTierOne/main/doc/contact%40zerotier.com.gpg' | gpg --import
curl -fsSL https://install.zerotier.com -o zt-install.sh.asc
gpg --output zt-install.sh --decrypt zt-install.sh.asc
The last command must report Good signature from followed by the ZeroTier key, which uses the address [email protected]. It is normal to also see a warning that the key is not certified with a trusted signature. Review the extracted script, then run it:
less zt-install.sh
sudo bash zt-install.sh
The script installs the zerotier-one package and enables its service. Verify the daemon is online and note your node ID (the 10-character address):
sudo zerotier-cli info
200 info a1b2c3d4e5 1.14.2 ONLINE
Here a1b2c3d4e5 is the node ID; the version number will vary.
Step 2 - Creating a network in ZeroTier Central
Log in to ZeroTier Central and create a new network. Central generates a 16-character network ID, for example 8056c2e21c000001. Open the network and review two settings:
- Access control: leave it Private, so every new member must be authorized before it can join.
- IPv4 auto-assign: pick one of the suggested ranges (this tutorial uses
10.147.17.0/24). Central then hands out addresses from that pool and adds a matching managed route.
Step 3 - Joining nodes to the network
On each server that should be part of the network, join it with the network ID. Replace your_network_id with your own:
sudo zerotier-cli join your_network_id
200 join OK
Check the membership status:
sudo zerotier-cli listnetworks
200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
200 listnetworks 8056c2e21c000001 my-network 5a:1b:2c:3d:4e:5f ACCESS_DENIED PRIVATE ztabcdef12 -
ACCESS_DENIED is expected on a private network: the node is waiting for authorization.
Step 4 - Authorizing members
In ZeroTier Central, open the network's member list. Each node that tried to join appears with its node ID. Check the authorization box next to the nodes you recognize, and optionally give each one a name.
You can also authorize members through the Central API, which is convenient when provisioning servers with scripts. Create an API token in your Central account settings and run:
curl -X POST "https://api.zerotier.com/api/v1/network/your_network_id/member/a1b2c3d4e5" \
-H "Authorization: token your_api_token" \
-H "Content-Type: application/json" \
-d '{"config": {"authorized": true}}'
Back on the node, the status changes to OK and an address from the pool appears:
sudo zerotier-cli listnetworks
200 listnetworks 8056c2e21c000001 my-network 5a:1b:2c:3d:4e:5f OK PRIVATE ztabcdef12 10.147.17.21/24
The interface name (ztabcdef12 here) is derived from the network ID and is the same on every Linux member of that network. Confirm it exists and ping another member:
ip -br addr show ztabcdef12
ping -c 3 10.147.17.35
To see whether peers connect directly or through a relay, list them:
sudo zerotier-cli peers
The link column shows DIRECT for peer-to-peer paths and RELAY when traffic is relayed.
Step 5 - Routing a private subnet through a member
ZeroTier members only reach each other by default. To let them reach a subnet behind one server, for example a private network 10.0.0.0/24 on that server's interface eth1, you turn that server into a router.
First, in ZeroTier Central add a managed route in the network settings:
- Destination:
10.0.0.0/24 - Via: the router's ZeroTier IP, for example
10.147.17.21
Then, on the router, enable IPv4 forwarding:
sudo nano /etc/sysctl.d/99-zerotier.conf
net.ipv4.ip_forward = 1
sudo sysctl -p /etc/sysctl.d/99-zerotier.conf
Hosts on 10.0.0.0/24 do not know how to reply to 10.147.17.0/24. The simplest solution is to masquerade ZeroTier traffic as it leaves eth1. With UFW, add a NAT section at the very top of /etc/ufw/before.rules, before the existing *filter line:
sudo nano /etc/ufw/before.rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.147.17.0/24 -o eth1 -j MASQUERADE
COMMIT
Allow forwarded traffic from the ZeroTier interface to the private interface and reload UFW:
sudo ufw route allow in on ztabcdef12 out on eth1
sudo ufw reload
From another ZeroTier member, confirm the route is installed and that a private host answers:
ip route get 10.0.0.5
ping -c 3 10.0.0.5
10.0.0.5 via 10.147.17.21 dev ztabcdef12 src 10.147.17.35 uid 1000
NoteReplace
eth1andztabcdef12with the interface names on your router. Useip -br addrto list them.
Step 6 - Restricting services to the ZeroTier network
A common pattern is to expose administrative services only on the ZeroTier interface. With UFW, allow SSH from the virtual network and allow UDP 9993 so peers can connect directly:
sudo ufw allow in on ztabcdef12 to any port 22 proto tcp
sudo ufw allow 9993/udp
sudo ufw enable
sudo ufw status verbose
Once you have confirmed you can log in over ZeroTier (ssh [email protected]), you may remove the public SSH rule with sudo ufw delete allow OpenSSH, keeping the CubePath web console as a fallback.
ZeroTier Central also has network-wide Flow Rules that filter traffic on every member. The default rule set drops non-IP traffic and accepts everything else; edit it only after reading ZeroTier's rules engine documentation, since a mistake applies to the whole network.
Troubleshooting
Status stays at REQUESTING_CONFIGURATION. The node cannot reach the controller. Check outbound UDP 9993 and run sudo zerotier-cli info; if it shows OFFLINE or TUNNELED, the network path is filtered.
Status is ACCESS_DENIED. The member has not been authorized yet in ZeroTier Central (Step 4).
Members can ping each other but not the private subnet. Check that the managed route exists in Central, that sysctl net.ipv4.ip_forward returns 1 on the router, and that sudo ufw status verbose lists the route rule. Also make sure the private hosts' own firewalls accept traffic from the router's private IP.
Service logs. Use sudo journalctl -u zerotier-one to check for errors after restarts or upgrades.
Conclusion
You now have a private ZeroTier network with authorized members, automatically assigned addresses and a server routing a private subnet to the rest of the network. From here you can add laptops and phones with the ZeroTier apps, automate member authorization through the Central API during server provisioning, or move internal services so they only listen on the ZeroTier interface.
