Docker ENTRYPOINT/CMD Shell Form - Signals Ignored, Container Won’t Stop
A shell-form ENTRYPOINT cmd / CMD cmd runs your process as a child of /bin/sh -c, which becomes PID 1 and does not forward signals. The container ignores SIGTERM, so docker stop waits the full grace period then SIGKILLs it.
What this error means
docker stop (or compose down) takes ~10 seconds per container before exiting, and the app never runs its graceful-shutdown logic. In CI this slows teardown and can leave work unflushed.
# Dockerfile (shell form - sh is PID 1, signals not forwarded):
ENTRYPOINT node server.js
# docker stop hangs for the 10s grace period, then:
# container exited with code 137 (SIGKILL after timeout)Common causes
Shell form wraps the process in /bin/sh
Shell form (ENTRYPOINT cmd) is implicitly /bin/sh -c "cmd". sh becomes PID 1, and by default it does not propagate SIGTERM to your process, so graceful shutdown never fires.
No init to reap/forward signals
Without exec form or an init, the app does not receive stop signals and docker stop falls back to killing it after the timeout.
How to fix it
Use exec form so your process is PID 1
JSON-array (exec) form runs the binary directly, so it receives signals.
ENTRYPOINT ["node", "server.js"]
# or, if you need a shell, exec into the process:
ENTRYPOINT ["/bin/sh", "-c", "exec node server.js"]Add a tiny init for signal handling
When the entrypoint must be a shell script, run it under an init that forwards signals and reaps zombies.
# docker run --init ... (uses tini)
# or in compose:
services:
api:
init: trueHow to prevent it
- Prefer JSON-array (exec) form for ENTRYPOINT and CMD.
- Use
execin entrypoint scripts so the final process replaces the shell. - Enable
--initfor images that legitimately need a shell wrapper.