The short answer
Pick plain WireGuard if you have a handful of devices that mainly need to reach one server, and you are happy to edit a configuration file for every device you add or remove. Pick Headscale if you want what Tailscale does, devices that find each other directly, names, access rules and exit nodes, with the list of devices on your own server and nothing to pay. Pick Tailscale's hosted service if you would rather not run the control server at all, accept that it knows your devices, and pay for it once you use it for work: the free plan is for non-commercial use only.
What we tested
We used one Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13 and the base from the Podman guide. On the same server we made three clients, each in a network namespace of its own: one with a public address, like a server at another provider, one behind a home-router style NAT that blocks incoming connections, and a third to add and remove. The server itself was a member of the network too.
- WireGuard: we followed our WireGuard guide with the server as the hub and two clients as the "phone" and "laptop", then added access rules with ufw.
- Headscale: we followed our Headscale guide, joined the server and two clients, one through the login link and one with a key, and added a policy file for access rules.
- Tailscale's hosted service was not tested. Signing up needs an account with Google, Microsoft, GitHub, Apple or another identity provider, and Tailscale has no sign-up by email address, so we could not create one without a person. Everything we say about it comes from Tailscale's own documentation and pricing page, checked on 14 October 2026. It uses the same Tailscale apps as Headscale, so what we saw on the device side applies to it too.
On both, we did the same job: connected the devices, measured speed with iperf3 and latency with ping against no tunnel at all, added and removed a device, limited one client to one port on the server, routed traffic through an exit node, and stopped the control server to see what kept working.
All clients ran on the same 2-vCPU server as the hub, so encryption on both ends of every tunnel shared the same two cores. The speeds below compare the three ways with each other on equal terms; between two real machines over the internet, your connection is usually the limit instead.
The numbers
| No tunnel | WireGuard | Headscale and Tailscale's app | |
|---|---|---|---|
| Client to server, speed | 18.3 Gbit/s | 1.15 Gbit/s | 1.34 Gbit/s |
| Client to server, latency | 0.12 ms | 0.55 ms | 1.2 ms |
| Client to client, speed | 14.8 Gbit/s | 683 Mbit/s, through the server | 1.20 Gbit/s direct, through NAT |
| Client to client, latency | 0.17 ms | 0.83 ms, through the server | 1.2 ms direct |
| Client to client, relayed | 52 Mbit/s, 3.3 ms | ||
| Time for the guide's commands | about 20 seconds | about 45 seconds, plus a few seconds per device | |
| Memory on the server | none to speak of: it is part of the Linux kernel | Headscale 16 to 20 MB, and about 50 MB for the Tailscale app on each device | |
| Disk | under 1 MB of packages | Headscale's image 78 MB, its data under 1 MB, and 75 MB for the Tailscale package on each Linux device | |
| Containers | none | 1 |
Times are how long the commands took to run, not the time you need to read the guide and type them. Headscale also needs a name with a certificate before devices can join; Caddy got it within a minute.
Setup
WireGuard is a few packages, a key pair per device and one configuration file on the server with a [Peer] block per device. Each device gets a file of its own, or a QR code for a phone. It is the quickest to get running, and there is nothing to keep up to date beyond Debian's packages. Our guide sets the server up as a VPN to the internet; to let the devices reach each other through the server, we also needed ufw route allow in on wg0 out on wg0, which the guide does not have.
Headscale is one container behind Caddy, with an A and AAAA record and the STUN port open. Each device runs the Tailscale app, pointed at your server. Phones and laptops log in through a link, and you confirm them with one command on the server; servers join with a key that works once. Every step in our guide worked as written with Headscale 0.29.5, which had just replaced 0.29.4.
Tailscale's hosted service, according to its documentation, needs no server: you sign in with an identity provider, install the app on each device and log in. It is the least work of the three by a wide margin.
Adding and removing devices
WireGuard: adding a device is a new key pair, a new [Peer] block and, in our guide, a line for its IPv6 address, then restarting WireGuard. The restart dropped every connected device for about 15 seconds while their tunnels came back. wg set can add a peer without a restart, but then the file and the running interface can drift apart. Removing a device is the reverse. If you delete its lines from wg0.conf before restarting, its IPv6 proxy entry stays behind, because WireGuard reads the new file when it stops; remove it with ip -6 neigh del proxy ADDRESS dev eth0.
Headscale: a new device with a key was online 8.5 seconds after we created the key. headscale nodes delete cut a device off at once, and it vanished from the other devices' lists. Nothing restarts and nobody else notices.
Tailscale: the same, from the admin console, with device approval and invites on top.
Direct, through NAT, or relayed
WireGuard connects each device to the address in its Endpoint, the server. A device behind NAT reaches the server but not another device behind NAT, so in practice everything goes through the hub. That suits a team reaching servers, and doubles the trip for device-to-device traffic: our client-to-client speed through the server was about half what Tailscale's app managed directly.
Headscale with the Tailscale app made a direct connection from the client behind NAT to the others: tailscale ping answered via the other client's address and port, not via DERP. When we blocked all UDP from that client, it fell back to Headscale's own relay, via DERP(headscale), at 52 Mbit/s and 3.3 ms instead of 1.2 Gbit/s and 1.2 ms. Relayed traffic is still encrypted end to end, but the relay is a bottleneck: plan for it if devices sit behind firewalls that block UDP.
Tailscale's hosted service uses the same apps, so it connects directly the same way, and relays through Tailscale's DERP servers around the world when it cannot. Its documentation also describes peer relays, devices of your own that relay for others.
Names
WireGuard has no names. You use addresses, put names in each device's hosts file, or run your own DNS such as Unbound.
Headscale gave every device a name with MagicDNS, such as c2.tail.example.com, and the clients resolved each other's names. The Tailscale app on Linux rewrites /etc/resolv.conf to use 100.100.100.100 while connected, including on the server, and puts it back with tailscale down.
Tailscale does the same, with names under a ts.net domain of its choosing.
Access rules
We limited one client to TCP port 8080 on the server and nothing else.
WireGuard: the firewall on the server is the access control. ufw allow in on wg0 from 10.8.0.2 to any port 8080 proto tcp let that client reach port 8080 and no other; device-to-device traffic is controlled by ufw route rules. It works, but the rules are written per address, and nothing ties an address to a person.
Headscale: our guide does not set up a policy, and without one every device reaches every other device and every port. We added a policy file, mounted it into the container and named it in config.yaml under policy.path, with two grants: anna@ reaches everything, and bob@ reaches tcp:8080 on the server. Bob's client got port 8080 and nothing else, and could not ping Anna's devices, while Anna's could ping his. Rules follow users and tags, not addresses.
One thing to know: the Tailscale app adds its own firewall chain in front of ufw, and lets everything on tailscale0 through. Your ufw rules do not apply to traffic over the tailnet, so the policy is what protects the server.
Tailscale uses the same policy language, edited in the admin console, with tests and a history of changes. The free Personal plan allows three groups, Standard 10 and Premium 300.
Exit nodes and subnets
WireGuard: our guide is an exit node already: devices with AllowedIPs = 0.0.0.0/0, ::/0 browse through the server, and each gets its own public IPv6 address. Reaching a network behind a device is a matter of adding it to AllowedIPs on both sides and routing it.
Headscale: we made the server an exit node with tailscale set --advertise-exit-node, and a client a router for a network behind it with --advertise-routes. Both needed approving with headscale nodes approve-routes, after which the client browsed with the server's IPv4 address and IPv6 address, and the server reached the network behind the client. Neither is in our guide. Unlike our WireGuard guide, devices share the server's IPv6 address rather than getting one each.
Tailscale: the same, approved in the admin console.
Keys and expiry
WireGuard keys never expire. Rotating one means a new key pair and editing both the device's file and the server's, and a device you forget to remove keeps working.
Headscale keys do not expire by default either (node.expiry: 0); you can set a default, or expire one device with headscale nodes expire, which logged our client out at once. Logging back in with tailscale up --force-reauth gave it a new key and kept its name and address.
Tailscale expires device keys after 180 days by default, adjustable from 1 to 180 days, and can switch expiry off per device, according to its documentation.
What the control server knows
WireGuard has no control server. Your server knows each device's public key, tunnel address, and the address it last connected from.
Headscale stores, for every device: its user, name, keys, tunnel addresses, the public and local addresses it can be reached at, when it was last seen, and what it reports about itself: operating system and kernel version, app version, distribution, whether UDP works, and which relay is nearest. It also logged the real address each device connected from. All of it is on your server, in one SQLite file.
The Tailscale app itself sends logs to log.tailscale.com by default. Headscale tells the apps to stop, but our clients had already sent about 7.5 kB of start-up logs before they connected to it. To send nothing, start the app with --no-logs-no-support, or on Linux put TS_NO_LOGS_NO_SUPPORT=true in /etc/default/tailscaled.
Tailscale's hosted service sees the same device information, and who signed in through which identity provider. The apps also stream logs of their operation to Tailscale, including when connections open and close, unless you opt out per device; Tailscale says it may then not be able to give support. Network flow logs, which record which devices talk to which, are optional and on the Premium plan.
When the control server is down
WireGuard: the hub is the network. If the server is down, so is everything that goes through it.
Headscale: we stopped it. Direct connections kept working for as long as we watched, and names kept resolving. The relayed connection stopped at once, because Headscale's relay runs in the same container. A client we restarted while Headscale was down came back logged out and could not reach anyone, and new devices could not join. When Headscale started again, the restarted client was back within 4 seconds and the relayed one within 6.
Tailscale's hosted service has the same design: its documentation and engineering blog say existing connections keep working, while adding devices and changing rules wait. Its relays are separate servers in many places, so they do not go down with the control server.
Apps
WireGuard has official apps for Android, iOS, macOS, Windows and Linux, and is built into many routers. It is the most widely supported of the three.
Headscale works with Tailscale's official apps for Linux, Windows, macOS, Android, iOS and iPadOS; you point each one at your server, which takes a few taps in a debug or account menu. Headscale's documentation lists exit nodes, subnet routers, MagicDNS, Taildrop, Taildrive and Tailscale SSH as supported, and Funnel, Serve and network flow logs as not done. It has no web interface of its own: you manage it with commands on the server.
Tailscale has the same apps, and a web admin console to manage devices, users and rules.
Updates and backups
WireGuard is part of the Linux kernel, and its tools come from Debian, so unattended-upgrades keeps it up to date. Back up /etc/wireguard, which holds every key.
Headscale follows the 0.29 tag, which Podman updates daily. Going to 0.30 and later is by hand, one minor version at a time, after reading the release notes. Its backup is one volume and the configuration file. The Tailscale apps update separately; Headscale 0.29 rejects apps older than 1.80.
Tailscale's hosted service is updated by Tailscale; you only update the apps.
Licence and cost
We checked the projects' licences and Tailscale's pricing page on 14 October 2026.
- WireGuard is GPLv2 in the Linux kernel and in
wireguard-tools. Its apps are MIT, and Apache 2.0 on Android. It costs nothing. - Headscale is BSD 3-clause. It is a community project, separate from Tailscale Inc. It costs nothing.
- Tailscale's client,
tailscaledand thetailscalecommand, is BSD 3-clause. The control server and the graphical apps for macOS, Windows and iOS are not open source. Its plans:- Personal, free, for up to 6 users and non-commercial use only.
- Standard, $8 per user a month.
- Premium, $18 per user a month, with network flow logs, log streaming and more groups.
- Enterprise, priced on request.
Business customers get a 14-day trial. A team using Tailscale for work needs Standard or above, so a team of ten pays $80 a month.
Which one to pick
- A few people reaching one or two servers? Plain WireGuard. It is the fastest per connection, has nothing to run beyond the kernel, nothing to log in to and nothing that expires. Its weaknesses: every change is a file edit and, in our guide, a restart that drops everyone for a few seconds; devices only reach each other through the server; there are no names; and access rules are firewall rules by address.
- A team with laptops, phones and servers in many places, and you want to own the control server? Headscale. You get direct connections through NAT, names, access rules by user, exit nodes, and nothing to pay, with your device list on your own server. Its weaknesses: you run and update one more service, there is no web interface, access rules need a policy file our guide does not set up, its relay goes down with it, the Tailscale app bypasses ufw, and the apps send start-up logs to Tailscale unless you switch that off.
- The same, but you would rather not run anything? Tailscale's hosted service. It is the least work and has the most polish, with an admin console, relays around the world and support. Its weaknesses: Tailscale sees every device, the free plan is not for work use, and a team pays $8 per user a month or more.
WireGuard and Headscale both run comfortably on a VC-P Alloy, using almost no memory between them, so the same server can run your other apps. Headscale needs the base from the Podman guide with Caddy; WireGuard needs only Debian.