For years my homelab had a small collection of port forwards on the router, and every one of them was a tiny apology I'd made to the internet. 51820 for WireGuard. A high port for a reverse proxy. SSH on something non-obvious because I'd read somewhere that moving it off 22 made the log noise go away, which it does, right up until it doesn't. Each one was deliberate. Each one was also a hole I had to keep thinking about, and the thinking is the expensive part.
The thing that finally pushed me over was nothing dramatic. I was rebuilding the router on a new bit of hardware, and I sat there with the old config open, copying forwarding rules across one at a time, and realised I couldn't confidently say what half of them were still for. That is the moment you should stop. If you can't justify a rule, the rule is a liability, not a feature.
So I did the thing I'd been putting off and moved the whole lot onto Tailscale.
what i actually wanted
The goal was simple to state. I wanted to reach everything in the house from anywhere, I wanted nothing inbound open on the router at all, and I wanted to stop maintaining a WireGuard config by hand. I'd run a hand-rolled WireGuard mesh before. It works beautifully until you have eight devices and you're editing AllowedIPs by hand and wondering why the new phone can see the NAS but not the printer.
Tailscale is WireGuard underneath, which is the bit that made me trust it. It's not a new tunnel protocol I have to take on faith. It's the same data plane I already liked, with the key distribution and NAT traversal problem solved for me by a coordination server. That division is the whole pitch: the control plane is theirs, the data plane is still just WireGuard going directly between my machines where it can.
the setup, which took an evening
Installing the client is genuinely a one-liner per machine, so I won't dwell on it. The two things worth actually thinking about were the subnet router and DNS.
I didn't want to install Tailscale on every last device. The smart bulbs and the printer are never getting a client, and I'm not putting one on the router itself if I can help it. So one always-on box advertises the home subnet:
tailscale up --advertise-routes=192.168.10.0/24 --accept-routes
Then approve the route in the admin console, and now every Tailscale device can reach the dumb devices on that subnet as if it were local. One box does the bridging, everything else rides along.
DNS was the part that made it feel finished. With MagicDNS turned on, every machine gets a stable name on the tailnet, so I stopped caring about IP addresses entirely. ssh nas works from my laptop in a coffee shop the same way it works from the sofa. No split-horizon DNS to maintain, no remembering whether I'm "inside" or "outside" the network, because that distinction has basically stopped existing.
locking it down
The default in Tailscale is that everything can talk to everything, which is fine for getting started and wrong for living with. The ACL file fixes that. Mine is short and boring, which is exactly what you want from an ACL. The principle is that my laptop and phone can reach the lab, the lab can't reach back out to my personal devices uninvited, and a couple of specific services are reachable by tag rather than by which human owns the machine.
{
"tagOwners": {
"tag:lab": ["jmylchreest@github"]
},
"acls": [
{ "action": "accept", "src": ["jmylchreest@github"], "dst": ["tag:lab:*"] },
{ "action": "accept", "src": ["tag:lab"], "dst": ["tag:lab:*"] }
]
}
Tagging machines instead of tying them to my user account matters more than it looks. A tagged node's key doesn't expire the way a user node's does, so the always-on subnet router doesn't silently drop off the tailnet in ninety days because I forgot to re-authenticate it. That is exactly the sort of thing that bites you at the worst possible moment, three months later, when you've forgotten it was ever a concern.
the part i didn't expect to enjoy
With all of that in place, I went back to the router and deleted every inbound forward. All of them. The WAN side now answers nothing. From the outside, the house looks like a closed door, because it is one. Anything I want to reach, I reach over the tailnet, and the tailnet establishes itself outbound, so there's nothing to find on a port scan and nothing to leave open by accident.
There's a subtler win too. I used to dread reaching something from a hotel or a client site with aggressive egress filtering, because my hand-rolled WireGuard on UDP would sometimes just not get out. Tailscale's NAT traversal tries direct first and falls back through a relay when it has to, so it gets a connection from places I'd previously given up on. It's slower over a relay, obviously, but slower and working beats fast and blocked.
I'll be honest about the trade. I've added a dependency on a coordination server I don't run. If Tailscale the company goes away, I have a migration to do, and that's a real consideration for something this central. But the keys are WireGuard keys, the data plane is WireGuard, and there's an open-source control server (Headscale) I can fall back to if I ever need to. That escape hatch is the reason I was comfortable committing.
A fortnight in, the homelab is easier to reason about than it's been in years. There are no forwards to audit, no config to hand-edit, and the mental tax of "what can the internet currently reach" has gone to zero. That last bit is the real prize. I didn't make the lab more capable. I made it something I no longer have to worry about, and on a Sunday evening that's worth more.