Node runs as PID 1 and ignores SIGTERM, so Docker waits 10 seconds and then kills it.
How docker stop works: it sends SIGTERM to PID 1 (the first process in the container). Then it waits a grace period, 10 seconds by default. If the process is still running, Docker sends SIGKILL.
Why PID 1 matters: on Linux, PID 1 is special. It ignores signals whose default action is to end the process, unless the process installs a handler. Node installs no SIGTERM handler by default. So as PID 1, Node ignores the signal and gets killed after the timeout.
With shell-form CMD (a plain string, like CMD node dist/main.js), the shell is PID 1. The shell may not pass the signal on to Node.
Fixes:
- Start Node with exec form:
CMD ["node", "dist/main.js"]. Then Node itself gets the signal. - Add
process.on("SIGTERM", ...). In it, stop accepting requests, finish in-flight work, close connections, then exit. - Optionally, run an init process with
docker run --initor tini. It forwards signals and reaps zombie processes (cleans up finished child processes).
- docker stop sends SIGTERM, then SIGKILL after 10 seconds.
- PID 1 ignores default-action signals unless it has a handler.
- Use exec form so node itself gets the signal.
- Handle SIGTERM and shut down cleanly.
- Use --init or tini as a minimal init process.