Skip to content

feat: move vscode-server GC into the template as a mounted script - #869

Merged
ppat merged 1 commit into
mainfrom
feat/vscode-server-gc-template
Aug 18, 2026
Merged

feat: move vscode-server GC into the template as a mounted script#869
ppat merged 1 commit into
mainfrom
feat/vscode-server-gc-template

Conversation

@ppat

@ppat ppat commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • ~/.vscode-server grows without bound (interrupted Remote-SSH
    downloads, server versions VS Code no longer considers live, their
    orphaned code-<hash> binaries, superseded extension versions) on
    any workspace VS Code connects to. This was originally scoped to the
    operator's dotfiles repo as a script installed to
    $HOME/.local/bin/vscode-server-gc, with this template's existing
    coder_script.vscode_server_gc weekly cron invoking it via
    [ -x $HOME/.local/bin/vscode-server-gc ] && ... || true.
  • That placement doesn't hold up: ~/.vscode-server is created by VS
    Code the moment it connects to a workspace, independent of whether
    dotfiles have ever been applied there. Confirmed directly — the
    disposable test Coder workspace reached ~11 GB of
    ~/.vscode-server with dotfiles never applied to it. A script whose
    presence depends on a component that may be absent can't own cleanup
    of something that appears regardless of that component.
  • Worse, the existing guard fails silently: [ -x ... ] || true
    makes "the script isn't installed" indistinguishable from "the
    script ran and had nothing to do" — both report success on the
    weekly cron, on every workspace dotfiles hasn't reached.
  • Fix: script-vscode-server-gc.sh becomes a template-owned script,
    same as script-agent-startup.sh / script-memory-watchdog.sh
    mounted into the ConfigMap (configmap.tf), mounted into the
    workspace container at /vscode-server-gc.sh (deployment.tf), and
    invoked directly by the existing cron (scripts.tf) with no
    existence-check guard, so an actual failure now surfaces as a failed
    run in the Coder UI instead of vanishing into || true.
  • Added a narrow, concrete gotcha to CLAUDE.md about this specific
    anti-pattern (a template-scheduled coder_script depending on a
    payload from outside the template with absence swallowed by
    || true) — deliberately not a general rule about who owns state
    under $HOME, since the template's own watchdog, dotfiles-installed
    tooling, and the image all generate state there too; "who generated
    it" doesn't determine "who owns its lifecycle" in general, only in
    this specific coupling.

What the script does (unchanged from the dotfiles version)

Every deletion is driven by a signal VS Code itself writes to disk,
plus a pgrep-based safety net, never a guess at "newest N":

Class Signal used
cli/servers/*.staging Suffix alone — always an interrupted download
cli/servers/Stable-<hash> Not listed in cli/servers/lru.json
code-<hash> CLI binaries No matching cli/servers/Stable-<hash> directory remains
extensions/<name> Listed in extensions/.obsolete and not referenced by extensions/extensions.json

data/logs/ is deliberately left alone — no on-disk signal marks a
dated log directory as safe to remove.

Testing

  • pre-commit run --all-files passes, including terraform validate/tflint.
  • Re-verified on the disposable test workspace via the new invocation
    path (/bin/bash /vscode-server-gc.sh --dry-run): idempotent, zero
    items to remove on a second run (this workspace was already cleaned
    by the earlier, dotfiles-hosted version of the same script during
    investigation — this PR only changes where the script lives and how
    it's invoked, not its logic).

Ref: ppat/dotfiles#771

~/.vscode-server grows without bound on any workspace VS Code connects
to, whether or not the operator's dotfiles have ever been applied to
it (confirmed on the `test` workspace, which reached ~11 GB with
dotfiles never applied). The GC for it was previously scoped to the
dotfiles repo, invoked from here via
`[ -x $HOME/.local/bin/vscode-server-gc ] && ... || true` - a guard
that makes "the script isn't there" indistinguishable from "the
script ran and had nothing to do", so the weekly cron reported
success on every workspace dotfiles hadn't reached.

Adds script-vscode-server-gc.sh as a template-owned script alongside
script-agent-startup.sh and script-memory-watchdog.sh: mounted into
the ConfigMap (configmap.tf), mounted into the workspace container at
/vscode-server-gc.sh (deployment.tf), and invoked directly by the
existing coder_script.vscode_server_gc cron (scripts.tf) with no
existence-check guard, so an actual failure now surfaces as a failed
run in the Coder UI.

The script's own logic is unchanged from what was verified against the
dotfiles version: same deletion signals (cli/servers/*.staging,
cli/servers/lru.json, orphaned code-<hash> binaries,
extensions/.obsolete cross-checked against extensions.json),
same pgrep-based in-use safety net, same decision to leave
~/.vscode-server/data/logs alone. Re-verified on the disposable `test`
workspace via the new invocation path (/bin/bash
/vscode-server-gc.sh): idempotent, zero items to remove on a second
run.

Ref: ppat/dotfiles#771
@ppat
ppat force-pushed the feat/vscode-server-gc-template branch from ddef024 to acdfa24 Compare August 18, 2026 02:41
@ppat
ppat merged commit 265f7ee into main Aug 18, 2026
16 checks passed
@ppat
ppat deleted the feat/vscode-server-gc-template branch August 18, 2026 03:27
@homelab-workflows-bot

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.27.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant