fix(docker): reap orphaned subprocesses via tini as PID 1#15116
Merged
Conversation
Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes #15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
nekorytaylor666
pushed a commit
to nekorytaylor666/hermes-agent
that referenced
this pull request
Apr 24, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
justrhoto
pushed a commit
to justrhoto/hermes-agent
that referenced
this pull request
Apr 24, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
ulasbilgen
pushed a commit
to ulasbilgen/hermes-adhd-agent
that referenced
this pull request
May 1, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
aj-nt
pushed a commit
to aj-nt/hermes-agent
that referenced
this pull request
May 1, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
donald131
pushed a commit
to donald131/hermes-agent
that referenced
this pull request
May 2, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
Egavasyug
pushed a commit
to Egavasyug/hermes-agent
that referenced
this pull request
Jun 10, 2026
…ch#15116) Install tini in the container image and route ENTRYPOINT through `/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh`. Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get waited() on and accumulate as zombies. Long-running gateway containers eventually exhaust the PID table and hit "fork: cannot allocate memory". tini is the standard container init (same pattern Docker's --init flag and Kubernetes pause container use). It handles SIGCHLD, reaps orphans, and forwards SIGTERM/SIGINT to the entrypoint so hermes's existing graceful-shutdown handlers still run. The -g flag sends signals to the whole process group so `docker stop` cleanly terminates hermes and its descendants, not just direct children. Closes NousResearch#15012. E2E-verified with a minimal reproducer image: spawning 5 orphans that reparent to PID 1 leaves 5 zombies without tini and 0 with tini.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #15012.
What this PR does
Installs
tiniin the container image and routesENTRYPOINTthrough/usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh.Without a PID-1 init, orphans reparented to hermes (MCP stdio servers, git, bun, browser daemons) never get
wait()-ed on and accumulate as zombies under PID 1. Long-running gateway containers eventually exhaust the PID table ("fork: cannot allocate memory").Why tini specifically
--initflag and Kubernetes'spausecontainer usetini 0.19.0-1)-gsends signals to the whole process group sodocker stopcleanly terminates hermes AND its descendantsValidation
E2E with a minimal reproducer image:
-g --(this PR)Unit tests (
tests/tools/test_dockerfile_pid1_reaping.py):Two contract tests that assert the Dockerfile (a) installs some init (tini, dumb-init, or catatonit), and (b) the last ENTRYPOINT line routes through it. Accepts any standard init so it's not a change-detector on tini specifically. Verified both tests fail on pre-fix Dockerfile, pass on post-fix.
What I considered and rejected
signal.signal(SIGCHLD, SIG_IGN)in Python — race-prone with Python's own subprocess wait(), doesn't cover shell entrypoint stage--initondocker run— requires user-side discipline, doesn't cover compose/k8sBaking tini into the image is the right layer — applies to every deployment path automatically.