A stopped container reports exit code 137, and another reports 143. Explain Docker's "128 + N" convention and say which signal produced each of those two codes.
answer
- Signal death → 128 + signal number
- 137 = 128 + 9 = SIGKILL
- 143 = 128 + 15 = SIGTERM
- 139 = SIGSEGV, 130 = SIGINT (Ctrl-C)
- Graceful shutdown that exits cleanly reports 0, not 143
basics
~20 sWhen a process is terminated by a signal rather than exiting on its own, the reported status is 128 plus the signal number. 137 = 128 + 9 = SIGKILL (force kill, often an out-of-memory kill). 143 = 128 + 15 = SIGTERM (a polite stop request the process did not survive).
solid answer
~60 sUnix reports signal deaths separately from normal exits, and the shell — and Docker, following the same convention — flattens that into a single byte as **128 + signal number**. So any container exit code above 128 usually means "killed by signal N", where N = code − 128. The two you see constantly: - **137 = 128 + 9 (SIGKILL)** — the process was force-killed and could not clean up. Common causes: the kernel's cgroup OOM killer hit the container's memory limit, `docker kill`, or `docker stop` timing out and escalating to SIGKILL. - **143 = 128 + 15 (SIGTERM)** — the process got the polite "please stop" signal and died on the default handler without catching it. That is what `docker stop` sends first. Others follow the same arithmetic: 130 = SIGINT (Ctrl-C), 139 = SIGSEGV (segfault), 134 = SIGABRT. Caveat: the rule is a convention, not a guarantee — an application is free to call `exit(137)` itself. For 137 specifically, confirm the cause with `docker inspect --format '{{.State.OOMKilled}}'`.
code
bash · 8 linesdocker ps -a --filter name=api --format '{{.Names}} {{.Status}}'
# api Exited (137) 4 minutes ago
docker inspect --format '{{.State.ExitCode}} oom={{.State.OOMKilled}} err={{.State.Error}}' api
# 137 oom=true err=
kill -l 9 # KILL
kill -l 15 # TERMgo deeper
Recall the arithmetic and the two headline codes: 137 = SIGKILL, 143 = SIGTERM, both mean the process was signalled rather than exiting itself.
Explain the convention's origin (signal deaths reported as 128+N), decode 130/134/139 too, and note that a clean SIGTERM handler yields 0 instead of 143.
Use the codes as triage input: 137 forks into OOM vs stop-timeout vs external kill, and you disambiguate with .State.OOMKilled, timestamps, and logs before concluding anything.
Treat exit codes as a platform-wide contract: standardise graceful shutdown so 0 means clean, 143 means "no handler", 137 means "we cut it off" — and make alerting distinguish them instead of paging on any non-zero.
## Two different ways a process can end On Linux a process can terminate in two distinct ways, and the kernel tracks them separately in the wait status the parent receives: 1. **Normal exit** — the program called `exit(n)` or returned from `main`. The status carries `n` (0–255). 2. **Killed by a signal** — some other process, or the kernel itself, sent a fatal signal such as SIGKILL or SIGTERM and the process died as a result. The status carries the *signal number*, plus a flag saying "this was a signal death". But the interfaces most people use — a shell's `$?`, a CI system's step status, Docker's `ExitCode` field — expose only a single small integer. So a convention was needed to squash both cases into one number. ## The 128 + N convention The convention, inherited from Bourne-shell behaviour and adopted by container runtimes, is: **a process killed by signal N reports status 128 + N**. Normal exits use 0–125 (with 126/127 also reserved by shells), leaving everything above 128 to mean "signal". Docker follows this, so `docker ps -a` shows `Exited (137)` and `docker inspect --format '{{.State.ExitCode}}'` returns 137. To decode any such code, subtract 128 and look up the signal number: | Code | Signal | Number | Typical meaning | |---|---|---|---| | 130 | SIGINT | 2 | Ctrl-C in an attached terminal | | 134 | SIGABRT | 6 | `abort()` — assertion failure, glibc heap corruption, JVM fatal error | | 137 | SIGKILL | 9 | Force kill; cannot be caught or ignored | | 139 | SIGSEGV | 11 | Segmentation fault — invalid memory access, often in native code | | 143 | SIGTERM | 15 | Polite termination request, default action is to die | (`kill -l` prints the full table on any Linux host.) ## Reading 143 143 means PID 1 received SIGTERM and let the **default disposition** kill it — it did not install a handler, or its handler still ended in a signal death. `docker stop` sends SIGTERM first (or whatever `STOPSIGNAL` sets), so 143 is the fingerprint of a container that stopped *because you asked it to* but did not run any shutdown code of its own. Is 143 bad? Not inherently. For a stateless process with nothing to flush, dying on SIGTERM is fine. It becomes a problem when the app had work to finish — draining in-flight HTTP requests, committing an offset, releasing a lock — because none of that ran. An application that catches SIGTERM, shuts down gracefully, and exits normally reports **0**, not 143. So the code is a useful signal about whether graceful shutdown exists at all. ## Reading 137 137 means SIGKILL. SIGKILL cannot be caught, blocked, or handled: the kernel removes the process immediately, with no chance to flush buffers or close connections. Three realistic sources: - **The kernel OOM killer** — the container hit its memory limit and the kernel killed the process inside the cgroup. Docker records this separately in `.State.OOMKilled`. - **`docker stop` escalation** — `docker stop` sends SIGTERM, waits for a grace period (10 seconds by default, `-t` to change), and if the process is still alive sends SIGKILL. A container that ignores or is too slow at SIGTERM therefore ends up at 137 rather than 143. - **An explicit kill** — `docker kill` (which sends SIGKILL by default), an operator's `kill -9`, or a supervising system forcing a stop. That ambiguity is why 137 alone is never a diagnosis. Check `docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container>`: `true 137` points at memory, `false 137` points at a stop that timed out or an external kill. ## Where the convention breaks down The rule is a convention about *reporting*, not a kernel-enforced fact, so two caveats matter: - **A program can return these values itself.** `exit(137)` from application code is legal and indistinguishable from a SIGKILL in the exit code alone. Corroborate with `OOMKilled`, timestamps, and logs. - **Exit statuses wrap at 256.** A program returning 300 reports 44. And a program returning, say, 143 deliberately looks exactly like a SIGTERM death. In practice, though, above-128 codes on containers are overwhelmingly signal deaths, and memorising 137/143 (plus 139 for segfaults) covers most incident triage you will ever do.
- Two containers both stop when you run `docker stop`. One reports 143 and the other 137. What is the difference?The 143 container died on SIGTERM's default disposition immediately. The 137 container ignored or outlived SIGTERM, so after the grace period (10 seconds by default, adjustable with `docker stop -t`) Docker escalated to SIGKILL and the process was force-killed. A 137 on stop usually means shutdown work is being cut off mid-flight, so either the app has no SIGTERM handler wired up or its shutdown takes longer than the timeout.
- You see exit code 139. What does it mean and what would you look at next?139 = 128 + 11 = SIGSEGV, a segmentation fault: the process accessed memory it was not allowed to. It is usually a bug in native code — a C/C++ library, a JNI binding, a runtime built for a different CPU architecture, or a corrupted binary. Next steps are the container logs and any core dump or crash file, plus checking the image architecture matches the host (an amd64 binary emulated on arm64 is a common cause).
The exit status is a one-line incident report with only one number of space. A normal exit writes the program's own verdict; a signal death writes 128 plus the signal, like a code prefix that says "this wasn't self-inflicted — here is who pushed."
saying these in an interview costs you the question
- Claiming 137 always means out of memory — `docker kill` and a `docker stop` timeout produce it too
- Believing an application can catch or handle SIGKILL so it gets a chance to clean up
- Thinking 143 proves graceful shutdown worked, when it actually means the default handler killed the process
- Not knowing the code decodes as 128 + N and treating each number as an unrelated magic value