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.

  • VPN
  • Self-Hosting
  • Networking