fix: Tailscale container restart loop caused by web UI port conflict#974
Open
yyyr-p wants to merge 1 commit into
Open
fix: Tailscale container restart loop caused by web UI port conflict#974yyyr-p wants to merge 1 commit into
yyyr-p wants to merge 1 commit into
Conversation
Move the LAN-facing `tailscale web` listener from 5252 to 5253 (5252+1)
and update `port_map` to match.
Since Tailscale v1.56, `tailscaled` ships an on-device web UI ("device
web interface", KB 1325) that binds `<tailnetIP>:5252` when the
`RunWebClient` pref is enabled via `tailscale set --webclient`. The
port 5252 is a hardcoded constant in `client/web/web.go`
(`const ListenPort = 5252`).
The previous entrypoint's `tailscale web --listen 0.0.0.0:5252` then
fails to bind (`*:5252` overlaps the tailnet-IP-bound socket already
held by `tailscaled`); the `tailscale web` process exits; the shell
exits (status 0); Docker treats the container as done; and
`restart: unless-stopped` restarts it — every ~10s indefinitely.
This is user-pref-dependent, not upstream-version-dependent: users who
never enabled `--webclient` don't hit it. That's why the setup in IceWhaleTech#833
worked when written on v1.90.8 and only later broke for users who
turned the pref on. `tailscale set --webclient=false` doesn't reliably
release the socket either (tailscale/tailscale#12084, still open), so
choosing a non-conflicting port is the correct fix.
Port 5253 was chosen over `tailscale web`'s default 8088 because 8088
is already used by the Trilium app (`Apps/Trilium/docker-compose.yml`,
also host-networked). 5253 is unused across the entire AppStore
(grep-verified) and stays visually associated with the built-in 5252.
Verified on ZimaOS with tailscale/tailscale:v1.98.3 and
`RunWebClient: true` (the failure case):
- `docker compose config -q` passes.
- After deploy: `Up`, `RestartCount=0` for 60s+ (previously 143).
- No `die`/`start` events observed for 60s.
- LAN `curl http://<host>:5253/` returns HTTP 200.
- Tailnet `curl http://100.100.200.1:5252/` still returns HTTP 200
(built-in UI unaffected).
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
tailscale weblistener from5252to5253(5252+1) to avoid a bind conflict withtailscaled's built-in tailnet web UI, which also uses port 5252.x-casaos.port_mapto5253accordingly so the CasaOS app tile keeps opening the right port.Root cause
Tailscale ships an on-device web UI ("device web interface", KB 1325) introduced in v1.56. When a user enables it for tailnet access via
tailscale set --webclient(persisted as theRunWebClientpref),tailscaledstarts listening on<tailnetIP>:5252and[<tailnet-v6-ip>]:5252. Port 5252 is a hardcoded constant inclient/web/web.go(const ListenPort = 5252, chosen because 5252 sits above "TSTS" on a qwerty keyboard).The current entrypoint tries to expose a LAN-facing web UI with:
The wildcard socket
*:5252overlaps with the tailnet-IP-bound socket thattailscaledalready holds, so the bind fails andtailscale webexits. Because it's the last foreground command in the entrypoint (tailscaled ... & sleep 10; tailscale web ...), the shell exits (status 0), Docker treats the container as done, andrestart: unless-stoppedrestarts it — every ~10s indefinitely. On a live system:RestartCountgrows continuously,docker eventsshows back-to-backdie/startpairs withexecDuration=10 exitCode=0.This is user-pref-dependent, not upstream-version-dependent: users who never enable
--webclientdon't hit it. That is presumably why the setup worked when written onv1.90.8in #833 and only later broke for users who turned the pref on.Turning the pref back off with
tailscale set --webclient=falseis not a reliable workaround either — see tailscale/tailscale#12084 (still open). The pref changes but the socket isn't released without atailscaledrestart (independently reproduced during this investigation on v1.98.3). So choosing a non-conflicting port is the correct fix.Why 5253 (not 8088)
The
tailscale webcommand's documented default islocalhost:8088, which would be the natural choice — but8088is already used by the Trilium app in this store (Apps/Trilium/docker-compose.yml, also host-networked), so two apps would collide on the same host port when both are installed.5253was chosen because:Apps/*/docker-compose.yml).5252, making the conflict-avoidance intent obvious.Validation
docker compose config -qon the modified file passes.tailscale/tailscale:v1.98.3withRunWebClient: true(i.e. the failure case):Up,RestartCount=0after 60s+ (previouslyRestartCount=143and growing).die/startevents observed for 75s of monitoring.curl http://<host-lan-ip>:5253/→HTTP/1.1 200 OKfrom a LAN client.curl http://100.100.200.1:5252/→HTTP/1.1 200 OKfrom a tailnet peer (built-in UI unaffected — the two coexist).Scope
Metadata-only change; no image bump (still
v1.98.3), soversion,update_at, andrelease_notesare intentionally left unchanged — matching the minimal-diff style of prior small fixes like #957.