Tailscale's coordination servers know which of your machines exist, where they are, and who may talk to whom — and until recently, almost nobody self-hosted that piece. That changed quietly and then all at once: Headscale, the open-source reimplementation of the Tailscale control server, now sits at more than 43,000 GitHub stars, making it one of the most-starred networking projects in open source. Thousands of teams run their entire tailnet against a control plane they own, using the unmodified official Tailscale clients.
This post is the concrete version of that story: what Headscale actually replaces (and what it deliberately does not), the exact build sequence for meshing a multi-region fleet with it, what it costs next to Tailscale's 2026 pricing, and the one honest limitation — a coordination plane of one — you must self-host around.
The split that makes Headscale possible: control plane vs data plane
Every Tailscale network has two halves, and only one of them ever touches your bytes. The control plane (also called the coordination server) hands out node addresses in the 100.64.0.0/10 CGNAT range, distributes WireGuard public keys, pushes ACL policy, resolves MagicDNS names, and tells clients which relay region to use when direct connections fail. The data plane is plain WireGuard between your machines: direct peer-to-peer, end-to-end encrypted, with keys the coordination server never holds.
Headscale replaces only the first half. Your laptops, servers, and containers still run the official Tailscale client; they just point at your server instead of Tailscale Inc.'s:
tailscale up --login-server=https://headscale.example.comThat one flag is the whole migration. NAT traversal, endpoint discovery, encryption, subnet routing, exit nodes, Taildrop file sharing — all of it keeps working, because all of it was always client-side. What you take back is the metadata layer: the registry of who is online, the ACL evaluation, the DNS, and the kill switch. Nobody upstream can throttle, reprice, or shut off node-to-node connectivity, because no upstream remains in the path.
| Concern | Tailscale hosted | Headscale self-hosted | Hand-rolled WireGuard |
|---|---|---|---|
| Coordination server | Tailscale Inc.'s cloud | Your VM or container | None — static configs |
| Client apps | Official, all platforms | Same official clients | wg-quick + hand config |
| Key exchange | Automatic | Automatic | Manual per peer pair |
| NAT traversal / DERP relay | Global DERP fleet | Embedded or self-hosted DERP | DIY, usually absent |
| Access control | ACLs in admin console | ACL policy file (HuJSON) | Firewall rules per host |
| Identity | Google, Okta, Entra, OIDC | Pre-auth keys + self-hosted OIDC | SSH your keys around |
| Per-seat coordination cost | Priced per user (see below) | One small server, flat | Your time, unbounded |
The hand-rolled column is the reason this matters operationally. Static WireGuard scales to about five machines before every onboarding becomes a key-distribution ceremony and every NAT change becomes an outage. Headscale keeps WireGuard's data plane and deletes the ceremony.
Standing it up for a multi-region fleet
Here is the concrete build for a fleet spanning two Hetzner regions plus a homelab — the shape the TODO for this post asked about. Three moving parts: one Headscale server, one subnet router per site, and an ACL file.
1. One small server for the control plane. Headscale ships as a single Go binary with SQLite by default (Postgres for larger fleets). The minimal config.yaml decisions that matter:
server_url: https://headscale.example.com
dns:
magic_dns: true
base_domain: fleet.example.com
derp:
server:
enabled: true
region_id: 999
region_code: "home-derp"server_url must be a public, TLS-terminated URL the nodes can reach — Headscale can fetch its own Let's Encrypt certificate, or sit behind Caddy or nginx. The embedded DERP block is the piece most guides skip: it gives your tailnet its own relay region for the networks where UDP hole-punching genuinely fails (hotel Wi-Fi, CGNAT exit nodes), so fallback traffic stays geographically close instead of hairpinning through someone else's relay fleet. Open UDP 3478 for DERP/STUN alongside TCP 443, or relays silently never happen and you will misdiagnose it as a client bug.
2. Enroll nodes with pre-auth keys, not clicks. Create a user per trust domain, mint a reusable pre-auth key, and bake tailscale up --login-server=... --auth-key=... into your machine provisioning (cloud-init, your Cluster API bootstrap, Ansible — wherever nodes are born). OIDC against your own identity provider works too, and node expiry can follow token expiry, but pre-auth keys are the boring default that survives provider outages.
This is the correct seam for a Cluster API fleet: every Machine that joins the infrastructure joins the tailnet in the same boot script.
3. One subnet router per site, approved explicitly. On one node per region, advertise the local routes:
tailscale up --login-server=https://headscale.example.com \
--advertise-routes=10.0.0.0/8,192.168.0.0/16Then approve them server-side — Headscale never auto-enables advertised routes, which is a security property, not a papercut:
headscale nodes approve-routes --identifier <ID> --routes "10.0.0.0/8,192.168.0.0/16"Repeat per region and every site's private ranges become reachable from every node, with the WireGuard transport direct wherever the network allows and DERP-relayed where it does not. The Kubernetes ecosystem around this is mature: a Headscale operator exposes auto-approvers as CRDs, Helm charts package the server, and Headplane adds a web UI over the API for teams that want one.
4. Write the ACL file before you need it. Headscale evaluates the same HuJSON policy language as Tailscale, including autogroups and tag-based rules for subnet routers and exit nodes. The default-deny posture you want — engineers reach everything, CI runners reach the cluster API servers, exit nodes only route tagged traffic — is a hundred-line JSON file in version control instead of clicks in a dashboard. That file being in git is half the sovereignty argument: the network's access policy gets reviewed, diffed, and rolled back like any other config.
The honest catch: a coordination plane of one
Headscale runs as a single instance. There is no active-active mode and no built-in failover: if the control-plane VM dies, no new nodes can enroll, routes cannot change, ACL edits cannot push, and clients retry with exponential backoff until it returns. Say it plainly in the runbook, because this is the dependency you traded Tailscale Inc. for.
The blast radius is smaller than it sounds, for one architectural reason: established WireGuard sessions keep flowing while the control plane is down. Peers already hold each other's keys and endpoints; there is nothing to re-authorize per packet. A control-plane outage freezes membership changes, it does not drop existing tunnels. That is the difference between "the VPN is down" and "nobody new can join the VPN for twenty minutes," and your on-call severity should reflect it.
Self-hosting it correctly therefore means three unglamorous things. Back up the database — SQLite file copies or Postgres point-in-time recovery, tested by restore, not by assumption. Monitor the process like any singleton: systemd or Kubernetes liveness, an alert on port 443, a synthetic tailscale status from a canary node.
And place DERP capacity near your users, not near your control plane: one relay region per continent where your nodes roam keeps the fallback path short when direct connections fail. None of this is exotic. It is the standard singleton playbook, and it costs a fraction of one engineer's afternoon per quarter.
What the metered alternative costs in 2026
Tailscale overhauled its pricing in April 2026: the free Personal plan now covers up to six users with unlimited devices (up from three users and 100 devices), team plans start around $6 per user per month, and Premium climbs to roughly $18 per user per month with device posture, MDM, and audit features. For a 50-person engineering org, that is on the order of $300 per month on the entry team tier — about $3,600 a year, every year, scaling linearly with headcount.
Headscale's equivalent is one small server — a flat few euros a month on a Hetzner CX-class box with terabytes of included traffic — plus the DERP bandwidth your fallback traffic actually consumes, which for a fleet whose peers mostly connect directly rounds to noise. The breakeven math is not subtle: the moment your tailnet exists to connect infrastructure rather than seat humans, per-user pricing charges you for the wrong axis. Machines do not have seats.
When hosted Tailscale still wins
Intellectual honesty requires the other column. Stay on the hosted control plane when your auditors need a vendor with a SOC 2 report and a support SLA rather than your runbook; when identity lifecycle (SCIM provisioning, MDM enrollment, device posture checks) must be enforced by the platform instead of your OIDC wiring; and when the team running the network is the same team that would page on the singleton at 3 a.m. — and that team is already overloaded.
A 50-person company paying $300 a month for someone else to own coordination, compliance evidence, and enterprise identity is buying sleep, and sleep is fairly priced. Self-hosting the control plane is for teams whose infrastructure already has an owner and whose threat model names the vendor as a dependency worth removing.
Why this is a PaaS story, not just a VPN story
A self-hosted platform lives or dies on how many third-party control planes can take it down. DNS, container registries, identity, tunnels — each hosted dependency is a line in someone else's status page that becomes your outage. Meshing a multi-region Cluster API fleet over Headscale removes one of those lines permanently: nodes in Nuremberg, Singapore, and the homelab rack reach each other over WireGuard coordinated by a binary you run, under ACLs you version, with relay fallback on ports you opened. When an AI agent operator needs to SSH into a stuck node at 2 a.m., the path there does not traverse a SaaS whose maintenance window you never approved.
That is the deeper meaning of 43,000 stars. Not that Tailscale is bad — it is excellent, which is why Headscale reuses its clients, its protocol, and its policy language. The stars say that excellent software with a vendor-owned coordination point eventually grows an open-source shadow, and that thousands of teams would rather own the boring registry in the middle than rent it per seat.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



