Tailscale makes WireGuard mesh networking effortless, but its coordination (“control”) server is a hosted service. Headscale is an open-source, self-hosted implementation of that control server. Your devices use the regular Tailscale clients, while keys, users, devices and ACLs are managed on your own VPS. It’s a good fit for privacy-minded teams, homelabs and anyone who wants no device limits.
How it fits together
- Tailscale clients on each device: Linux, Windows, macOS, iOS, Android.
- Headscale on your VPS coordinates them: it hands out keys and IPs (100.64.0.0/10), distributes the peer list, and enforces ACLs.
- Data traffic flows peer-to-peer over WireGuard between devices, not through Headscale. When direct connections aren’t possible, it goes through DERP relays (Tailscale’s public relays by default, or your own).
Headscale itself is light: a small VPS (1 vCPU, 512 MB–1 GB of RAM) handles many devices.
Requirements
- A VPS with a public IP, and a domain name, e.g.
hs.example.com→ the VPS IP. - HTTPS on port 443, required by clients. Headscale can get a Let’s Encrypt certificate itself, or sit behind Nginx or Caddy.
1. Install
Download the latest .deb from Headscale’s GitHub releases:
wget https://github.com/juanfont/headscale/releases/download/v0.26.0/headscale_0.26.0_linux_amd64.deb
sudo apt install ./headscale_*_linux_amd64.deb
(Use the current version. Docker images are also available.)
2. Configure
Edit /etc/headscale/config.yaml. Key settings:
server_url: https://hs.example.com
listen_addr: 127.0.0.1:8080 # behind a reverse proxy
metrics_listen_addr: 127.0.0.1:9090
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
dns:
magic_dns: true
base_domain: tail.example.com # must differ from server_url's domain
nameservers:
global:
- 1.1.1.1
- 9.9.9.9
3. TLS via a reverse proxy
Nginx with Let’s Encrypt (WebSocket upgrade headers are required):
server {
server_name hs.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_buffering off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 443 ssl; # certificates via certbot --nginx
}
See Nginx WebSocket proxy and Let’s Encrypt for Nginx. Caddy works too, with automatic HTTPS. See Caddy reverse proxy.
Start it:
sudo systemctl enable --now headscale
sudo systemctl status headscale
4. Create users and register devices
sudo headscale users create alice
sudo headscale users list
Pre-auth key (good for servers):
sudo headscale preauthkeys create --user alice --expiration 1h
# on the device:
sudo tailscale up --login-server https://hs.example.com --authkey <key>
Interactive login (laptops, phones): run tailscale up --login-server https://hs.example.com. The client shows a URL with a registration command for the admin:
sudo headscale nodes register --user alice --key <key-from-url>
Mobile apps let you set a custom control server in their settings (an alternate coordination server URL).
List devices:
sudo headscale nodes list
5. ACL policies
By default, all nodes can reach each other. Restrict it with a policy file (HuJSON), referenced in config.yaml (policy.path):
{
"groups": { "group:admins": ["alice@"], "group:dev": ["bob@"] },
"tagOwners": { "tag:server": ["group:admins"] },
"acls": [
{ "action": "accept", "src": ["group:admins"], "dst": ["*:*"] },
{ "action": "accept", "src": ["group:dev"], "dst": ["tag:server:22,443"] }
]
}
Reload Headscale after changes. Least privilege applies here too. See zero trust networking.
6. Subnet routers and exit nodes
Subnet router: a node that gives access to a whole network, such as a VPS’s private network:
sudo tailscale up --login-server https://hs.example.com --advertise-routes=10.0.0.0/24
sudo headscale nodes list-routes
sudo headscale nodes approve-routes --identifier <node-id> --routes 10.0.0.0/24
Exit node: route a device’s internet traffic through a VPS with --advertise-exit-node, approved the same way. Enable IP forwarding on that node first. See NAT explained.
Operations
- Back up
/var/lib/headscale/(the SQLite database and keys) and the config. - Update Headscale regularly, and read release notes. Config formats change between versions.
- Firewall: only 443 (and 80 for ACME) need to be public. SSH can then be limited to the tailnet. See UFW.
- Optional web UIs (community projects) make user and node management easier.
Headscale vs Tailscale SaaS vs ZeroTier
| Headscale | Tailscale (hosted) | ZeroTier | |
|---|---|---|---|
| Control server | Your VPS | Tailscale Inc. | Hosted or self-hosted |
| Clients | Official Tailscale apps | Same | ZeroTier apps |
| SSO / OIDC | Yes (OIDC config) | Built in | Limited |
| Features | Core features, some lag | Full | L2 networking |
| Cost | VPS only | Free tier, paid plans | Free tier, paid plans |
See Tailscale on a VPS and ZeroTier.
Quick reference
| Task | Command |
|---|---|
| Create user | headscale users create NAME |
| Pre-auth key | headscale preauthkeys create --user NAME |
| Register node | headscale nodes register --user NAME --key KEY |
| List nodes | headscale nodes list |
| Approve routes | headscale nodes approve-routes … |
| Client join | tailscale up --login-server https://hs.example.com |
Related: WireGuard VPN server and VPN vs proxy.