On this page
If several Tailscale devices sit behind the same router and only one of them gets a direct connection, give each device its own tailscaled UDP port instead of the default 41641. My office router kept port 41641 for exactly one machine. The other four fell back to a relay, including the node that carries all my traffic home, whose path to my NAS was relayed for most of a month. Four new port numbers fixed it in an afternoon.
A month of relay alerts
My lab lives in two places. The Proxmox cluster (four nodes, pvelab01 to pvelab04) and my laptop sit at my office, behind a router that belongs to the building’s IT team, not me. My NAS sits at home. Tailscale joins the two sites, and one node, pvelab04, acts as the subnet router that every NAS-bound flow goes through: file shares, the nightly offsite backup sync, restores.
I’d built a small monitor for exactly this situation. A script on pvelab04 asks Tailscale every minute whether the path to the NAS is direct and publishes the answer to Prometheus as a 1 or a 0. A Grafana alert fires if it sits at 0 for 30 minutes. And I’d been getting its emails.
When I finally sat down with the history, this is what Prometheus had recorded for that path, one sample per day across the 29 days it held:
| Share of each day the office-to-NAS path was direct | Days |
|---|---|
| 0% (relayed all day) | 27 |
| About 21% | 1 |
| About 61% | 1 |
So for almost the entire retention window, every byte between my cluster and my NAS went through a relay. Nothing was broken, which is why I’d let the emails pile up. That’s the trap with relays.
What DERP is, and why it’s a fallback
Tailscale’s preferred path is a direct, encrypted WireGuard tunnel between two devices. To build one across two routers, each side has to learn the public address and port the other can be reached on, then both send packets at the same moment so each router lets the other’s packets in. Tailscale’s own write-up, How NAT traversal works, walks through it, and it’s honest about the limit: if one side can’t predict which outside port to send to, the direct path can’t be built.
When that happens, Tailscale uses a DERP relay server. The docs describe it as a last resort for when other connection types aren’t possible. Your traffic is still encrypted end to end; the relay just forwards packets it can’t read. It works, which is the problem. Everything keeps running, only slower and with more latency, and nothing tells you unless you go looking.
The pattern: one winner, four losers
The clue was in who was direct. When I compared the office devices, pvelab01 was reaching the outside world from the office’s public address on port 41641, and its path to the NAS was direct. Every other office device was showing up on some other, seemingly random port, and every one of them was relayed. The home side was fine: the NAS was reachable on its usual 41641 the whole time.
Tailscale listens on UDP port 41641 by default, and every one of my office devices was using that default. When a packet leaves through the office router, the router rewrites its source to the office’s public address and picks an outside port. In my office, the router kept a device’s own port number on the outside, but it can only hand any one outside port to one device at a time. pvelab01 had claimed 41641 first. Everyone else asking for 41641 got something else, and from home those other ports never worked for a direct connection.
I can’t see inside a router I don’t manage, so I’m describing what it did, not how it’s built. But the behaviour was consistent, and it pointed straight at the fix: stop making five devices fight over one port.
Tailscale’s firewall page notes that allowing inbound traffic to the UDP port one device listens on can help it go direct. That needs control of a router. I don’t have it at the office, and at home I couldn’t set up a forward for the NAS because it doesn’t show up in my ISP router’s device list. Unique ports needed nothing from either router.
The fix: five devices, five ports
The node names (pvelab01 to pvelab04), the port numbers 41642 to 41645 and the peer name nas are mine. Pick any free UDP ports you like, one per device behind the same router, and use your own device names. 203.0.113.10 below stands in for an office’s public address and 198.51.100.20 for a home one. If a value looks specific to one machine, it’s a placeholder to change, not a literal to copy.
I left pvelab01 on 41641, since it already held it, and gave everyone else the next number up:
| Device | Port |
|---|---|
pvelab01 |
41641 (unchanged) |
pvelab02 |
41642 |
pvelab03 |
41643 |
pvelab04 (the subnet router) |
41644 |
| laptop | 41645 |
On a Debian or Ubuntu machine with Tailscale’s package installed, the port lives in /etc/default/tailscaled. The systemd service reads that file and starts tailscaled with --port=$PORT, which you can confirm on your own machine with systemctl cat tailscaled. Tailscale’s firewall page mentions the FLAGS variable in the same file as another place to set it; on my install, changing PORT is the direct route.
PORT="41642"
FLAGS=""
systemctl restart tailscaled
tailscale ping nas
Restarting tailscaled drops that device’s tunnel for a few seconds. If your SSH session reaches the device through that same tunnel, or through a subnet route it provides, you’ll cut yourself off mid-change. My laptop reaches the office LAN through pvelab04’s subnet route, so I restarted every node from pvelab01 over the office LAN instead of from the laptop.
For my laptop I wrote a small script that backs up the file, sets the port, restarts the service and pings the NAS. The first run printed unexpected state: NoState: the ping had raced tailscaled coming back up. The script now waits until tailscale status reports the backend as running before it pings. If you script this, add the same wait.
How I know it worked
tailscale ping is the quickest check. On a direct path it answers with an address and port; on a relayed one it answers via DERP(...) with the relay’s city code. Here’s pvelab01 today, with the addresses swapped for documentation ones:
$ tailscale ping nas
pong from nas (100.64.0.0) via 198.51.100.20:41641 in 48ms
That 100.64.0.0 is a stand-in for the NAS’s tailnet address, which will be somewhere in that range on your tailnet. From the NAS’s side, every node now shows up from the office’s public address on its own port (203.0.113.10:41642, :41643 and so on), all direct, and the laptop is direct too.
The metric agreed. The office-to-NAS path flipped from 0 to 1 at about 10:00 that morning and read 1 in every 15-minute sample through the rest of the day. The relay alert has gone quiet.
What I haven’t done yet is compare the nightly backup and sync durations before and after, so I’m not going to claim a speed-up here. The point of this fix was getting off a detour I didn’t choose. Whether that detour was costing me minutes or hours each night is a separate measurement, and it’s one I’d rather make than guess.
The rule I keep now
Every Tailscale device behind the same router gets its own port. It’s written into my lab notes next to the port table above, so the next device I add at the office gets 41646 instead of quietly taking the default.
And if the relay alert ever fires again, the first thing I’ll check is whether two devices behind one router have ended up sharing a port. A direct-or-relayed metric with an alert on it is what turned this from “everything works, just a bit slow” into something I could actually see. If you run Tailscale between sites, it’s worth having.
Related posts:
- Tailscale Subnet Router: Remote Homelab Access Without Port Forwarding — the subnet router whose path to the NAS was stuck on a relay
- WireGuard vs Tailscale vs OpenVPN: Which VPN for Your Homelab? — where Tailscale’s NAT traversal and DERP fallback fit among the options
- Why a 208 KB Buffer Throttled My Tailscale Backups — a different slowdown on the same office-to-home path
- The Packet Three Firewalls Ate: Debugging a Tailscale Subnet Route — another Tailscale problem across the same two sites, found one variable at a time
- Green Lights, No Packets: Common Homelab Network Issues — more ways a network keeps working while quietly doing the wrong thing
- What Is Prometheus? Homelab Metrics and Alerts Explained — the metrics side of the direct-or-relayed alert
- Tailscale Exit Node: Route All Traffic Through Your Homelab — the other big Tailscale feature in a homelab
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.