fix: wipe /tmp before the Coder agent starts, not after - #868
Conversation
The released template wipes /tmp from script-agent-startup.sh, which deletes the Coder agent's own CLI. Coder's generated bootstrap downloads the agent binary into a per-boot "mktemp -d -t coder.XXXXXX" directory under /tmp, chdirs into it, appends it to the PATH of every session and script the agent runs, and only then runs the startup script - so the wipe removes the binary the agent installed seconds earlier. Every "coder stat" metadata panel reports "coder: command not found"; the agent process survives on an unlinked binary, so nothing else fails visibly. Excluding the agent's paths from the wipe would trade this breakage for a later one. The set is version-dependent and not uniformly named: v2.35.3 owns coder.XXXXXX/, coder-agent.sock (a hardcoded absolute path, not TMPDIR-relative), rotated coder-agent*.log files, coder-script-data/, coder-screen/, and boundary-audit.sock, which carries no "coder" prefix at all. The wipe moves to the workspace container's entrypoint instead. That is the only hook that runs on a container-only restart within a live Pod - the case the wipe exists for, since init containers run once per Pod - and it runs before the agent exists, so there is nothing to exclude. script-container-entrypoint.sh wipes /tmp and execs Coder's /workspace-init.sh, keeping the agent as PID 1 for orphan reaping and the liveness probe. The wipe there is best-effort rather than fatal: that entrypoint is the only path to a running agent, so aborting would turn stale scratch space into a CrashLoopBackOff with no way to shell in. It records its outcome instead, and script-agent-startup.sh asserts both that outcome and that the agent CLI resolves and runs - the check this change lacked the first time, which makes a recurrence a failed startup script in the workspace UI rather than eight quietly broken metadata panels.
Live verificationRun against a disposable workspace created from Cold Pod start
Metadata scripts, resolved through the same PATH the agent gives them: Container-only restart within the live Pod — the case the wipe exists for. A 64 MiB scratch file was written to The workspace was deleted afterwards. The check, run against the broken released templateThe new The trailing Why the wipe is worth keeping rather than droppingContainer-only restarts are not hypothetical here: the longest-running workspace pod in the cluster shows 3 restarts over 19 days, each of which would otherwise inherit the previous container's scratch space on a fixed-size 20Gi volume. Notes
|
|
🎉 This PR is included in version 2.27.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
What this fixes
A regression in the released template.
/tmpis wiped fromscript-agent-startup.sh, and that wipe deletes the Coder agent's own CLI.Coder's generated bootstrap (
/workspace-init.sh) downloads the agent binary into a per-bootmktemp -d -t coder.XXXXXXdirectory under/tmp,cds into it, appends that directory to thePATHof every session and script the agent runs (cli/agent.godoes anos.Setenv("PATH", …)on startup), and only then runs the startup script. The wipe therefore removes the binary the agent installed seconds earlier.Symptom: all eight
coder_agentmetadata panels report/bin/bash: line 1: coder: command not found. The agent process itself keeps running from an unlinked binary, so nothing else fails visibly — on a workspace recreated from the released template,/proc/1/cwdreads/tmp/coder.XXXXXX (deleted).What the agent owns under
/tmpEstablished from the deployed agent version's source, not inferred from the observed file names:
coder.XXXXXX/(holds the CLI, is the agent's cwd, is appended toPATH)provisionersdk/scripts/bootstrap_linux.sh,BINARY_DIRcoder-agent.sockagent/agentsocket/socket_unix.go— a hardcoded absolute/tmp/…, notTMPDIR-relativecoder-agent.log,coder-agent-<ts>.log,coder-agent-init.log,coder-startup-script.logLogDir, defaults toos.TempDir()coder-script-data/(bin/is prepended toPATH,<uuid>/per script)ScriptDataDir, defaults toos.TempDir()coder-screen/agent/reconnectingpty/screen.goboundary-audit.sockagent/boundarylogproxy/proxy.goThe set is version-dependent, and
boundary-audit.sockalready breaks thecoder*prefix that an exclusion glob would key on.The fix
The wipe moves from the agent startup script to the workspace container's entrypoint.
script-container-entrypoint.shbecomes the containercommand. It wipes/tmp, thenexecs Coder's/workspace-init.sh.script-agent-startup.shno longer wipes anything.The container entrypoint is the only hook that runs on a container-only restart within a live Pod — the exact case the wipe exists for, since the
/tmpvolume is per-Pod and init containers run once per Pod. It runs before the agent exists, so there is nothing to exclude and no coupling to Coder's internals.execkeeps the agent as PID 1, which the orphan reaping and thepgrep -f "coder agent"liveness probe both depend on.Alternatives rejected
/tmp(BINARY_DIR,CODER_AGENT_LOG_DIR,CODER_AGENT_SCRIPT_DATA_DIR). Cannot be done completely —coder-agent.sockandcoder-screen/are not fully configurable — and it would break the dotfiles.bashrcblock, which locates the CLI by globbing/tmp/coder*.The check that would have caught this
script-agent-startup.shnow asserts, after the agent is up:command -v codergoes through the samePATHacoder statmetadata script uses, then the resolved binary is executed. Against the currently released template this fails immediately.okoutcome fails here.startup_script_behaviorisblockingand the script setserrexit, so either failure surfaces as a failed startup script in the workspace UI while the agent keeps running and the workspace stays reachable to debug.Failure posture
The wipe in the entrypoint is best-effort rather than fatal. That entrypoint is the only path to a running agent, so aborting there converts stale scratch space into a
CrashLoopBackOffwith no way to shell in and look. Visibility is preserved by the recorded outcome that the startup script asserts on.Verification
testworkspace, which is still on the released version: it exits 1. See the same comment.