In late 2021 I wrote Easily build your own network with Tailscale. The pitch was simple: drop OpenVPN, install a client, log in, get a stable 100.x address that works from anywhere.

That part still holds. What changed is everything around it. The mesh is no longer “a few laptops and one Proxmox box.” It is multiple sites, advertised home LAN routes, reverse proxies, camera streams, and NFS mounts that need to keep working when I am on the same Wi-Fi as the server and when I am halfway across the region.

This is the boring version of five years later: what I run, what bit me, and what I would set up the same way again.

Then vs now

In 2021 the mental model was one flat personal network over WireGuard. Devices talked to each other by Tailscale IP. Subnet routing was optional. ACLs were “maybe later.”

Today the shape looks more like this:

  • A few named nodes that stay up (homeland, berlin, and a smaller one I treat as secondary)
  • Proxmox on more than one site, with services split between LXC and VMs
  • Frigate (NVR) in more than one place, plus go2rtc for camera restreams
  • Nginx Proxy Manager in front of internal HTTP services
  • NFS for bulk media / recordings on at least one node
  • Phones and laptops as clients, not servers

I still install Tailscale the same way. The difference is I treat the mesh as infrastructure, not a convenience VPN I forget about until travel day.

The mesh is the backbone, not the LAN replacement

Early on I caught myself thinking “everything is on Tailscale, so the home LAN matters less.” That was wrong for anything that moves real traffic locally.

Cameras, NFS, and Frigate recordings want the fast path. When I am at home, the laptop should hit the NAS or the NVR on the wired or Wi-Fi network, not bounce through a relay or a longer mesh hop when ethernet is right there. Tailscale is how I reach the lab from outside and how sites talk to each other. It is a poor substitute for a clean layout on site.

So the rule I use now:

  • Same building, bulk or latency-sensitive traffic: prefer the home network
  • Cross-site or away from home: Tailscale
  • Browser entrypoints I want on a hostname: reverse proxy on the mesh (or on a node I can reach via the mesh)

Two kinds of “routing” (this confused me for a while)

People say Tailscale “picks the shortest path.” That is true for one thing and not for another.

Between Tailscale nodes (laptop to berlin, phone to a 100.x address), Tailscale does path selection. Direct WireGuard when it can, relay when it must. That is the “works from anywhere” story from the 2021 post. I still trust that part.

Subnet routes are different. When a node advertises a home LAN range, an admin approves it, and a client accepts subnet routes, that client installs those destinations so traffic to e.g. 192.168.x.y can go into Tailscale toward the advertising node. The client does not first ask “am I already on that LAN?” before installing the route. Windows, macOS, and phones accept subnet routes by default. Linux needs tailscale set --accept-routes.

So: good path selection between mesh peers is not the same feature as “send this private home IP out the ethernet port because I am sitting on that network.”

If you only run Tailscale node-to-node and never advertise home ranges, you can skip the next section.

Subnet routes earned their keep

Advertising a home subnet into the tailnet is the feature that turned “SSH to one box” into “the lab is reachable.” ACLs matter more once routes exist, because a tagged laptop can suddenly see a whole 192.168.x.0/24, not one host.

Things I route on purpose:

  • Camera / IoT segments I need from another site or from a jump host
  • Management networks for Proxmox or out-of-band bits I refuse to expose on the public internet
  • Selected LAN ranges so a phone on cellular can open an internal service without a separate VPN profile

Things I do not route blindly:

  • Guest Wi-Fi
  • Entire “all of home” if a narrower range is enough
  • Two sites both using the same private ranges (for example both on 192.168.1.0/24) with no plan

If two houses both love 192.168.1.0/24, pick one side to renumber or only advertise specific hosts. I would rather fix addressing once than debug “why does this IP mean different machines depending on which route wins.”

The accept-routes footgun (Linux, especially)

This is the bug that cost me real time, so it gets its own section. It is also easy to mis-describe.

The failure mode is not “Tailscale forgot how to pick a fast peer path.” It is: a Linux box lives on a home LAN, and it accepts that same range (or an overlapping one) as a subnet route from the tailnet. Traffic you expected to stay on the wire starts following the mesh instead.

On Linux, accepted subnet routes are not ordinary routes that politely lose to “I am directly on this network.” Tailscale puts them in a separate route table (table 52) and installs a rule so that table is checked before the normal one. That is intentional. Tailscale’s stated reason is safety on untrusted networks: if you are on coffee-shop Wi-Fi that also uses 192.168.1.0/24, and your office advertises 192.168.1.0/24, they do not want the coffee shop to win and quietly eat traffic meant for the office.

The other side of that tradeoff: always-on lab servers on a network you trust get the same priority rules. NPM backends, NFS mounts, and http://192.168.x.y admin UIs start following the mesh path. Sometimes that path works. Sometimes it blackholes. Sometimes it works until reboot and then fails in a new way. This class of breakage has been reported for years; it is not unique to my lab.

