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).
Confirmed on a live NixOS host today: the managed serve (pid ~1.5k-task agent scope, memory.high stalling, TasksMax pressure) was repeatedly probed past 2s and recycled — exactly this loop.
Draft fix in #48591: on Linux, when a user session bus and systemd-run exist, Daemon.start() spawns via systemd-run --user --scope --quiet --collect, so the serve lands in its own transient scope — a sibling of the client's cgroup. Verified end-to-end: serve pid landed in app.slice/run-p<id>.scope next to (not inside) the client's scope; service status/stop still work and the scope is collected on exit. Fallback path is unchanged on other platforms.
Activity
w0wl0lxd commentedon Sep 12, 2026
Confirmed on a live NixOS host today: the managed
serve(pid ~1.5k-task agent scope,memory.highstalling,TasksMaxpressure) was repeatedly probed past 2s and recycled — exactly this loop.Draft fix in #48591: on Linux, when a user session bus and
systemd-runexist,Daemon.start()spawns viasystemd-run --user --scope --quiet --collect, so the serve lands in its own transient scope — a sibling of the client's cgroup. Verified end-to-end: serve pid landed inapp.slice/run-p<id>.scopenext to (not inside) the client's scope;service status/stopstill work and the scope is collected on exit. Fallback path is unchanged on other platforms.