19.2.3.3 Stop Signal Delivery
A focused guide to Stop Signal Delivery, connecting core concepts with practical Docker and container operations.
Signal delivery in the context of docker stop refers to how Docker sends operating system signals to the process running inside a container to trigger its shutdown. The signal mechanism is the primary channel through which the Docker daemon communicates a stop request to the container's process. Understanding signal delivery explains why some containers stop quickly, why others wait the full timeout before being killed, and how to configure containers to respond correctly.
What Is a Signal
A signal is a limited form of inter-process communication defined by POSIX. Signals are identified by numbers and names. When a signal is sent to a process, the operating system interrupts the process and invokes its registered signal handler. If no handler is registered, the kernel applies a default action (which for most signals is to terminate the process).
Common signals used in Docker:
| Signal | Number | Default Action | Description |
|---|---|---|---|
| SIGTERM | 15 | Terminate | Request graceful shutdown |
| SIGKILL | 9 | Kill (forced) | Forceful termination — cannot be caught |
| SIGINT | 2 | Terminate | Interrupt from keyboard (Ctrl+C) |
| SIGHUP | 1 | Terminate | Hang up / reload configuration |
| SIGQUIT | 3 | Core dump | Quit with a core dump |
How docker stop Delivers Signals
When docker stop is called, the Docker daemon sends the stop signal (default: SIGTERM) directly to PID 1 of the container. Docker does not send signals to all processes in the container — only to the process that has PID 1 within the container's PID namespace.
docker stop my-container
Internally, Docker calls the equivalent of:
kill -SIGTERM <pid-1-of-container>
The Docker daemon then starts a countdown timer (the stop timeout). If PID 1 exits before the timer expires, the container moves to the exited state. If the timer expires, Docker sends SIGKILL:
kill -SIGKILL <pid-1-of-container>
SIGKILL cannot be caught or ignored by the process. It terminates it immediately regardless of what it was doing.
Customizing the Stop Signal
By default, docker stop sends SIGTERM. Some applications do not handle SIGTERM but respond to a different signal (e.g., SIGINT, SIGHUP, or SIGUSR1 for configuration reload). The stop signal can be customized in the Dockerfile:
STOPSIGNAL SIGINT
Or at container run time:
docker run -d --stop-signal SIGINT myapp:1.0.0
After this configuration, docker stop sends SIGINT instead of SIGTERM as the first signal.
Signal Delivery and PID 1
PID 1 occupies a special position in Unix process management. The kernel treats PID 1 differently from all other processes:
- PID 1 does not inherit the kernel's default signal handlers. Signals sent to PID 1 that have no registered handler are simply ignored.
- This means if PID 1 does not explicitly register a SIGTERM handler, SIGTERM is silently dropped, and the container waits the full timeout before being killed.
This is a common source of confusion. An application process running as PID 2 would terminate when it receives SIGTERM (because of the default kernel handler), but the same application running as PID 1 in a container ignores it.
Example: if the Dockerfile uses the shell form of CMD:
CMD ./server
The shell (/bin/sh) becomes PID 1 and runs ./server as a child process. When SIGTERM is sent to PID 1 (the shell), the shell does not forward it to the child process. The server never receives the signal and keeps running until SIGKILL.
With the exec form:
CMD ["./server"]
./server becomes PID 1 directly. It receives SIGTERM and can handle it.
Signal Delivery Flow
Using docker kill for Custom Signals
docker stop always sends the stop signal (SIGTERM by default) followed potentially by SIGKILL. If you need to send an arbitrary signal without the two-phase logic, use docker kill:
docker kill --signal SIGHUP my-container
This sends SIGHUP (commonly used to trigger configuration reload) without stopping the container. The container continues running after receiving the signal.
docker kill --signal SIGUSR1 my-container
Applications can define custom behavior for user-defined signals (SIGUSR1, SIGUSR2).
Verifying Signal Handling Works
To test whether a container responds to SIGTERM before deploying to production:
# Start the container
docker run -d --name test-app myapp:1.0.0
# Send SIGTERM and time how long it takes to exit
time docker stop test-app
# Check the exit code
docker inspect --format '{{.State.ExitCode}}' test-app
If docker stop returns in well under the timeout and the exit code is not 137, the application handled SIGTERM correctly. An exit code of 137 or a stop that takes exactly the timeout duration indicates that SIGKILL was needed, meaning the application did not handle SIGTERM.