Symptoms I hit:

  • Reverse proxy targets on LAN IPs timed out only from certain nodes
  • NFS looked mounted then stalled
  • A service that answered on the host’s own subnet became unreachable after a Tailscale upgrade or reconnect

The fix that stuck for me is not “never use subnet routes.” It is:

  1. Be deliberate about which nodes accept routes. Many servers only need other nodes’ 100.x addresses and never need accept-routes at all.
  2. On nodes that must accept routes and sit on a real home LAN, prefer the local interface for the local ranges (a small ip rule / table pin so home traffic stays on the physical NIC).
  3. Point reverse proxies and mounts at addresses that stay stable under that policy (often the node’s Tailscale IP or a hostname that resolves the way you intend, not a LAN IP that flips behavior).

I keep a small local script so the home ranges stay glued to the physical NIC. Without that pin, accept-routes is a footgun on any always-on Linux lab node that also sees advertised home subnets.

If you only remember one thing from this post: do not casually enable accept-routes on a server that is already on the LAN you are advertising elsewhere, until you know whether those destinations still leave via the real network interface after Tailscale comes up.

ACLs stopped being optional

With three clients the default “nodes can talk” policy is fine. With subnet routes, tags, and a roommate or friend device once in a while, defaults get scary.

I moved to tags and destination-based rules. The exact policy file is personal, but the shape is:

  • Tags for server, client, camera-net (or similar)
  • Servers accept SSH/HTTPS/admin ports from clients
  • Clients do not need to accept inbound from the camera subnet
  • Subnet route exposure is granted to roles that need it, not to every device on the tailnet

The day you advertise a home subnet is the day a lost laptop is more than “someone can ping my VPS.” Write the ACL before you advertise the route, not after.

How I reach web UIs now

I used to port-forward or SSH-tunnel random admin ports. That aged badly.

Pattern that replaced most of it:

  1. Service listens on localhost or the LAN only on the lab node
  2. Nginx Proxy Manager (or any reverse proxy) sits on a node I can reach over Tailscale
  3. DNS name (sometimes a real domain, sometimes MagicDNS) points at the proxy’s Tailscale IP
  4. HTTPS terminates at the proxy; upstream is internal

Frigate is the obvious example in my setup: open the NVR on a hostname when I am on the tailnet, no inbound port on the home router. Same idea for other dashboards.

MagicDNS alone is enough for quick SSH (ssh berlin). For browser apps I still like a proper hostname and a proxy so cookies, TLS, and path routing behave like a normal service.

What runs where (high level)

Without turning this into an inventory dump:

  • Proxmox still anchors the heavy nodes. Some workloads fit LXC; others (Docker-heavy stacks, odd kernel needs) stay in VMs.
  • Frigate exists in more than one place for site-local cameras. Cross-site I restream when I need a view, rather than dragging every camera feed across the internet full time.
  • NFS holds bulky recordings/media on the node with the disks. Mounts are explicit; I do not pretend the mesh makes network storage as safe as local disk.
  • Secondary / smaller nodes are fine for lightweight services and as another hop on the tailnet. Not everything needs to be a full hypervisor.

Capacity planning is still capacity planning. Giving a VM “lots of RAM” with ballooning that collapses under load taught me to set floors I mean. That is not a Tailscale lesson, but it showed up on the same boxes the mesh ties together.

What I would repeat

  • Tailscale as the default remote access layer for a personal lab
  • Subnet routes for specific ranges, with ACLs written first
  • Reverse proxy on the mesh instead of opening internet ports for admin UIs
  • Boring hostnames and a small number of always-on nodes with clear roles
  • Documenting which machines accept routes, and any local-LAN preference pins, so future me does not “fix” things back into a broken state

What I would not repeat

  • Advertising the same private ranges from two sites without renumbering
  • Turning on accept-routes everywhere “for convenience”
  • Pointing critical mounts and proxy upstreams at LAN IPs on nodes where those destinations can flip onto the mesh
  • Assuming bulk camera or NFS traffic is fine over a relay path without checking
  • Skipping tags until “later” once friends or spare devices join the tailnet

Compared to the 2021 post

The old post was a migration story: OpenVPN was work, Tailscale was not. That recommendation stands for personal use.

Five years on, the product is still the easy part. Node-to-node mesh is still the easy part. The work moved to the edges: addressing when two sites collide, who accepts subnet routes, ACLs, and which services should never leave the building on a skinny path.

If you are still on the single-site, no-subnet-routes setup from my first post, you do not need most of this yet. When you add a second site or advertise a home LAN into the tailnet, come back and treat routes and ACLs as the real install step. And if something on the LAN “randomly” breaks only on Linux after accept-routes, check whether table 52 is winning before you blame the app.

I still would not go back to running my own VPN concentrator for this. I would start the ACL file earlier, and I would not confuse “mesh path selection” with “home LAN route priority.”