Replies: 4 comments
Accepted, in the part that is crew's — and
|
| # | Recommendation | Owner |
|---|---|---|
| 1 | cgroup ceiling + OOMPolicy=kill |
crew for the ceiling; the crontab→systemd-unit half is your decision, below |
| 2 | oom_score_adj before launch |
crew — one line in run_session, needs no decision |
| 3 | more guest RAM / zram | host + heavy-duty/box |
| 4 | earlyoom |
host + heavy-duty/box |
| 5 | record peak RSS | crew — SESSION END gains the figure; the head half is void (§2) |
| 6 | host-side liveness watchdog | host + heavy-duty/box |
crew provisions no boxes and has no surface for 3, 4 or 6 — they are real and they are not filed here.
4. The one question I am not answering for you
Recommendation 1's "better still" — a systemd timer + service replacing the crontab entry, with TimeoutStartSec= replacing timeout — is not a fix, it is a change to the install channel. shared/crontab.example is deliberately one line per box because "five hand-edited variants" drifted, and the engine's dependency floor is stated as bash+gh+jq+git+flock+timeout (common.sh:85) — systemd-run adds systemd to it. That is yours, and it is written into entry 20 as an open question rather than picked by me. oom_score_adj needs no ruling either way and rides regardless.
Placement is also yours: entries 21 (instrument) and 22 (evidence) are 0.1.3's charter in as many words; entry 20 is hardening and may belong to 0.1.9. The roadmap (#338) is where it moves if you want it moved. No needs-ruling is set: nothing on the board is stopped by either question, and release-init step 4 already puts the wave in front of you.
5. What the postmortem could not see, and it is crew's
A session killed with the box leaves a SESSION START with no end — a fourth shape the evidence contract does not name, because tick.sh writes its FAILED line from an rc that never existed. The consequence is not cosmetic: _session_terminal_record runs at common.sh:240, after the SESSION END line, so the #388 session breaker records neither a failure nor a reset for it. A prompt that OOMs the guest deterministically would repeat every tick forever and never trip the one mechanism built to stop that. Entry 22.
6. Not the day's only incident on this box
A ~10-hour credential-exhaustion window preceded it — 147 failed sessions, 03:04:25Z–13:03:44Z, all on the weekly limit — recorded on #207. Unrelated in cause, and worth holding beside this one: the board was clean and the tick cadence unbroken through both, so neither is visible from anything but the log. That is the same argument entries 21 and 22 make.
Leaving this thread open for §4. If you would rather the two questions were on the board as a ruling ask instead of here, say so and I will put them there in the canonical form.
|
I'd add a fix to this in 0.1.3 --- my take is simple, we have to prevent ways in which the crew stops working and breaks our invariants and constraints. this is a perfect case of something that can happen at any time, at the very least we should have a way to get back up: a force stop in the crew floor, a mechanism to start a box that had crashed, a restart option for boxes in the crew floor. also a way to monitor this or similar situations. having a way to get back on our feet @dan-claude-bot --- if this involves new issues, they should be added as comment in thorough detail in 0.1.3 release epic issue |
Recorded on #327 as asked — and two of the four already shipTriage 2026-08-14 Your reply settles entry 20. "a fix to this in 0.1.3" — so containment (20), the peak-RSS instrument (21) and the orphaned-start repair (22) are all this window's, and the placement question I left open is closed. Correct me if that reads wrong. Measured before writing: two of the four asks already exist
So the gap is not that the floor cannot act. It is that every stop the floor can fire is a graceful stop — The three entries
Two things you do not have to decideThe force path needs no ruling and no change in heavy-duty/box. box#11's own rule is "box owns a command when it must enforce an invariant incus cannot see … everything else is incus's job, and that door stays open." A force stop enforces nothing incus cannot see, so it is the escape hatch's — The host-side half stays out of crew — more guest RAM or zram, Still open, still not blockingThe one question from my earlier §4 that this reply does not answer: whether the crontab entry becomes a systemd timer + service, with |
Closing as resolved — every ask on this thread is now a live issue on the
|
| your ask | issue | state today |
|---|---|---|
| containment — one session must not take the box down | #474 | open, blocked (carries its own needs-ruling on the mechanism) |
| the peak-RSS instrument | #473 | open, blocked |
the orphaned SESSION START repair |
#478 | open, ready |
| reclaiming a box that accretes state | #457 | open, ready |
| a force stop the floor can actually fire | #486 | open, claimed — PR #525 |
Nothing is owed here and the record lives on the issues. Closing so the discussion board shows only threads that are actually waiting on somebody — reopen or open a fresh one if any of those specs reads wrong against the tree.
Uh oh!
There was an error while loading. Please reload this page.
The file tooling isn't responding right now, so here it is inline — copy it straight into your incident docs.
Postmortem:
claude-triageVM unresponsive, 2026-08-14Status: Resolved Severity: Low (single non-production VM; no host or user-facing impact) Duration: ~12 minutes of unresponsiveness (20:12:45 – 20:24:25 UTC)
Summary
claude-triage, an Incus virtual machine ondev-server-1, became unresponsive to normal management commands on the evening of 2026-08-14. The guest kernel remained alive and its Incus agent kept answering, but nothing inside the VM could make forward progress.The cause was a scheduled cron job that invokes Claude Code. One invocation grew to 3.42 GB of resident memory on a VM with roughly 4 GB of RAM and no swap. This drove the guest into an unrecoverable memory state: the kernel OOM killer fired, but killed only one process out of a larger runaway tree, and the surviving siblings kept the machine pinned.
The VM was recovered with a forced stop and restarted cleanly. The host was never affected.
Environment
Root cause
A single cron-invoked process consumed roughly 85% of the VM's total RAM.
The process tree systemd printed while trying to restart cron shows the full chain:
2.1.220is the Claude Code binary, which renames itself to its version string at runtime. Aheadprocess appears alongside it, suggesting the job pipes Claude's output — a plausible route to unbounded memory growth if the upstream side produces a large stream.Three conditions turned a large allocation into a hung machine:
Total swap = 0kB. The kernel had no secondary tier to page anonymous memory out to.active_file:0kB,inactive_file:112kB, andall_unreclaimable? yes. With essentially no page cache, every code page fault became a major fault requiring a disk read. Ordinary processes — including sshd and the agent's helpers — thrashed rather than ran. This is why the VM looked dead while the kernel was demonstrably alive.The job ran directly under
cron.servicewith no memory limit, so its failure took cron down with it.What was ruled out
dmesgshows no OOM events, no I/O errors, and no storage-layer problems; the only entries are from the host's 2026-07-29 boot.incus inforeturned live guest OS version, kernel version, hostname and process count. Those fields are supplied by the in-guestincus-agent, so the guest kernel and agent were both running.ENOSPCor EXT4 errors.Contributing factors
incus top <instance>(the argument is a remote, not an instance) andincus down(the verb isincus stop). This added minutes before--forcewas reached./var/log/journal, makingjournalctl -b -1available after reboot. Had the journal been RAM-only, this incident would have been unexplainable.Impact
Limited to the
claude-triageVM. Any cron work scheduled during the window did not run, andcron.serviceitself was down. No host impact, no data loss, no user-facing impact. The forced stop risked an unclean filesystem, but the VM rebooted without errors.Remediation
Done
incus stop --forceand restarted.Recommended
OOMPolicy=killterminates the entire process tree — the specific gap that let this incident persist after the kernel's kill. Better still, migrate the job from a crontab entry to a systemd timer + service so these limits, and aTimeoutStartSec=replacing thetimeoutwrapper, live in a version-controlled unit file.echo 500 > /proc/self/oom_score_adj.incus config set claude-triage limits.memory 8GiB) or add compressed swap viasystemd-zram-generator.earlyoom, which acts on free-memory thresholds and works without swap, rather than waiting for the kernel to reach the fully-unreclaimable state./usr/bin/time -v) to establish whether this is a one-off spike or a growing trend, and inspect what is being piped throughhead.incus exec claude-triage -- true) that dumpsdf -h,free -mand top processes by RSS on failure before restarting — preserving evidence and recovering automatically.Explicitly not recommended: daily restarts
A scheduled restart was considered and rejected. This was not a slow leak that accumulates over uptime — it was a single invocation exceeding available memory, which a daily restart would not have prevented. Scheduled restarts also destroy the evidence needed to diagnose recurrences, and mask the misbehaving job rather than fixing it. They remain a reasonable workaround for a confirmed, unpatchable leak; that is not the situation here.
Lessons
incus infoprove the guest kernel is alive — which usefully rules out hypervisor faults and points diagnosis inward, toward resource exhaustion.OOMPolicy=killto fail cleanly./var/log/journal, forcing the VM down would have destroyed the only record of the cause.All reactions