Context
A container can appear healthy during requests yet fail to stop cleanly. One cause is its process tree: a shell wrapper may sit between Docker and the application without forwarding signals. Docker sends the configured stop signal to the container's main process, so the executable that owns PID 1 matters.
Before starting, identify the image, entrypoint, command override, stop-signal configuration, and application shutdown contract. Read the live container configuration first. Any signal exercise should use a disposable test instance with no production traffic, persistent business work, or shared mutable data.
1. Inspect the command Docker actually started
docker container inspect catalog
docker container top catalog
docker image inspect catalog-image:review
The names are illustrative. Compare image Entrypoint and Cmd with the running container's Path and Args. A deployment override can change behavior even if the Dockerfile looks correct. The process listing helps show whether a shell or wrapper is the parent of the application. Avoid interpreting a command string in a dashboard as a full process-tree explanation.
2. Review shell form and wrapper behavior
The exec form of ENTRYPOINT makes argument boundaries explicit:
ENTRYPOINT ["catalog-server"]
CMD ["--listen", "0.0.0.0:8080"]
For an explicitly illustrative wrapper that performs initialization and then hands over control, the final operation should replace the shell with the application:
#!/bin/sh
set -eu
exec catalog-server "$@"
These fragments are examples, not a complete image. The wrapper's initialization must itself handle failure correctly. A script that launches the application in the background and waits needs a more deliberate signal and child-process design. Merely adding exec to an unrelated command does not establish correct shutdown for multiple child processes.
3. Exercise the intended stop path
Start an isolated representative instance through your normal test harness and observe its process tree. Trigger a normal container stop only for that disposable instance. Record when the application receives the signal, stops accepting work, completes or abandons active operations, and exits.
A longer stop timeout can accommodate legitimate draining, but it cannot deliver a signal that the application never receives. Likewise, correct signal delivery cannot repair an application that ignores its shutdown hook. The test needs application logs and an external request observation, not only Docker's final stopped state.
Classify the shutdown failure
| Observation | Likely boundary | Next check |
|---|---|---|
| Shell is PID 1, app sees no stop signal | Entrypoint process tree | Shell form and wrapper handoff |
| App receives signal but never exits | Application shutdown | Active work and shutdown hooks |
| App exits while child processes linger | Child management | Supervisor or init strategy |
| Clean exit takes longer than configured grace | Time budget | Measured draining behavior and stop policy |
Consider an appropriate init process when child reaping or signal forwarding is part of the design. Do not assume it removes the need for application shutdown behavior. Also verify the configured stop signal; an image may intentionally use a signal other than the default.
Correct, verify and keep a shutdown SOP
Change the Dockerfile, wrapper, runtime override, or application handler that evidence identifies. Rebuild a local review image and repeat the same test with active harmless requests and a small background task. Confirm the application exits within its documented budget and leaves no incomplete test output.
Record the expected PID 1, signal, drain sequence, timeout rationale, and verification commands in the operating procedure. Monitor shutdown duration and forced termination indicators during releases. Revisit the test when entrypoints, process managers, or background workers change, because steady-state health does not reveal termination behavior.
Explore Clouvenix docker consulting for implementation and operational handover.
Technical reference
Dockerfile ENTRYPOINT semantics and exec-form build guidance.