SIGHUP (Signal 1) in Docker & Kubernetes
SIGHUP is signal 1. Its default action is terminate, and a process killed by it exits 129 = 128 + 1. Daemons such as nginx and syslogd catch SIGHUP to reload configuration, while nohup makes the signal ignored. Containers receive it from a closing terminal, an explicit kill -HUP (including docker kill -s HUP), or an image configured with STOPSIGNAL SIGHUP.
Proof commands: confirm the exit status and the killer
Run these before changing anything. The first two identify what the runtime recorded; the third finds a kernel-level kill on the host. Replace the placeholder container ID, pod name or host path with your own.
Docker inspect
docker inspect <container_id> --format '{{.State.Status}} | ExitCode: {{.State.ExitCode}} | OOMKilled: {{.State.OOMKilled}}'Kubernetes state
kubectl get pod <pod_name> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.lastState.terminated.reason}{"\tExit:"}{.lastState.terminated.exitCode}{"\n"}{end}'Signal mapping and senders
kill -l 1; sudo journalctl --since '15 min ago' | grep -E -i 'sighup|reload|hangup'SIGHUP has no kernel log line by default. Proof comes from process state and the sender: a closed terminal, systemd ExecReload, docker kill -s HUP, or an operator's kill command. If the app logs a reload line before exiting, its handler exists but failed to keep running.
Three production root causes
Default action: terminate. The textbook use is a daemon reloading configuration without a restart; nohup makes the signal ignored. In containers it arrives from a closing terminal, an explicit kill -HUP, or an image configured with STOPSIGNAL SIGHUP; docker stop sends the configured stop signal, SIGTERM by default.
1. The controlling terminal or session closed
When a terminal or SSH session goes away, the shell sends SIGHUP to its jobs. A process started without nohup or setsid dies unless it handles HUP. In containers, the equivalent is an attached docker run session that is interrupted or a parent process group that exits.
2. A reload was requested but the handler is missing or broken
Operational runbooks send kill -HUP $MAINPID to reload. The application must install a handler that re-reads configuration atomically and keeps serving; a handler that panics or calls exit turns a reload into an outage. Test the reload path with a config change and traffic in flight.
3. A sidecar or script sends HUP on configuration change
Reloader-style controllers and entrypoint scripts often signal PID 1 when a mounted ConfigMap changes. If the target is not the daemon but a shell wrapper, the shell may die and the child may be orphaned or killed. Make the application PID 1 with exec form before expecting reloads to work.
Copy-ready fixes for Docker Compose and Kubernetes
The two manifests set explicit memory limits and a termination grace period. Use them as a baseline, then adjust the values to the measured working set of the service.
docker-compose.yml
SIGHUP is not the default stop signal; set stop_signal: SIGHUP only if the application truly expects it. For a reload, run docker kill -s HUP <container> and watch the logs; do not use docker restart, which performs a full stop and start.
services:
app:
image: ghcr.io/acme/app:1.4.2
restart: on-failure:3
stop_grace_period: 20s
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
deployment.yaml
Kubernetes has no SIGHUP stop signal. To trigger a reload, exec into the pod and send it to PID 1: kubectl exec <pod> -- kill -HUP 1. The process must be PID 1 for that to reach it, which the exec form of the entrypoint guarantees.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 2
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
image: ghcr.io/acme/app:1.4.2
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
Handle HUP as a reload, not a shutdown
Keep the application as PID 1 and re-read configuration in place.
#!/bin/sh
set -eu
exec /usr/local/bin/app "$@"Frequently asked questions
What does SIGHUP do by default?
The default disposition is terminate. A process that does not install a handler dies, and the parent sees exit status 129 (128 + 1). Daemons override this to reload configuration, and nohup changes the disposition to ignore so a terminal hangup does not kill the job.
Why does my pod have exit code 129?
The container received SIGHUP and had no handler. By default Kubernetes does not send HUP on deletion; it sends the image's stop signal, SIGTERM unless STOPSIGNAL says otherwise. Look for kubectl exec kill -HUP 1, a sidecar or reload controller, or a shell that received a hangup. Confirm with the pod events and the application log immediately before exit.
Can I reload a container without restarting it?
Yes, if the application handles SIGHUP: docker kill -s HUP <container> or kubectl exec <pod> -- kill -HUP 1. If it only handles filesystem changes through a mounted ConfigMap, use a reloader controller that sends the signal. A restart is the fallback only when the app has no reload path.
Related exit codes, signals and network references
Follow the status you actually observed, then check the related port or calculator when the failure involves networking, exposure or firewall rules.
Reference basis: POSIX signal semantics (128 + n), Docker container exit status conventions, and Kubernetes container termination behaviour. Verify the exact behaviour against your runtime version before relying on it in an incident.
Last reviewed 2026-11-06 · All exit codes · Developer tools · Privacy