Maintenance: reap stale headless-browser (chrome/playwright) processes — kill anything alive > 60 min #388
Labels
No labels
Agent
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
1 participant
Notifications
Total time spent: 8 minutes 8 seconds
Due date
igor
8 minutes 8 seconds
No due date set.
Dependencies
No dependencies set
Reference
joshtronic/igor#388
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
CoS-owned reliability ticket (Igor). Leaked headless-browser processes have choked the host several times (screenshot/visual-verification captures that never exit) and we've never built a guard. Add one.
What
At the top of each maintenance tick, sweep for browser-automation processes and SIGKILL any that have been alive longer than the threshold — no questions asked.
Why this is safe to be aggressive
This is a headless server. There is no desktop browser — every chrome/chromium/headless_shell/playwright process is automation (screenshots, visual self-verification, deploy smoke). A legitimate capture finishes in seconds to low single-digit minutes. Anything alive for an hour is a leak, full stop.
Threshold (hardcoded, not a knob)
STALE_BROWSER_SECS = 3600(60 minutes). Josh's bar was "1–2 hours is too long"; 60 min is the tight end and still ~10× any real run. Hardcode the constant per our strong-opinions convention — do not add an env var.Heuristic (which processes to target)
Match on process name / cmdline. Kill when age (etimes) >= 3600 AND the command matches an automation signature:
headless_shell,chrome,chromium,chromium-browser,chrome_crashpad_handler}--headless,--remote-debugging-port,--user-data-dir=/tmp,playwright,puppeteer,ms-playwrightKill the whole process tree / process group (chrome forks a renderer/GPU/zygote tree — killing just the parent orphans the children). Prefer
kill -9 -<pgid>or walk children by PPID.Guardrails
claude/nodemain process or its PPID chain.Observability
When it kills something, log one line per victim:
pid,etime, and a truncated cmdline (so it surfaces in logwatch and we can actually confirm the reaper is doing its job). Log nothing when it's a no-op.Placement
Fold into the existing maintenance sweep (top of the tick) — least code, self-healing every cycle. Do not stand up a separate systemd timer unless the tick cadence turns out too slow to matter; default to folding in.
Tests (required)
Per our "tests for harness work" rule: extract the decision into a pure predicate — given a parsed ps table (pid, ppid, etimes, cmd), return the set of pids to kill — and unit-test it in
bin/test-*.sh:nodebuild) -> spared (signature must match)--headless-> killedThe test must exercise selection only (mock table), never actually kill.
Acceptance
Validate) passes.