anomalyco / anomalyco/opencode
service: managed serve stays inside the spawning client's cgroup — client memory limits starve the shared server into watchdog kill loops
@kitlangton is already working on this.
Since Sep 12, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
The managed serve --service process is spawned detached: true by whichever client's ensure() wins the contender race, but detaching does not move it out of the client's cgroup. On Linux, when the client runs inside a resource-limited scope (systemd scope, container, etc.), the shared server inherits — and is billed against — the client's memory/CPU limits for its entire lifetime, outliving that client and serving every other attached session.
When the cgroup crosses memory.high, the kernel forces synchronous direct reclaim on all processes in the group. The serve stalls long enough to fail the /api/health probe (2s requestTimeout, 3 consecutive timeouts in packages/client/src/effect/service.ts), after which the client calls terminate() — SIGTERM on the shared server every session depends on. All clients then race to spawn contenders; the winner lands in another pressured cgroup, and the loop repeats every ~60–90s indefinitely.
Observed
- 4 TUI clients sharing one
serve --service. The serve was replaced every ~60–90s; the log shows repeatedBackground service is unresponsive; recovery cannot preserve persistent terminalsfollowed by concurrentbackground service startingfrom several clients within ~300ms. memory.eventson the client scopes:highcounter at 357k–628k crossings;oom_kill0 — pure reclaim stalls, not OOM kills.- Raising the scope's
memory.highfrom 3G to 8G stopped the flap immediately: the same serve survived a 4.76GB peak (above the old hard cap) and has been stable since. - Reproduces identically on
0.0.0-beta-19425,-19500, and-19507— not a version regression. The serve's footprint itself is large; #47727's undisposed per-request instances/MCP children likely contribute to crossing the limit.
Environment
- opencode
0.0.0-beta-19500/-19507/-19425,npm:@opencode/clivia mise - NixOS, kernel 6.12, systemd user scopes (
systemd-run --user --scope,MemoryHigh=3G) - 4 concurrent TUI clients; ~6GB
opencode.db; several MCP servers
Suggestions
- Spawn the managed serve outside the client's cgroup when a mechanism is available (e.g.
systemd-run --user --scopeon Linux), and/or - Make the health watchdog tolerant of reclaim stalls: longer
requestTimeout/ backoff during startup, or verify process liveness separately from HTTP latency, and/or - Document that resource limits on the launching client apply to the shared server.
Related: #37795 (same fragile 2s probe on service restart), #47727 (serve memory growth from undisposed instances), #42270 (stale daemons after restart).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.