What happened
tailscaled (1.102.3, installed by this feature) panicked inside a long-running dev container and was never restarted. Because it crashed rather than shut down, it did not restore /etc/resolv.conf, which it had pointed at MagicDNS. The container was left with:
nameserver 100.100.100.100
nameserver fd7a:115c:a1e0::53
and no daemon to answer it, so every DNS lookup in the container black-holed: getent hosts registry.npmjs.org failed, npm/pnpm failed with EAI_AGAIN, and curl hung for its full timeout. The container itself stayed up and looked healthy, so this went unnoticed for hours.
From /var/log/tailscaled.log:
health(warnable=derp-timed-out): error: Tailscale hasn't heard from the '<region>' relay
server in 5m51s. The server might be temporarily unavailable, or your Internet
connection might be down.
panic: ipnlocal: watchdog timeout
goroutine 240320 [running]:
tailscale.com/ipn/ipnlocal.(*LocalBackend).reportDeadlock(0x...?)
tailscale.com@v1.102.3/ipn/ipnlocal/watchdog.go:146 +0xb2
created by time.goFunc
time/sleep.go:215 +0x2d
Two containers on the same host, both using this feature, panicked within the same minute — one panic each, both preceded by the same DERP stall. So whatever the underlying deadlock is, it is reachable from an ordinary network blip and can hit every container at once.
Why this is a feature-level problem
The panic itself belongs to tailscale/tailscale, but the blast radius is this feature's, for two reasons:
- Nothing supervises the daemon.
tailscaled-devcontainer-start runs exec setsid /usr/local/sbin/tailscaled in a backgrounded subshell and never looks at it again, and tailscaled-entrypoint calls that script once before exec "$@". If tailscaled exits for any reason, it stays exited for the life of the container.
- The crash takes DNS down with it, and the fix is already on disk. The feature's own backup,
/etc/resolv.pre-tailscale-backup.conf, was intact and correct the whole time. Nothing consults it unless tailscaled shuts down cleanly.
The result is the worst failure shape: a container whose networking is silently broken in a way that looks like a registry outage, a proxy problem, or a broken image.
Suggestions
- Supervise
tailscaled — a restart loop in the start script, or run it under whatever init the image has. A crash then costs seconds of connectivity instead of the rest of the container's life.
- Failing that, make the state self-healing: on start (and ideally periodically), if
tailscaled is not running, /etc/resolv.conf names 100.100.100.100, and /etc/resolv.pre-tailscale-backup.conf exists, restore the backup. That alone converts "all DNS is dead" into "the tailnet is down", which is both recoverable and diagnosable.
Possibly the same root cause as #57 (rebuild leaves DNS broken, backup missing) and #11.
Environment
- Feature:
ghcr.io/tailscale/codespace/tailscale
- tailscale/tailscaled 1.102.3 (commit 9329c3677031109ff6d0b80abee0cddc8f35ff6f)
- Base image: Ubuntu 22.04.5 LTS, Docker (not Codespaces), containers up 7-14 days
What happened
tailscaled(1.102.3, installed by this feature) panicked inside a long-running dev container and was never restarted. Because it crashed rather than shut down, it did not restore/etc/resolv.conf, which it had pointed at MagicDNS. The container was left with:and no daemon to answer it, so every DNS lookup in the container black-holed:
getent hosts registry.npmjs.orgfailed,npm/pnpmfailed withEAI_AGAIN, andcurlhung for its full timeout. The container itself stayed up and looked healthy, so this went unnoticed for hours.From
/var/log/tailscaled.log:Two containers on the same host, both using this feature, panicked within the same minute — one panic each, both preceded by the same DERP stall. So whatever the underlying deadlock is, it is reachable from an ordinary network blip and can hit every container at once.
Why this is a feature-level problem
The panic itself belongs to tailscale/tailscale, but the blast radius is this feature's, for two reasons:
tailscaled-devcontainer-startrunsexec setsid /usr/local/sbin/tailscaledin a backgrounded subshell and never looks at it again, andtailscaled-entrypointcalls that script once beforeexec "$@". Iftailscaledexits for any reason, it stays exited for the life of the container./etc/resolv.pre-tailscale-backup.conf, was intact and correct the whole time. Nothing consults it unless tailscaled shuts down cleanly.The result is the worst failure shape: a container whose networking is silently broken in a way that looks like a registry outage, a proxy problem, or a broken image.
Suggestions
tailscaled— a restart loop in the start script, or run it under whatever init the image has. A crash then costs seconds of connectivity instead of the rest of the container's life.tailscaledis not running,/etc/resolv.confnames100.100.100.100, and/etc/resolv.pre-tailscale-backup.confexists, restore the backup. That alone converts "all DNS is dead" into "the tailnet is down", which is both recoverable and diagnosable.Possibly the same root cause as #57 (rebuild leaves DNS broken, backup missing) and #11.
Environment
ghcr.io/tailscale/codespace/tailscale