POSIX Signals - SIGTERM
SIGTERM (Termination Request, signal number 15) is the standard POSIX signal used to request that a process terminate gracefully.
Unlike SIGKILL (which destroys a process unconditionally at the kernel level without notifying it), SIGTERM is catchable, blockable, and ignorable. It acts as a polite notification from the operating system or system administrator giving the application an opportunity to finish in-flight requests, flush buffers, release file locks, and shut down cleanly.
The Graceful Shutdown Contract
SIGTERM is the universal foundation of process lifecycle management across Unix, Docker, and Kubernetes.
[ Kubelet / Docker sends SIGTERM ]
│
▼
┌────────────────────────────────────────────────────────┐
│ 1. Application Catches SIGTERM │
│ - Stops accepting new network connections │
│ - Closes listening socket │
└───────────────┬────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 2. Drains Active Operations │
│ - Completes in-flight HTTP requests/transactions │
│ - Flushes cached disk buffers & WAL logs │
│ - Closes database connection pools & IPC semaphores │
└───────────────┬────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 3. Process Exits Cleanly: exit(0) │
└────────────────────────────────────────────────────────┘
The Kubernetes Pod Termination Sequence
When a pod is deleted in Kubernetes:
- The pod is immediately removed from the Service
EndpointSliceso it stops receiving new ingress traffic. - The Kubelet delivers a
SIGTERMsignal to the primary container process (PID 1). - The
terminationGracePeriodSecondstimer starts (default:30seconds). - If the application finishes draining and exits before the grace period expires, shutdown is complete.
- If the application is still running when the timer hits zero, the Kubelet issues
SIGKILLto terminate it abruptly.
Production Implementation Pattern (C)
A resilient graceful shutdown handler sets an atomic flag, allowing the main event loop to execute the teardown logic safely outside signal handler context:
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
volatile sig_atomic_t g_shutdown_requested = 0;
void handle_sigterm(int signum) {
// Only set an atomic flag (async-signal-safe)
g_shutdown_requested = 1;
}
int main() {
struct sigaction sa = {
.sa_handler = handle_sigterm,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL); // Catch Ctrl+C identically
printf("Server listening. Send SIGTERM (kill %d) to shutdown.\n", getpid());
while (!g_shutdown_requested) {
// Run main event loop / accept connections
sleep(1);
}
printf("\nSIGTERM received! Draining connections...\n");
// 1. close(listen_fd);
// 2. drain_active_workers();
// 3. flush_database_buffers();
printf("Shutdown complete. Exiting cleanly.\n");
return 0;
}
The Docker PID 1 "Signal Swallowing" Trap
In Linux, the process with Process ID 1 (PID 1) receives special treatment from the kernel:
- If a signal has not registered an explicit handler in a PID 1 process, the kernel will NOT apply the default action for that signal.
- If you run an application in a Docker container as PID 1 (e.g.
ENTRYPOINT ["./my_server"]) without registering a customSIGTERMhandler, runningdocker stopwill sendSIGTERM, which your process will silently ignore. - Docker will hang for 10 seconds before giving up and killing your container with
SIGKILL.
The Solution: Use an Init Process
Use an init runner like tini or dumb-init in your Dockerfile, or add docker run --init:
# Uses tini to forward SIGTERM correctly to child processes
ENTRYPOINT ["/usr/bin/tini", "--", "./my_server"]
Frequently Asked Questions (FAQ)
What is the difference between SIGTERM and SIGKILL?
SIGTERM (Signal 15) requests polite, graceful termination and can be caught and handled by application code, whereas SIGKILL (Signal 9) cannot be caught, blocked, or ignored and immediately terminates the process at the kernel level. Production systems always send SIGTERM first, allowing the application a grace period to drain connections before resorting to SIGKILL.
Why does docker stop wait 10 seconds before killing a container?
docker stop transmits SIGTERM and waits up to 10 seconds (the default grace timeout) for the container to exit cleanly before sending an uncatchable SIGKILL. If the containerized application ignores SIGTERM (common with PID 1 signal handling bugs) or hangs during cleanup, Docker forces termination after the timeout.
Can an application ignore SIGTERM indefinitely?
Yes, because SIGTERM is catchable, a buggy or malicious application can ignore it or remain stuck in an infinite cleanup loop. For this reason, process supervisors (systemd, Kubernetes, Docker) always pair SIGTERM with a hard timeout after which SIGKILL is sent automatically.
Related Topics
- POSIX Signals - SIGKILL: Uncatchable process termination and OOM killer mechanics.
- POSIX Signals - SIGHUP: Terminal disconnection and configuration reloads.
- POSIX Process Groups & Sessions: Signal distribution and job control.
