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:

  1. 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"], or exec in entrypoint scripts.
  2. 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.

  • Linux
  • Processes
  • DevOps