
self-host your own tailscale with headscale
tailscale up --login-server https://your-serverwhy your vpn's control plane matters
tailscale is genuinely great. wireguard under the hood, dead simple mesh networking, works across nat without you touching a single port forward. but every device you add checks in with tailscale's servers to figure out who's allowed to talk to who. that's fine for most people. but if you're the type who wants zero third parties holding the keys to your network, or you just want to understand exactly how this stuff works under the hood, headscale lets you run that same control plane yourself.
this isn't about distrust of tailscale specifically. it's about knowing what "control plane" even means, and why owning it changes your threat model for the better if you're running sensitive infra, homelabs, or client networks.
what headscale actually replaces
tailscale has two parts. the client (the actual wireguard tunnels doing the encrypting) and the coordination server (the thing that tells clients about each other, hands out keys, and enforces acls). headscale reimplements that second part as an open source project. your devices still run the normal tailscale client, totally unmodified, they just point at your server instead of tailscale's.
that means you get the same peer to peer mesh, the same nat traversal tricks, the same easy "tailscale up" experience, but the list of who's in your network and what they can reach lives on hardware you control.
spinning it up: one container on a small vps
you don't need much. a $5/month vps with docker is plenty since headscale itself is lightweight, it's just coordinating metadata, not routing your actual traffic. pull the official image, mount a config volume, and expose the port over https (this matters, more on that below).
docker run -d --name headscale \
-v $(pwd)/config:/etc/headscale \
-v $(pwd)/data:/var/lib/headscale \
-p 8080:8080 \
headscale/headscale serve
get this behind a reverse proxy with a real tls cert. don't run coordination traffic over plain http, that's the part that authenticates your whole mesh.
creating a user namespace
headscale users create home
headscale organizes devices under "users," which is really just a logical grouping, not necessarily a human. think of it as a namespace for a set of machines that'll share policy together. you might have one for "home," one for "work-lab," one for "clients" if you're consulting. keeping these separate means a compromised key in one group doesn't automatically expose everything else.
preauth keys: the part people get wrong
headscale preauthkeys create --expiration 1h
this generates a one-time key that lets a new device join without you manually approving it on the server. the expiration flag is the whole point. a lot of self-hosted tutorials show you generating keys with no expiration, or worse, reusable ones, because it's convenient for testing. don't ship that to production. a long-lived preauth key sitting in a shell history or a provisioning script is a standing door into your mesh. one hour is usually plenty of time to spin up the one box you meant to add.
joining a device
tailscale up --login-server https://your-server
this is the normal tailscale client, told to authenticate against your headscale instance instead of the default. the device presents its preauth key (or goes through the normal browser login flow if you set that up), headscale checks it against its user list, and the device gets added to the mesh with whatever acl policy you've defined.
the takeaway
running headscale doesn't make you magically more secure than using tailscale's hosted service, it just moves the trust boundary. you're now responsible for patching the server, locking down the admin api, rotating preauth keys, and backing up your sqlite state (lose that file and you lose your whole device registry). if you're comfortable owning that, you get a vpn control plane with no external dependency and full visibility into every acl rule and every device that's ever joined.
if you do stand this up: put headscale behind tls, never hand out preauth keys without an expiration, review your acl file like you'd review a firewall rule set, and keep an eye on who's actually in your users list. a self-hosted anything is only as safe as the attention you give it.