You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
service: managed serve stays inside the spawning client's cgroup — client memory limits starve the shared server into watchdog kill loops #48588
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 repeated Background service is unresponsive; recovery cannot preserve persistent terminals followed by concurrent background service starting from several clients within ~300ms.
memory.events on the client scopes: high counter at 357k–628k crossings; oom_kill 0 — pure reclaim stalls, not OOM kills.
Raising the scope's memory.high from 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.
opencode 0.0.0-beta-19500 / -19507 / -19425, npm:@opencode/cli via 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 --scope on 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).
Summary
The managed
serve --serviceprocess is spawneddetached: trueby whichever client'sensure()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/healthprobe (2srequestTimeout, 3 consecutive timeouts inpackages/client/src/effect/service.ts), after which the client callsterminate()— 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
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.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.0.0.0-beta-19425,-19500, and-19507— not a version regression. The serve's footprint itself is large; serve: per-request instances are never disposed - MCP child processes accumulate until memory exhaustion #47727's undisposed per-request instances/MCP children likely contribute to crossing the limit.Environment
0.0.0-beta-19500/-19507/-19425,npm:@opencode/clivia misesystemd-run --user --scope,MemoryHigh=3G)opencode.db; several MCP serversSuggestions
systemd-run --user --scopeon Linux), and/orrequestTimeout/ backoff during startup, or verify process liveness separately from HTTP latency, and/orRelated: #37795 (same fragile 2s probe on
service restart), #47727 (serve memory growth from undisposed instances), #42270 (stale daemons after restart).