A mesh VPN is one of those tools that quietly makes everything easier: your laptop, home server, phone and VPSes all see each other on private addresses, wherever they are. The catch with hosted mesh VPNs is the coordination server — a company keeps the map of every device you own. Headscale is an open-source replacement for that control server. Run it on a small VPS, keep using the normal client apps, and the map is yours.
How it fits together
- Headscale (on your VPS) hands out addresses, distributes public keys and enforces your ACLs.
- Clients are the standard Tailscale apps, pointed at your server.
- Traffic goes device-to-device over WireGuard. The control server never carries it.
That last point is why a tiny plan works: the VPS only answers small coordination requests.
What you need
- Nano-IP ($8/mo) — 2 vCPU, 1 GB RAM is far more than Headscale uses.
- A dedicated IPv4 and a domain, e.g.
hs.example.com. Devices talk to the control server over HTTPS, so the VPS must accept inbound 443 — NAT plans don't (details).
Install (Debian 12 / Ubuntu 24.04)
Grab the current .deb from the Headscale releases page on GitHub — replace the version with the latest one:
HS_VER=0.26.1
wget -O headscale.deb \
"https://github.com/juanfont/headscale/releases/download/v${HS_VER}/headscale_${HS_VER}_linux_amd64.deb"
apt install -y ./headscale.deb caddy
Edit /etc/headscale/config.yaml — the two lines that matter:
server_url: https://hs.example.com
listen_addr: 127.0.0.1:8080
Put Caddy in front for HTTPS:
cat > /etc/caddy/Caddyfile <<'EOF'
hs.example.com {
reverse_proxy 127.0.0.1:8080
}
EOF
systemctl enable --now headscale
systemctl reload caddy
Add a user and a device
headscale users create alice
headscale users list # note the user's ID
headscale preauthkeys create --user 1 --expiration 24h
Older releases take the user name instead of the ID in --user; headscale preauthkeys create --help tells you which yours expects.
On a Linux device:
tailscale up --login-server https://hs.example.com --authkey YOUR_KEY
On phones and desktops, the Tailscale app lets you set a custom control server in its settings. Run headscale nodes list on the VPS and your device should be there.
Relays and NAT traversal
Most devices connect directly. When two of them can't (strict mobile networks, double NAT), traffic goes through a DERP relay. Out of the box Headscale uses Tailscale's public relays — they only see encrypted packets. If you want nothing external in the path, enable the embedded relay in config.yaml (derp.server.enabled: true) and open UDP 3478. The relayed traffic then counts against your VPS bandwidth.
Honest limits
There's no official web dashboard: users, keys and routes are CLI commands (fine once you've done it twice). Newer features of the hosted service show up in Headscale later, if at all. And the control server is now your responsibility — if it's down, existing connections keep working, but new devices can't join and keys can't refresh. Back it up: the whole state is /var/lib/headscale.
For a home lab, a family, or a small team that wants its device map off third-party servers, it's one of the best uses of an $8 VPS.
Related
- Self-host a WireGuard VPN — plain WireGuard, if you only need one tunnel
- VPS for a VPN
- VPS for Home Assistant remote access
Comments
No comments yet. Be the first.