Skip to content

Reference orphan claude reaper

Console (11 GB VM) gets crushed by orphaned claude processes. Cause: when the Claude Code Remote Control desktop UI archives a session, it kills the tmux server but the claude binary doesn't always exit. Each orphan = 250-450 MB. A day's accumulation (10+ orphans) pushes Console into swap.

Files

  • forge/scripts/forge_reap_orphan_claudes.py — the reaper
  • /etc/systemd/system/forge-reap-orphan-claudes.{service,timer} — system units
  • forge/data/orphan_claude_state.json — first-seen tracking, keyed by pid:start_time
  • forge/logs/orphan-claude-reaper.log — silent runtime log

Detection (v2, 2026-05-04)

Source of truth: ~/.claude/sessions/<pid>.json — written by the claude binary on startup, consumed by the Remote Control desktop UI. Each registry has fields pid, sessionId, procStart, entrypoint, bridgeSessionId, optional name.

Mapping rule: a session shows as "Active" in the desktop UI iff bridgeSessionId is a non-null string AND process is alive AND entrypoint != "claude-vscode".

Reap rule: a registry whose process is alive AND has null/missing bridgeSessionId AND entrypoint != "claude-vscode" is "archived from user's view." Track first-seen by pid:procStart (procStart defeats PID reuse). After 2h grace -> SIGTERM (30s) -> SIGKILL.

Protected: any session whose name starts with a prefix in PROTECTED_NAME_PREFIXES (currently ("home-base",)) is skipped — these are intentional persistent anchors whose bridge tokens often go stale.

Also sweeps stale tmux sockets in /tmp/tmux-1000/ and dead registry files in ~/.claude/sessions/.

v1 (deprecated) — why it was wrong

v1 walked tmux sockets in /tmp/tmux-1000/ and labeled any claude whose ppid shell wasn't a registered pane an "orphan." False positives because tmux servers can run on sockets outside that dir, so genuinely live sessions got mis-labeled. Replaced with the registry-based approach above.

Cadence

OnUnitActiveSec=15min, OnBootSec=5min. Silent (no notify).

Tuning knobs (in script)

  • GRACE_SECONDS = 2 * 60 * 60 — orphan must persist 2h before kill
  • SIGTERM_WAIT = 30 — seconds before SIGKILL

Verify

sudo systemctl status forge-reap-orphan-claudes.timer
tail -f /home/justinwieb/forge/logs/orphan-claude-reaper.log
sudo systemctl start forge-reap-orphan-claudes.service  # force a tick

Caveats

  • First detection sets the grace clock at NOW, not at process start. So on initial deploy, existing orphans get a 2h reprieve before they're killed.
  • claude --dangerously-skip-permissions (no other args) raw invocations from ssh ttys are eligible for reap if their tty's tmux pane disappears.