I did not get the Tailscale pitch until I installed it. The client goes on a device, you log in once, and the rest of the tailnet shows up like it was always there. File servers, home boxes, the laptop on the couch, the work Mac at the desk. The mesh is real and it works. What I did not register on day one is that every device on my tailnet is checking in with someone else’s coordination server to figure out which other devices exist and how to reach them. The actual traffic stays peer-to-peer. The directory of devices, the key exchange, the topology updates, that lives on Tailscale’s side.
The split is clean enough that it is easy to miss. WireGuard (a kernel-level VPN protocol that encrypts traffic between two endpoints) handles the tunnels between devices. Tailscale’s coordination server keeps the device list in sync, hands out keys, and tells each client where to find the others. The traffic is yours but the address book is theirs. Once that clicked, the next move was obvious: replace the coordination server with something I control, and keep the rest of the setup.
That replacement is Headscale. It is an open-source implementation of the Tailscale coordination server, and the drop-in design is the reason it works. You do not have to give up the official Tailscale client on any of your devices. You point them at your own server and the experience is identical from the device’s perspective.
What Headscale actually replaces
The mental model is worth being clear about. Tailscale has two pieces. The first is a mesh of WireGuard tunnels between the devices on your tailnet. The second is the control plane, the server that knows which devices exist, what their public keys are, and how they should find each other on the public internet. Headscale replaces the second piece only. The tunnels, the keys, and the client on your laptop are still the official Tailscale code.
The reason this matters is that you do not have to learn a new client. The same tailscale up command works. The same MagicDNS names resolve. The same exit nodes and subnet routes are configured through the same ACL file. Headscale just becomes the server that the clients check in with. If you have ever set up a Tailscale account and used the dashboard, you have already done most of the conceptual work.
Headscale is designed for a single tailnet, not for the multi-tenant enterprise case. The official Tailscale coordination server can host many separate tailnets under one organization. Headscale is one tailnet per instance, which is exactly what a homelab, a small studio, or a personal setup needs and exactly what an enterprise would not.
The parts of the control plane you actually own
When you run Headscale on your own hardware, the device list lives in a SQLite or Postgres database on your server. The key exchange happens on your server. The ACL file lives on your server, and the policy decisions about which device can reach which other device happen on your server. The public-facing relay nodes (DERP, the relays Tailscale runs for cases where direct peer-to-peer fails) are still the official Tailscale ones by default, though you can run your own DERP server if you want to own that part too.
This is the privacy win in concrete terms. The coordination server sees your device names, your IP addresses, the times your devices come online, and the topology of your network. It does not see the traffic between your devices, that still flows over WireGuard directly. The list of what is on your tailnet, and the schedule of when each device shows up, that is what moves onto your hardware. For a homelab, that is a meaningful change. For a small business with compliance requirements, it can be the difference between Tailscale being an option and not.
The Docker setup is the fast path
Headscale ships as a single binary plus a configuration file. The official Docker image wraps both, and the fastest path to a working install is the compose file in the Headscale documentation. The pattern is a single container with a mounted config directory and a mounted data directory for the database, fronted by a reverse proxy that handles the HTTPS certificate.
The configuration file is YAML, and the defaults are sensible. The first decision worth making is the database backend. SQLite is the right choice for a personal install. The file lives on disk, the backups are just a file copy, and the write volume of a single tailnet is small. Postgres is the option for a small team with more concurrent device registrations, but the operational overhead is real and not worth it for the homelab case.
The second decision is the base domain. Headscale identifies the control server through a domain like control.example.com, and the Tailscale clients use that domain when they register. Pick a domain you control, point an A record at your server, and use the same domain in the Headscale config. The clients will refuse to register against a domain that does not resolve, which is the right failure mode.
Move the existing clients over
If you already have a Tailscale tailnet, the migration is not instant but it is not painful either. The Headscale documentation walks through the namespace mapping, the part that lets the same device show up on your new control server under a new identity. The basic shape is to register each device against Headscale using a pre-auth key, accept the device on the server, and decommission the old identity through the official Tailscale admin console.
The part worth budgeting time for is the ACL file. The default ACL on a fresh Headscale install is permissive. If your existing Tailscale tailnet has a tight ACL, porting the policy file over is the step that actually moves the security model onto your server. The ACL syntax is the same one Tailscale uses, so the port is a copy with a few identifier changes.
The MagicDNS names need a small adjustment too. The clients will still resolve the same names, but the search domain becomes your Headscale domain instead of the Tailscale one. Anything in your tooling that hardcodes the old search domain needs an update. This is a small thing, but it is the kind of small thing that breaks scripts two weeks after the migration if you do not catch it now.
Run your own DERP if you want to own the relays too
Tailscale’s default DERP servers are how clients find each other when direct peer-to-peer is blocked. Most of the time the direct path works, but on networks that block incoming UDP (the protocol WireGuard uses) or that NAT aggressively, the DERP relay is the fallback that keeps the tailnet reachable. Running your own DERP server means even that fallback traffic is yours.
The DERP server is a small Go binary that listens on a public port and reports its presence to your Headscale server. The configuration in Headscale adds the DERP map, and the clients pick the closest DERP automatically. For a homelab on residential internet, the bandwidth and reliability of a DERP relay is rarely the bottleneck. The reason to run your own DERP is the same as the reason to run your own Headscale: the relay is one more piece of the network under your control, and one more party removed from the data path.
Trade-offs
Headscale is the right call if you want the Tailscale experience with the coordination server on your own hardware. The trade-offs are real, and naming them honestly is part of why this works.
- Single tailnet per instance is fine for a homelab and a hard limit for an enterprise. Multi-tenant deployments are not what this is.
- The official DERP relays are the default, and the privacy story is incomplete if you stop at Headscale. The DERP traffic is still touching Tailscale’s servers.
- Updates to the Tailscale client can outpace Headscale’s protocol support, and the gap shows up as a registration error after a client upgrade. Pinning client versions or watching the Headscale release notes is part of the operational cost.
- Backups are your problem now. The SQLite database is a file, but the ACL file and the pre-auth keys are part of the same backup story. Losing the database means re-registering every device.
- The MagicDNS search domain changes during the migration, and any script or systemd unit that hardcoded the old domain needs a touch-up.
- Headscale is a single project with a small maintainer team relative to Tailscale. The bus factor (the risk that key contributors leave and the project stalls) is part of the long-term risk profile.
Bottom line
Headscale is the right move if you want a Tailscale-style mesh and you want the address book on your own hardware. The setup is a single Docker container and a YAML file. The migration from an existing tailnet is a few hours, most of which is the ACL port and the MagicDNS search domain. The privacy story improves meaningfully, the operational cost goes up by an order of magnitude, and the result is a tailnet that is genuinely yours.
If you only do one thing first, get the Docker container running on a domain you control before you migrate any device. The rest of the migration is faster when the control server is already answering, and a clean install with a working base domain is a better starting point than a half-migrated tailnet with a stale search domain.