← Networkingself-host your own tailscale with headscale

self-host your own tailscale with headscale

$tailscale up --login-server https://your-server

tailscale · official source ↗

why 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.

watch the reel ↗
the weekly drop

one command a week that makes you harder to hack.

a single tool, explained in plain english, every week. straight to your inbox.

no spam. one email a week. unsubscribe anytime.