Problem
Every web service gets its own Tailscale sidecar, which means its own tailnet node and its own <service>.<tailnet>.ts.net name:
linux-server/: 18 sidecars (adguard, atvloadly, cockpit, filebrowser, forgejo, glances, homepage, nginx-proxy-manager, ntfy, openspeedtest, portainer, qbittorrent, speedtest-tracker, syncthing, tailscale-web, ups, uptime-kuma, watchtower)
linux-pi/: 4 more (adguard, cups, homepage, motioneye)
Each new service adds a node, which floods the tailnet's machine list. The flat namespace also doesn't scale to more servers: the name doesn't say which host a service runs on, and two servers can't both run a service with the same name.
Goal
One tailnet node per host, with services addressed under it:
https://<server>.<tailnet>.ts.net/<service>
Adding a server should add exactly one node. Adding a service should add none.
Things to settle before implementing
- Subpath support per app. Path routing (
tailscale serve --set-path /<service>) only works cleanly when the app can run under a base path or uses only relative URLs. Each service needs checking. Some have a base-path setting (forgejo ROOT_URL, filebrowser baseURL, cockpit UrlRoot). Others are known to be awkward behind a path prefix. Services that can't take a path need a fallback:
- a per-service HTTPS port on the host node (
https://<server>.<tailnet>.ts.net:<port>), or
- keep that one service as its own node.
- Tailscale Services. Evaluate Tailscale's newer "Services" feature (
svc:<name>, its own MagicDNS name, hosted by one or more nodes) as an alternative or complement. It could keep short names for a few key services without a node per service, and it fits multiple servers hosting the same service.
- Host serve vs one proxy container. Either run
tailscale serve on the host's own tailscaled (see linux-server/tailscale-web.service), or run one reverse-proxy sidecar per host. With host serve, backends are reached on 127.0.0.1:<published-port>. Services that currently share the sidecar's netns (network_mode: service:<svc>-ts) would need ports published to localhost instead.
- Non-HTTP endpoints. forgejo exposes git SSH on
:22 through its sidecar, and that clashes with the host's sshd if moved onto the host node. It needs another port, or forgejo stays its own node.
- URL consumers to migrate. homepage links and widgets, uptime-kuma monitors, ntfy base URL (subscribed phone clients and publishers), forgejo remotes on every machine and CI runners,
.env files referencing *.ts.net hosts, HTTPS.md/post-install.md.
- Cleanup. Remove the old nodes from the admin console, delete
ts-state/ dirs and the per-service TS_AUTHKEY entries, and update the ACL to the new per-host tags.
Related
Acceptance
Problem
Every web service gets its own Tailscale sidecar, which means its own tailnet node and its own
<service>.<tailnet>.ts.netname:linux-server/: 18 sidecars (adguard, atvloadly, cockpit, filebrowser, forgejo, glances, homepage, nginx-proxy-manager, ntfy, openspeedtest, portainer, qbittorrent, speedtest-tracker, syncthing, tailscale-web, ups, uptime-kuma, watchtower)linux-pi/: 4 more (adguard, cups, homepage, motioneye)Each new service adds a node, which floods the tailnet's machine list. The flat namespace also doesn't scale to more servers: the name doesn't say which host a service runs on, and two servers can't both run a service with the same name.
Goal
One tailnet node per host, with services addressed under it:
Adding a server should add exactly one node. Adding a service should add none.
Things to settle before implementing
tailscale serve --set-path /<service>) only works cleanly when the app can run under a base path or uses only relative URLs. Each service needs checking. Some have a base-path setting (forgejoROOT_URL, filebrowserbaseURL, cockpitUrlRoot). Others are known to be awkward behind a path prefix. Services that can't take a path need a fallback:https://<server>.<tailnet>.ts.net:<port>), orsvc:<name>, its own MagicDNS name, hosted by one or more nodes) as an alternative or complement. It could keep short names for a few key services without a node per service, and it fits multiple servers hosting the same service.tailscale serveon the host's own tailscaled (seelinux-server/tailscale-web.service), or run one reverse-proxy sidecar per host. With host serve, backends are reached on127.0.0.1:<published-port>. Services that currently share the sidecar's netns (network_mode: service:<svc>-ts) would need ports published to localhost instead.:22through its sidecar, and that clashes with the host's sshd if moved onto the host node. It needs another port, or forgejo stays its own node..envfiles referencing*.ts.nethosts,HTTPS.md/post-install.md.ts-state/dirs and the per-serviceTS_AUTHKEYentries, and update the ACL to the new per-host tags.Related
TS_AUTHKEY, pin the sidecar image, tighten thetag:containerACL. Most of these shrink or go away with one node per host.Acceptance
https://<server>.<tailnet>.ts.net/<service>(or a documented fallback)ts-state/removed; old nodes gone from the tailnet