Bug: Docker container leaks zombie chrome-headless processes (no init/reaper)
Description
When the server runs in Docker as a long-lived process, exited Chromium subprocesses accumulate as zombie (<defunct>) processes and never get reaped. Over a multi-day-lived container these pile up.
Steps to reproduce
- Run the published image as a long-running service, e.g. streamable-http:
docker run -d --name linkedin-mcp \
-v ~/.linkedin-mcp:/home/pwuser/.linkedin-mcp \
stickerdaniel/linkedin-mcp-server:latest \
--transport streamable-http --host 0.0.0.0 --port 8080 --path /mcp
- Issue a few scraping calls so the bridge/scrape flow launches Chromium.
- Inspect the container's process tree:
docker top linkedin-mcp
# or, on the host:
ps -ef | grep defunct
Observed
Several [chrome-headless] <defunct> processes parented to the Python PID, e.g.:
python(1)-+-chrome-headless(...) <- Z (zombie)
|-chrome-headless(...) <- Z (zombie)
...
In my case 4 zombies appeared within ~20s of the first request and sat there for 9 days.
Root cause
Dockerfile sets ENTRYPOINT ["python", "-m", "linkedin_mcp_server"], so Python runs as PID 1. When Patchright/Chromium helper processes exit, the kernel reparents them to PID 1, but Python never calls waitpid() (no SIGCHLD handler / asyncio child watcher), so they remain as zombies forever. On a normal host this is invisible because real init reaps orphans; inside a container with no init, only PID 1 can reap and Python doesn't.
Proposed fix
Add a minimal init (tini) as the entrypoint so PID 1 reaps orphaned subprocesses:
RUN apt-get update && apt-get install -y --no-install-recommends tini && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["tini", "--", "python", "-m", "linkedin_mcp_server"]
This fixes the published image for all consumers, including plain docker run users (i.e. without relying on docker run --init / compose init: true).
Environment
- Image:
stickerdaniel/linkedin-mcp-server:latest
- Transport: streamable-http
- Host: Linux
Bug: Docker container leaks zombie
chrome-headlessprocesses (no init/reaper)Description
When the server runs in Docker as a long-lived process, exited Chromium subprocesses accumulate as zombie (
<defunct>) processes and never get reaped. Over a multi-day-lived container these pile up.Steps to reproduce
docker run -d --name linkedin-mcp \ -v ~/.linkedin-mcp:/home/pwuser/.linkedin-mcp \ stickerdaniel/linkedin-mcp-server:latest \ --transport streamable-http --host 0.0.0.0 --port 8080 --path /mcpObserved
Several
[chrome-headless] <defunct>processes parented to the Python PID, e.g.:In my case 4 zombies appeared within ~20s of the first request and sat there for 9 days.
Root cause
DockerfilesetsENTRYPOINT ["python", "-m", "linkedin_mcp_server"], so Python runs as PID 1. When Patchright/Chromium helper processes exit, the kernel reparents them to PID 1, but Python never callswaitpid()(noSIGCHLDhandler / asyncio child watcher), so they remain as zombies forever. On a normal host this is invisible because realinitreaps orphans; inside a container with no init, only PID 1 can reap and Python doesn't.Proposed fix
Add a minimal init (
tini) as the entrypoint so PID 1 reaps orphaned subprocesses:This fixes the published image for all consumers, including plain
docker runusers (i.e. without relying ondocker run --init/ composeinit: true).Environment
stickerdaniel/linkedin-mcp-server:latest