Exit code 129 Warning Docker & Kubernetes

Exit Code 129 (SIGHUP) in Docker & Kubernetes

Verdict · 129 = 128 + 1 (SIGHUP)

Exit code 129 equals 128 + 1, so the process was terminated by SIGHUP. The default action of SIGHUP is terminate, but daemons such as nginx and syslogd catch it to reload configuration. In a terminal it usually means the controlling terminal closed; in a container it means someone sent HUP or a parent session ended.

Observed exit129
Signal / meaningSIGHUP
Default actionWarning

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

kill -l 129; kill -l 1; sudo dmesg -T | tail -n 20

The parent observes 129, but the signal number is 1. Look at who sent it: an interactive session that closed, a systemd ExecReload, a docker kill --signal HUP, or a shell job control hangup. The signal is not in the application log unless the app installed a handler.

Three production root causes

128 + 1: the process received a hangup signal and did not handle it, so the default terminate action applied. Detach long-running processes with nohup or setsid, run containers detached, and implement a SIGHUP reload handler where reload is intended.

1. The controlling terminal or SSH session closed

Shells send SIGHUP to their jobs when the terminal disappears. A process started without nohup or setsid, or a docker run without -d attached to a closing terminal, receives HUP and terminates. Use a supervisor, a detached container, or enable lingering for a systemd user service.

2. A reload was requested but no handler exists

Operations teams send kill -HUP $MAINPID to reload configuration. nginx and several daemons implement this; an arbitrary application does not, so the default terminate action kills it. If reload is part of the runbook, the application must install a SIGHUP handler that re-reads configuration atomically.

3. An operator or orchestrator sent HUP

docker kill -s HUP <container> and kubectl exec <pod> -- kill -HUP 1 both send the signal directly. Kubernetes does not use SIGHUP by default; it sends the image's stop signal, SIGTERM unless STOPSIGNAL says otherwise. A sidecar or init script that sends HUP on config change can therefore terminate the main process if the handler is missing.

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.

Compose

docker-compose.yml

Docker stop sends SIGTERM by default, not SIGHUP, so a clean compose down should not produce 129. If it does, something inside the entrypoint or a sidecar is sending HUP; trace it with docker events and the entrypoint log.

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
Kubernetes

deployment.yaml

Kubernetes has no stable pod-level field for choosing the stop signal; it asks the runtime to send the image's STOPSIGNAL, which defaults to SIGTERM. A pod that exits 129 received HUP from a process inside the container, from kubectl exec, or from an image that sets STOPSIGNAL SIGHUP. Otherwise pod deletion produces 143 and then 137.

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"
Entrypoint

Reload on SIGHUP and keep the process alive

A handler changes 129 from a crash into an intended reload.

#!/bin/sh
set -eu
reload() {
  echo "SIGHUP received: reloading configuration"
  /usr/local/bin/app --reload-config
}
trap reload HUP
exec /usr/local/bin/app "$@"

Frequently asked questions

Why is SIGHUP exit code 129?

Shells report a signal termination as 128 plus the signal number. SIGHUP is signal 1, so 128 + 1 = 129. The same arithmetic gives 130 for SIGINT, 137 for SIGKILL and 143 for SIGTERM.

How do I prevent SIGHUP from killing a process?

Run it under a supervisor or detach it with nohup or setsid, and use docker run -d or a systemd service for long-lived workloads. If the process must handle reloads, install a SIGHUP handler instead of ignoring the signal; ignoring it removes the ability to reload in place.

Does docker stop send SIGHUP?

No. Docker sends the image's STOPSIGNAL, which defaults to SIGTERM, waits for the timeout and then sends SIGKILL. SIGHUP only appears if a process inside the container or an operator sends it explicitly with docker kill --signal HUP.

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