Signals are short messages the kernel delivers to processes: “please stop”, “stop now”, “reload your config”. Every time you press Ctrl+C, run kill, restart a service or stop a container, signals are involved. Knowing the common ones helps you stop processes cleanly, reload services without downtime, and write apps that shut down gracefully.
The signals you’ll actually use
| Signal | Number | Default action | Typical meaning |
|---|---|---|---|
| SIGTERM | 15 | Terminate | “Please shut down cleanly.” The default for kill, systemd and Docker |
| SIGKILL | 9 | Kill (can’t be caught) | Immediate termination by the kernel. Last resort |
| SIGINT | 2 | Terminate | Ctrl+C in a terminal |
| SIGHUP | 1 | Terminate | Terminal closed. Many daemons use it as “reload config” |
| SIGQUIT | 3 | Core dump | Ctrl+\. Nginx uses it for graceful shutdown |
| SIGUSR1 / SIGUSR2 | 10 / 12 | Terminate | Application-defined: Nginx reopens logs on USR1 |
| SIGSTOP | 19 | Stop (can’t be caught) | Pause a process |
| SIGCONT | 18 | Continue | Resume a stopped process |
| SIGTSTP | 20 | Stop | Ctrl+Z |
| SIGCHLD | 17 | Ignore | A child process exited |
| SIGPIPE | 13 | Terminate | Writing to a closed pipe or socket |
List them all with kill -l.
Sending signals
kill 12345 # SIGTERM
kill -TERM 12345 # same, by name
kill -HUP 12345 # reload, for daemons that support it
kill -9 12345 # SIGKILL: only if TERM doesn't work
pkill -USR1 nginx # by process name
killall -HUP rsyslogd
See kill process.
Why SIGTERM first, SIGKILL last
SIGTERM can be caught. The process gets a chance to finish requests, flush data to disk, close database connections and remove lock files. SIGKILL can’t be caught, so the kernel removes the process instantly, mid-write if necessary. Killing a database with -9 can mean crash recovery, or worse. Always send TERM, wait a few seconds, and only then use KILL.
Signals and services
systemd stops services with SIGTERM, waits TimeoutStopSec (90 s by default), then sends SIGKILL. Reloads (systemctl reload) run the unit’s ExecReload, which often sends a signal:
ExecReload=/bin/kill -HUP $MAINPID
What popular daemons do with signals:
| Program | Signal | Effect |
|---|---|---|
| Nginx | HUP | Reload config gracefully |
| Nginx | USR1 | Reopen log files (logrotate) |
| Nginx | QUIT | Graceful shutdown |
| PHP-FPM | USR2 | Graceful reload of workers |
| Gunicorn | HUP | Reload workers. TTIN/TTOU add or remove workers |
| PostgreSQL | HUP | Reload config (pg_ctl reload) |
| rsyslog / sshd | HUP | Reload config |
Prefer systemctl reload name where available. It uses the right method.
Graceful shutdown in your app
Apps should catch SIGTERM, stop accepting new work, finish in-flight work and exit. That’s essential for zero-downtime deploys and container platforms.
Node.js:
process.on('SIGTERM', () => {
server.close(() => process.exit(0)); // stop accepting, finish open requests
setTimeout(() => process.exit(1), 10000); // safety timeout
});
Python:
import signal, sys
def shutdown(signum, frame):
# finish work, close connections
sys.exit(0)
signal.signal(signal.SIGTERM, shutdown)
See zero-downtime Node deploys.
Signals in Docker
docker stop sends SIGTERM to the container’s PID 1, waits 10 seconds, then sends SIGKILL. Two common problems:
- The shell form of CMD (
CMD node server.js) runs your app under/bin/sh -c. The shell is PID 1 and doesn’t forward signals, so the app gets killed after the timeout. Use the exec form,CMD ["node", "server.js"], orexecin entrypoint scripts. - PID 1 has no default signal handlers in Linux. An app that doesn’t install a SIGTERM handler ignores it as PID 1. Add a handler, or run with
--init(tini) to get proper signal handling and zombie reaping.
Containers that always take 10 seconds to stop are showing this problem. See Dockerfile tutorial.
trap in Bash scripts
cleanup() { rm -f /tmp/myjob.lock; echo "stopped"; }
trap cleanup EXIT
trap 'echo "interrupted"; exit 130' INT TERM
EXIT runs on any exit, which makes it ideal for cleanup. See Bash error handling.
Exit codes from signals
A process killed by a signal exits with 128 + signal number:
| Code | Signal |
|---|---|
| 130 | SIGINT (Ctrl+C) |
| 137 | SIGKILL, often the OOM killer or docker stop timeout |
| 143 | SIGTERM |
Exit code 137 in Docker or Kubernetes is a classic sign of out-of-memory. See OOM killer.
Quick reference
| Task | Command |
|---|---|
| Polite stop | kill PID |
| Force stop | kill -9 PID |
| Reload daemon | kill -HUP PID / systemctl reload name |
| Pause / resume | kill -STOP PID / kill -CONT PID |
| List signals | kill -l |
| Catch in Bash | trap 'cmd' TERM INT EXIT |
Related: ps and nohup and background jobs.