WireGuard Is Perfect, Actually, If You Enjoy Configuration Suffering
Let me be clear: WireGuard is technically excellent. The codebase is tiny, the cryptography is modern, the performance is great. Setting it up involves generating keypairs on every device, distributing public keys, defining allowed IPs, configuring endpoints, handling NAT traversal yourself, and repeating the whole process every time you add a device or your home IP changes.
Calling Tailscale “just WireGuard, but easy” is a bit like calling a car “just an engine, but with wheels.” Technically true. Also missing the entire point. I spent two years running WireGuard by hand across a handful of boxes, and the week a laptop got stolen and I had to rotate every key on every peer at 2 AM is the week I stopped being precious about “real” networking and installed Tailscale instead.
Tailscale wraps WireGuard in a coordination layer that handles all of that automatically. Every device gets a 100.x.x.x address that works no matter where either end is. No port forwarding, no key distribution, no NAT headaches. It just works, which is either a blessing or deeply suspicious depending on your paranoia level.
This article assumes you’ve already run tailscale up and thought “cool, what else can this do?” The answer: quite a bit.
The Control Plane vs. The Data Plane
A distinction that clears up most of the confusion people have about Tailscale’s trust model:
Data plane: the actual WireGuard tunnels between your devices. Traffic flows directly peer to peer (or through relay servers when a direct connection isn’t possible). Tailscale never touches your actual traffic. This part is verifiable, it’s just WireGuard.
Control plane: the coordination server that distributes public keys, manages device registration, enforces ACLs, and keeps the network topology in sync. This runs at login.tailscale.com by default, and Tailscale does see metadata about your devices here (hostnames, IPs, who’s connected to what).
When people worry about Tailscale “seeing their traffic,” they’re conflating the two planes. Your packets are still end-to-end WireGuard. What you’re actually trusting Tailscale with is the control plane, and if that bothers you, Headscale (covered below) lets you run your own.
ACL Policies: Actually Control Who Talks to What
By default, every device in your tailnet can reach every other device on every port. Fine for one person and a laptop. Increasingly uncomfortable once you’re mixing work laptops, home servers, and IoT junk on the same network.
ACL policies live in the Access Controls section of the admin console, written in HuJSON (JSON with comments allowed, mercifully).
{ "groups": { },
"tagOwners": { "tag:server": ["group:admin"], "tag:media": ["group:admin"], "tag:homelab": ["group:admin"] },
"acls": [ { "action": "accept", "src": ["group:admin"], "dst": ["*:*"] }, { "action": "accept", "src": ["group:family"], "dst": ["tag:media:32400"] }, { "action": "accept", "src": ["tag:homelab"], "dst": ["tag:homelab:*"] } ]}Without an explicit accept rule, traffic is denied. The default policy that lets everyone reach everything is a single "*:*" -> "*:*" rule you can and should delete once you’ve written real rules.
Tags are assigned per device and let you write policy against device categories instead of specific users:
# Apply a tag at registration timetailscale up --advertise-tags=tag:server
# Or set it later in the admin consoleThat way when you rotate the human running a server, you don’t have to rewrite every ACL that mentioned them by name.
Test before you lock yourself out. The admin console has a policy tester built in. Use it before you save a change. Getting locked out of your own tailnet is fixable but embarrassing, and it’s always at the worst possible time.
Subnet Routers: Bring Your Whole LAN Into the Mesh
Not every device can run Tailscale, printers, NAS boxes, smart switches, whatever legacy appliance is still running in a closet. Subnet routing solves this by letting one Tailscale device advertise routes for the rest of the LAN behind it.
On the device that will act as the router:
# Enable IP forwarding firstecho 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.confsudo sysctl -p
# Advertise your home LAN (multiple subnets, comma-separated, also works)sudo tailscale up --advertise-routes=192.168.1.0/24Then approve the route in the admin console: click the device, click the three dots, approve the subnet. This is a deliberate speed bump, you don’t want an accidental route advertisement quietly opening a hole into your network.
On client devices, accept routes:
tailscale up --accept-routesNow your laptop, wherever it physically is, can reach 192.168.1.x devices that have never heard of Tailscale. Your old NAS, your printer, your router’s admin page, all reachable as if you were sitting on the couch at home.
You can also target subnet CIDRs directly in ACL rules if you want to control who gets that access at the policy level, not just at the router.
Exit Nodes: Route All Your Traffic Through Home (Or a VPS)
Exit nodes turn a Tailscale device into a full VPN egress point. Everything else you do on the internet routes through that device and appears to come from its public IP.
On the device that will be the exit node:
sudo tailscale up --advertise-exit-nodeApprove it in the admin console, same as a subnet route.
On the client, you can point at it two ways. The classic form re-runs up with the flag:
tailscale up --exit-node=100.x.x.x --exit-node-allow-lan-accessBut if Tailscale is already running, tailscale set changes just the exit node without touching anything else up configured, faster and less likely to reset flags you forgot you set:
tailscale set --exit-node=100.x.x.x
# And to turn it back off:tailscale set --exit-node=Good use cases: coffee shop Wi-Fi you don’t trust, dodging regional content locks, presenting your home IP to a service while traveling. One real limitation: your home upload speed becomes everyone’s bottleneck. If your uplink is 50Mbps, that’s the ceiling for anything routed through it.
Tailscale SSH: SSH Without Opening Port 22
Tailscale SSH lets devices on your tailnet SSH into each other over the Tailscale connection, no exposed port 22, no separate key management for machines already on the mesh.
# Enable on the servertailscale up --ssh
# Connect from any device on the tailnet# or with MagicDNSssh user@myserverACLs control who’s allowed in, and you can require re-authentication for sensitive boxes:
{ "ssh": [ { "action": "accept", "src": ["group:admin"], "dst": ["tag:server"], "users": ["root", "ubuntu"] }, { "action": "check", "src": ["*"], "dst": ["tag:server"], "users": ["*"] } ]}The "check" action forces identity re-verification before the connection completes. Reserve it for anything you’d rather not have someone’s stale browser session walk into.
MagicDNS: Stop Memorizing 100.x.x.x Addresses
MagicDNS gives every device in your tailnet a resolvable hostname (devicename.tailnetname.ts.net). Turn it on in the admin console under DNS, and ping mydesktop, ssh homeserver, and curl nextcloud.tailnetname.ts.net all just work.
Split DNS lets you send specific domains to your own resolver while everything else goes wherever you’d normally send it:
{ "dnsConfig": { "nameservers": ["1.1.1.1"], "routes": { "internal.example.com": ["100.x.x.10"], "home.arpa": ["192.168.1.1"] } }}Queries for internal.example.com and home.arpa go to your internal DNS server over the tailnet. Everything else goes to Cloudflare. No split-brain DNS setup required.
Taildrive and Taildrop: File Sharing You Didn’t Have to Build
Need to get a file from one machine to another without spinning up Syncthing or emailing yourself a zip? Taildrop is peer-to-peer file transfer, built into the client:
tailscale file cp ~/document.pdf alice-laptop: # send (note the trailing colon)tailscale file get ~/Downloads/ # pull anything queued for youThe file goes straight to the recipient over the mesh. No intermediate server, no size limits you didn’t set yourself.
Taildrive is the newer sibling: it lets you mount a directory on one device as a network share visible from another.
# On the machine sharing filestailscale drive share Documents
# On the machine that wants accesstailscale drive list# then browse it or mount it under /mnt/tailscale/...It’s a lightweight replacement for NFS or SMB when the only thing you actually need is “let this other device see that folder,” without opening a real file-sharing protocol to the whole network.
Headscale: Own Your Control Plane
Headscale is an open source, self-hosted reimplementation of the Tailscale coordination server. Your devices register with your instance instead of Tailscale’s. The data plane is still plain WireGuard either way, only the control plane moves.
What you get: no external dependency, no data about your topology leaving your infrastructure, and it keeps working even if Tailscale the company has a bad day or changes its pricing.
What you trade: some client features (Funnel/Serve, a few MagicDNS conveniences, the polished web console), plus the operational overhead of running and patching another service yourself.
Running it with Docker, pinned to v0.28.0 for everything below:
services: headscale: image: headscale/headscale:0.28.0 container_name: headscale volumes: - ./config:/etc/headscale - ./data:/var/lib/headscale ports: - "8080:8080" - "9090:9090" # metrics command: headscale serve restart: unless-stoppedserver_url: https://headscale.yourdomain.comlisten_addr: 0.0.0.0:8080metrics_listen_addr: 0.0.0.0:9090
db_type: sqlite3db_path: /var/lib/headscale/db.sqlite
dns_config: nameservers: - 1.1.1.1 magic_dns: true base_domain: headscale.internalPut this behind a reverse proxy (Traefik, Caddy, nginx) with TLS. Headscale requires HTTPS for client registration to work at all.
Create a user and a pre-auth key for automated device enrollment:
docker exec headscale headscale users create myuserdocker exec headscale headscale preauthkeys create --user myuserTwo commands changed shape as Headscale matured toward 0.28, and it’s easy to find stale docs mixing old and new syntax. Approving a subnet route now goes through nodes approve-routes (the older routes enable -r ID was removed in 0.26):
docker exec headscale headscale nodes list-routesdocker exec headscale headscale nodes approve-routes --identifier NODE_ID --routes 192.168.1.0/24Registering a node that connected without a pre-auth key now goes through the auth command group instead of the old nodes register:
docker exec headscale headscale nodes list # find the pending node's IDdocker exec headscale headscale auth register --user myuser --key NODE_KEYThose are two different operations (approving a route vs. approving a new device), not two ways of doing the same thing, so you’ll likely use both.
On the client, point it at your instance:
tailscale up --login-server=https://headscale.yourdomain.com \ --authkey=YOUR_PREAUTHKEY \ --hostname=myserverSkip the auth key and tailscale up prints a registration URL instead, which you approve with the auth register command above.
Tailscale vs Headscale: When to Use Which
| Feature | Tailscale (Cloud) | Headscale (Self-hosted) |
|---|---|---|
| Setup complexity | Minimal | Moderate |
| Control plane ownership | Tailscale Inc. | You |
| Admin UI | Polished web console | CLI only (unofficial UIs exist) |
| Tailscale SSH ACLs | Full support | Limited |
| Funnel/Serve | Yes | No |
| Taildrop / Taildrive | Yes | Partial, client-dependent |
| Mobile clients | Yes | Yes (same clients) |
| Free tier | Up to 100 devices | Unlimited devices |
| Paid team tier | Per-user monthly billing, adds SSO and centralized admin | N/A, you run the billing (i.e., there is none) |
| Uptime dependency | Tailscale’s servers | Your server |
For personal use and small home labs, cloud Tailscale is the right call. The free tier covers most single-user setups, the data plane is the same WireGuard either way, and the admin console saves you real time. If you outgrow the free tier and need team seats, Tailscale’s paid plan adds SSO and centralized device management for a per-user monthly fee, check the current pricing page before you budget, since it’s moved more than once and this article will age faster than the software will.
For teams with compliance requirements, a hard no on third-party control planes, or a genuine need for air-gapped networking, Headscale earns its operational overhead. And because Headscale speaks the same protocol, you can point your existing Tailscale clients at it and migrate without touching a single device’s install.