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
[Bug] Zombie (defunct) Chrome processes accumulate as children of a live server under --isolated HTTP transport — never reaped #1769
Running the MCP server as a long-lived HTTP server with --isolated, Chrome processes accumulate as direct children of the still-running node /app/cli.js process. They are never reaped. Restarting the container clears them.
This appears distinct from #1568 and #1634. Those concern orphaned live Chrome processes left behind after the parent exits. Here the parent is alive throughout — its dead children are simply never wait()ed.
Environment
Image: mcr.microsoft.com/playwright/mcp:latest (container started 2026-09-15)
Flags: --headless --browser chromium --isolated, plus --host / --port / --allowed-hosts — long-lived HTTP transport, not stdio
Container not run with --init
Host pid_max: 4194304
Observed
After ~6.9 days uptime: 96 zombie processes, all <defunct> — roughly 14 per day on average, bursty rather than steady.
100% share the same PPID — the server's own live node /app/cli.js process. None reparented to PID 1.
The count grew from 96 to 100 in under two minutes while ss showed no established connections to the server port. Suggests children of already-disconnected sessions exit later and are never reaped — the leak outlives the session.
Restarting the container: 100 → 0 in about three seconds.
Why --init does not help
tini / --init reaps zombies that are reparented to PID 1 after their parent dies. These zombies' parent never dies, so they are never reparented and --init never sees them. The fix has to be in the server process itself — reaping its own children (SIGCHLD handling / waitpid), or ensuring child process handles are awaited when a session closes.
Impact
Low at this rate — each zombie holds only a process-table slot, not memory — but unbounded. Under sustained concurrent use, it would eventually exhaust the PID table. Current workaround: periodic restart, safe under --isolated since no browser profile state persists.
Expected reproduction
Run the server with --isolated over HTTP transport.
Open and close several sessions, concurrently and repeatedly.
ps -eo pid,ppid,stat,cmd | grep defunct— defunct Chrome / crashpad children accumulate under the node process.
Happy to provide further diagnostics — a version string, a full process-tree dump, or anything else that would help.
Thanks for the detailed report! In your setup the node process is actually PID 1 inside the container, because the image's entrypoint runs node /app/cli.js directly and you're not passing --init. The zombies are Chrome's own children (chrome_crashpad_handler, zygote processes). When a browser closes they get orphaned and reparented to PID 1. Node only reaps processes it spawned itself, so they stay <defunct>. I reproduced it with :latest: a few --isolated HTTP sessions left 12 zombies, all with ppid=1.
Running the container with --init fixes it: same test, 0 zombies. The Docker examples in the README already use it. #1771 also bakes tini into the image, so it will work without --init too, which helps on Kubernetes/ECS where there's no such flag.
For a long-lived HTTP server, I'd also recommend --idle-timeout. Clients that disconnect without closing their session otherwise leave that session's browser running indefinitely. With the option, the browser is closed after the given number of milliseconds without a completed tool call, and the next tool call launches it again:
Activity
yury-s commentedon Sep 25, 2026
Thanks for the detailed report! In your setup the node process is actually PID 1 inside the container, because the image's entrypoint runs
node /app/cli.jsdirectly and you're not passing--init. The zombies are Chrome's own children (chrome_crashpad_handler, zygote processes). When a browser closes they get orphaned and reparented to PID 1. Node only reaps processes it spawned itself, so they stay<defunct>. I reproduced it with:latest: a few--isolatedHTTP sessions left 12 zombies, all withppid=1.Running the container with
--initfixes it: same test, 0 zombies. The Docker examples in the README already use it. #1771 also bakestiniinto the image, so it will work without--inittoo, which helps on Kubernetes/ECS where there's no such flag.For a long-lived HTTP server, I'd also recommend
--idle-timeout. Clients that disconnect without closing their session otherwise leave that session's browser running indefinitely. With the option, the browser is closed after the given number of milliseconds without a completed tool call, and the next tool call launches it again:That should fix both the zombies and the browsers left running by abandoned sessions.
fix(docker): run server under tini to reap orphaned browser processes (…
chore: mark v0.0.83 (#1779)
chore: mark v0.0.83 (microsoft#1779)