WireGuard vs Tailscale in my homelab after a year
I spent 2023 running a naked WireGuard mesh across my homelab and two cloud VMs. Early in 2024 I added Tailscale as an overlay on top of some of those same hosts for a narrow use case. A year later I have opinions about both.
What I run
- Three homelab Proxmox nodes (NUCs) on a flat
/24at home. - A Hetzner CX22 for public ingress and a cheap shared mail relay.
- Two DigitalOcean droplets that host monitoring.
- My laptop and phone, which roam.
WireGuard peers it all together with a static key distribution. Tailscale runs on a subset of machines: the two cloud VMs, the laptop, the phone, and one NUC that acts as an exit node.
The WireGuard setup
A single peer config on the homelab gateway looks like:
[Interface]
PrivateKey = <redacted>
Address = 10.77.0.1/32
ListenPort = 51820
Table = off
PostUp = ip route add 10.77.0.0/24 dev %i; ip rule add from 10.77.0.0/24 table 200
PostDown = ip rule del from 10.77.0.0/24 table 200
[Peer]
# hetzner-edge
PublicKey = <redacted>
AllowedIPs = 10.77.0.10/32
Endpoint = edge.example.net:51820
PersistentKeepalive = 25
The policy routing bits matter because I want only certain flows to use the tunnel. Table = off tells wg-quick not to install a catchall.
Key distribution lives in a Nix flake that templates out each peer. I rotate keys once a year, which is less frequently than security nerds would like but more often than most homelab owners bother with.
Where WireGuard wins
- Speed. On my 1 Gb/s link I can saturate it. Tailscale’s userspace has gotten better but is still not as fast as kernel WireGuard on a cheap box.
- Deterministic. Every peer knows every peer. There is no coordination server. Nothing in the middle to have an opinion about my traffic.
- Boring. Once set up, it runs. I do not think about it.
Where Tailscale wins
NAT traversal from my phone. My home is behind carrier NAT for IPv4 from the cellular provider. Setting up inbound WireGuard from my phone required IPv6 end to end or a relay. Tailscale does this out of the box with DERP.
ACLs. Tailscale’s ACL file is pleasant:
{ "acls": [ {"action": "accept", "src": ["tag:laptop"], "dst": ["tag:home:*"]}, {"action": "accept", "src": ["tag:home"], "dst": ["tag:cloud:3000,9090"]} ], "tagOwners": {"tag:home": ["autogroup:admin"], "tag:cloud": ["autogroup:admin"]} }I can describe “laptop can reach any home service, home machines can reach the two observability ports on cloud” in five lines. Doing that in WireGuard means iptables on every host and a spreadsheet.
MagicDNS. This is the one feature I would pay for on its own. Machines can refer to each other by short name without me maintaining a zone. I have a post about how I ripped out my dnsmasq tricks once MagicDNS came online see my post on Pi-hole forgetting its DNS override.
Where they both have sharp edges
Tailscale plus a separate VPN on the same laptop is messy. I used to run a work VPN and Tailscale and routinely had routing surprises. Tailscale’s
--accept-routes=falseand sane route priorities fixed most of it.WireGuard’s handshake failures are silent at the application level.
wgshows you the problem but nothing wakes you up. I have a small bpftrace watcher for this:bpftrace -e 'kprobe:wg_handshake_receive { @[pid,comm] = count(); } interval:s:60 { print(@); clear(@); }'Not exactly production-grade, but if handshakes start going up in a weird pattern I get told.
Tailscale’s exit node feature is handy but surprising: when I set my laptop to use my home NUC as exit node, my upstream DNS changed. Tailscale by default sets the exit node’s DNS. This is often what you want, but the first time it happens you wonder why your work resolvers stopped answering.
What I would change
If I were starting fresh I would still run both, but I would put Tailscale on more things. The operational cost of running a coordination server in my head is real. I kept the naked WireGuard on the always-on servers because I like the minimal-dependency story for my cloud ingress: if Tailscale had an outage, I still want to reach my edge box.
Concretely my plan for the next year:
- WireGuard for server-to-server infrastructure, managed from the Nix flake.
- Tailscale for human-to-server: laptop, phone, my partner’s laptop when she asks to access the photos backup.
- Keep the exit node setup but document the DNS surprise in my homelab notes.
Reflection
They are not the same product. WireGuard is a tunnel. Tailscale is an overlay network with a control plane. Confusing the two is how you end up with posts titled “Why my Tailscale is slow” that turn out to be about not reading the docs.
If your needs are simple (two Linux servers, predictable topology), WireGuard alone is gold. If you have humans with phones and laptops that roam, Tailscale pays for its complexity in saved yak shaves. Running both is fine as long as you keep the routing tables separate in your head.