Why Run Your Own Tailscale Coordination Server
Tailscale makes building a private mesh network genuinely easy – connect devices across the internet as if they share a local network, without port forwarding or VPN configuration hell. But the coordination server that manages node authentication, key exchange, and access control runs on Tailscale’s infrastructure by default. For homelabbers, small businesses, and privacy-focused users, handing that control layer to a third party is an uncomfortable trade-off.
Headscale is the open-source answer. It reimplements the Tailscale control server protocol so you can self-host the coordination plane entirely, while still using the official Tailscale client on every device. Your traffic never flows through Headscale directly – it only handles authentication and route coordination – but ownership of that layer means no account limits, no pricing tiers, and no dependency on an external service staying online or keeping its current terms.
This guide walks through installing Headscale on a Linux server using Docker, creating users and preauth keys, and connecting your devices.

What You Need Before Starting
You need a Linux server with a public IP address and a domain name pointing to it. Headscale needs to be reachable by all your devices over HTTPS, so a VPS or a home server with a properly forwarded port 443 both work. The domain is required – Headscale won’t function correctly with just an IP address because the Tailscale client expects a valid HTTPS endpoint for coordination. If you already run a self-hosted stack and have a reverse proxy like Nginx or Caddy handling TLS termination, you can slot Headscale behind it the same way you would any other service.
Docker and Docker Compose should be installed on your server. If you prefer a native binary install, Headscale releases pre-built binaries and packages for common Linux distributions, but the Docker path is cleaner for most setups and easier to update. You also need the Tailscale client installed on every device you want to connect – download it from tailscale.com on each machine as normal, but you will not log in through the Tailscale dashboard. Instead, you will point each client at your Headscale instance during the login step.
Open ports 443 (HTTPS) and 3478 (UDP, for STUN) on your firewall. Port 3478 is what allows Headscale to help clients establish direct peer connections. Without it, traffic may still flow but will relay through DERP servers rather than punching through directly between devices.
Installing and Configuring Headscale
Create a working directory and pull the configuration template. The official Headscale Docker image is available at ghcr.io/juanfont/headscale. Start with a docker-compose.yml file:
- Set the image to ghcr.io/juanfont/headscale:latest
- Mount a local ./config directory to /etc/headscale in the container
- Mount a local ./data directory to /var/lib/headscale
- Expose port 8080 to your reverse proxy, or map 443 directly if you’re not using one
- Set the command to headscale serve
Download the sample config file from the Headscale GitHub repository and save it as ./config/config.yaml. The fields you must edit are server_url (set this to your full domain with HTTPS, like https://headscale.yourdomain.com), listen_addr (set to 0.0.0.0:8080 for Docker), and db_path (point this to /var/lib/headscale/db.sqlite). If you want to disable the built-in DERP map and use Tailscale’s public DERP servers for relay, leave the derp.urls field pointing to the default Tailscale DERP map URL. Set magic_dns to true if you want hostnames to resolve across the network automatically.

With your reverse proxy configured to terminate TLS and forward traffic to port 8080 on the container, bring the stack up with docker compose up -d. Confirm it is running with docker compose logs -f. You should see Headscale print its startup sequence with no errors. If you see a database error, check that the ./data directory exists and is writable by the container user. Once it is healthy, create your first user namespace with:
- docker compose exec headscale headscale users create myuser
Headscale groups devices under “users” the same way Tailscale groups them under accounts. You can create multiple users for different access segments, and ACL policies can reference them. For a homelab or small team, one user is typically enough to start.
Connecting Devices to Your Headscale Server
On each device running the Tailscale client, you connect to Headscale instead of Tailscale’s coordination servers by passing a custom login server flag. On Linux, run:
- tailscale up –login-server https://headscale.yourdomain.com
The client will print a URL you need to visit to complete authentication. Unlike the standard Tailscale flow, this URL is a Headscale-generated link that you authenticate manually on the server side. Copy the URL from the terminal output, then on your server run:
- docker compose exec headscale headscale nodes register –user myuser –key [the-node-key-from-the-url]
The node key appears at the end of the URL the client printed. After running that command, the device shows as registered and the Tailscale client on the device will report a connected status with a Tailscale IP assigned. Repeat this for every device. For devices where copy-pasting from a terminal isn’t practical – phones, tablets, or remote machines – generate a reusable preauth key instead:
- docker compose exec headscale headscale preauthkeys create –user myuser –reusable –expiration 24h
Pass that key during login with tailscale up –login-server https://headscale.yourdomain.com –authkey [key]. The device registers without any manual approval step on the server.

Managing Your Network Going Forward
List all registered nodes with headscale nodes list, view routes with headscale routes list, and approve advertised subnet routes with headscale routes enable –route [route-id]. Headscale’s CLI covers everything you would otherwise do through the Tailscale web dashboard. For access control, drop a valid Tailscale ACL policy file at the path specified by acls.policy_path in your config – Headscale reads the same HuJSON format Tailscale uses, so any ACL policy written for Tailscale works here without modification. If you already run a self-hosted dashboard to manage services – something like Dashy as a homepage – you can add Headscale’s web UI as a tile for quick access to its status endpoint. The one genuine limitation compared to the managed Tailscale experience is the admin UI: Headscale ships a minimal web interface, and the Tailscale mobile apps will not show a proper login flow for custom servers on older versions, which means device onboarding for non-technical users still requires a few extra steps.





