Skip to content

[Bug] Zombie (defunct) Chrome processes accumulate as children of a live server under --isolated HTTP transport — never reaped #1769

Closed

Description

Summary

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.

Activity

  1. yury-s commented on Sep 25, 2026

    @yury-s
    Member

    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:

    docker run -d --init -p 8931:8931 mcr.microsoft.com/playwright/mcp \
      --isolated --port 8931 --host 0.0.0.0 --idle-timeout 600000
    

    That should fix both the zombies and the browsers left running by abandoned sessions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions