For years my homelab was held together by port forwarding and hope. A forward here to reach the NAS, a forward there for a self-hosted thing, a dynamic DNS hostname that updated when the ISP felt like changing my address, and a constant low-grade worry that I had exposed something I had forgotten about. Over the holidays I tore the lot out and replaced it with Tailscale, and I am slightly annoyed at how long I waited.
The point of this post is the why, not just the how. Port forwarding is the wrong model for reaching your own machines, and it took me far too long to admit it.
The problem with the old way
Every port forward is a hole in the firewall that the entire internet can knock on. You are relying on whatever is behind that port being perfectly secure forever, which is a bet you lose eventually. I had a service exposed for remote access that turned out to have an authentication bypass disclosed months after I forwarded it, and I only found out by reading a security advisory, not because anything alerted me. That is the model: you open a door to the world and then spend the rest of the service's life hoping nobody walks through it.
The traditional answer is a VPN, and I ran one for a while. A self-hosted WireGuard setup is genuinely good once it works. The trouble is the once-it-works part. You still need one inbound port open for the VPN itself, you still need a stable address or dynamic DNS, and the moment you want a third device or a phone that roams between networks, the manual key distribution and config editing becomes a chore you quietly stop doing.
What Tailscale actually is
Tailscale is WireGuard plus the part everyone hand-rolls badly: coordination. WireGuard does the encryption and the actual data path. Tailscale handles key exchange, NAT traversal, and knowing which device is where, through a control plane you authenticate against with an identity provider you already have.
The crucial detail, the one that makes the whole thing safe to use, is that the control plane never sees your traffic. It is a coordination server. It tells your devices about each other and helps them find a path through whatever NAT and firewall mess sits between them, and then they talk directly, encrypted end to end with keys the coordination server never holds. Your data does not flow through Tailscale's infrastructure. That separation between coordination and data is the architectural idea worth understanding, because it is what lets you trust a hosted control plane without handing it your traffic.
The NAT traversal is the part that feels like magic and is mostly clever engineering. Two machines, both behind NAT, with no inbound ports open on either, end up with a direct encrypted connection. It does this with the usual hole-punching tricks, and when the network is genuinely too hostile for a direct path, it falls back to relaying the encrypted traffic through a DERP relay. Slower, but it never just fails. In practice most of my links are direct, and I can see which are with tailscale status.
Setting it up
The setup was almost insultingly quick. On each machine:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
It prints a URL, you authenticate in a browser against your identity provider, and the device joins your network with a stable address in the 100.x range that never changes regardless of what network the device is physically on. My phone, my laptop, the NAS, the homelab boxes, all of them sit on one flat private network now, addressable by name, whether I am at home or on hotel wifi.
The two features I have come to lean on hardest:
- MagicDNS gives every machine a name, so I reach the NAS as
nasrather than memorising a 100.x address. Small, but it is the difference between using the thing and not. - ACLs let me say, in a small policy file, that my phone may reach the NAS on one port and nothing else may reach it at all. Default-deny, expressed centrally, applied everywhere. This is the bit port forwarding could never give me.
What I tore out
Here is the satisfying part. Every port forward on the router is gone. The dynamic DNS hostname is gone. The hand-maintained WireGuard configs are gone. My router's firewall now has zero inbound holes for any of this, and I can still reach every machine I own from anywhere, by name, over an encrypted direct link, with access controlled by a policy I can read in one screen.
I do not say this often about a hosted service that sits in the middle of my network, but the design earns the trust it asks for, because of where it sits and, more importantly, where it does not. It coordinates, it does not carry. If Tailscale vanished tomorrow my existing connections would keep working until their keys rotated, and the open-source Headscale control server exists as a way out if I ever want to own the coordination plane too. That escape hatch matters to me, and its existence is part of why I was willing to commit.
I should have done this two years ago. The hour it took to roll out is one of the better hours I have spent on the homelab, and the nicest measure of success is that I have not thought about port forwarding since.