Exit Code 137: What It Means and How to Fix It
Exit code 137 means the process was killed with SIGKILL, the one signal no program can catch, block, or clean up after. The arithmetic is fixed: a process terminated by a signal exits with 128 plus the signal number, and SIGKILL is signal 9, so 128 plus 9 gives 137. In Docker and Kubernetes, the sender of that SIGKILL is almost always the kernel's out-of-memory killer, which means the practical translation of exit code 137 is simple: the container tried to use more memory than it was allowed.
The 128-plus-signal rule is worth keeping, because it decodes the whole family at a glance. 137 is SIGKILL, forced termination. 139 is 128 plus 11, a segmentation fault. 143 is 128 plus 15, SIGTERM, the polite shutdown request that well-behaved processes obey during normal deploys. Seeing 143 in your logs is a process ending correctly; seeing 137 is a process being executed. This article covers how to confirm the memory verdict, why containers hit their limits, the fixes in order of durability, and the second, sneakier source of 137s that has nothing to do with memory at all.
Confirming it was the OOM killer
Do not assume; the platforms record the verdict. In Docker, run docker inspect on the dead container and read the State section: the OOMKilled field is a plain true or false, next to the exit code itself. In Kubernetes, run kubectl describe pod on the affected pod and read the last state of the container: a memory kill shows the reason OOMKilled alongside exit code 137, and the restart count tells you how often it has been happening. On the host itself, dmesg records the kernel's own account, lines naming the out-of-memory killer, the victim process, and the cgroup whose limit was breached.
If OOMKilled says false, note the second source before hunting ghosts: an orchestrator's own stop sequence ends in SIGKILL. When Docker stops a container, it sends SIGTERM and waits a grace period, ten seconds by default, before escalating to SIGKILL; Kubernetes does the same with a thirty-second default during pod termination. A process that ignores SIGTERM, or takes longer to shut down than the grace period allows, exits 137 on every ordinary deploy. The fix for that variant is application manners, handle SIGTERM, finish fast, or lengthen the grace period, not memory.
Why containers run out of memory
The obvious cause is the honest one: the workload simply needs more than the limit grants, either steadily or in bursts, and limits set by guesswork months ago no longer fit the traffic or the data. The second cause is the leak, a process whose usage climbs monotonically until any limit, however generous, is eventually breached; the tell is a sawtooth memory graph, rising to the kill, dropping at the restart, rising again.
The third cause deserves special mention because it is a trap with a decade of victims: runtimes that size themselves by the machine rather than the container. The JVM is the canonical example. An unconfigured JVM decides its heap from what it believes the available memory to be, and misjudging the container's cgroup limit ends in the kernel killing the whole process from outside, which is exit code 137 arriving where you expected a catchable Java error. Modern JVMs respect container limits when told, and the flag MaxRAMPercentage exists to set the heap as a fraction of the container's allowance, leaving room for everything outside the heap. The mechanics of heap exhaustion and its tuning live in Odown's guides to the Java heap space error and garbage collection in Java, which pair naturally with this page for JVM workloads.
The fixes, in order of durability
Right-size the limit from evidence. Watch the container's actual memory use through docker stats or your cluster metrics across a real traffic cycle, then set the limit above the observed peak with honest headroom. In Kubernetes, set requests to typical use and limits to the tolerable ceiling, and revisit both when traffic or data grows.
Fix the leak instead of feeding it. If usage climbs without a plateau, more memory only lengthens the interval between kills. Profile the process, find what accumulates, and fix retention; the sawtooth graph flattens the day the leak dies.
Configure the runtime for its container. Give the JVM MaxRAMPercentage, cap worker counts and per-worker memory in Python and Node services, and bound caches explicitly. A runtime that believes it owns the host will spend the host.
Respect the shutdown, and lengthen it if needed. Handle SIGTERM, close work quickly, and if a clean shutdown genuinely needs more than the default grace period, extend it in the stop timeout or the pod's termination settings so deploys end in 143, not 137.
Watch the node, not just the pod. On a crowded node, memory pressure can trigger kills and evictions even when each container is inside its own limit. Cluster-level headroom is part of the fix, not a separate topic.
Common mistakes in fixing exit code 137
Raising the limit as a reflex. Sometimes correct, often a bandage on a leak. Read the memory graph first; the shape tells you whether the process needs more or keeps more.
Skipping the OOMKilled check. The inspect and describe commands answer the memory question definitively in ten seconds. Debugging without them means guessing between two unrelated causes.
Forgetting the deploy-time variant. A service that exits 137 only during rollouts is failing to honor SIGTERM within the grace period. That is a shutdown-handling bug, and no memory setting will touch it.
Letting the JVM size itself. Default heap sizing inside a container is a countdown. Set the percentage flag explicitly and leave real room for off-heap memory, threads, and the runtime itself.
Treating restarts as recovery. Orchestrators restart killed containers so smoothly that a service can be dying hourly while dashboards stay green. Rising restart counts are an incident in slow motion; alert on them.
FAQ
Does exit code 137 always mean out of memory?
No, but usually. It means SIGKILL; the two common senders are the kernel's OOM killer and an orchestrator escalating after an ignored SIGTERM. The OOMKilled flag in docker inspect or kubectl describe tells you which in seconds.
What is the difference between exit codes 137 and 143?
143 is SIGTERM, a graceful shutdown the process obeyed; 137 is SIGKILL, a forced execution. Deploys should produce 143. A rollout producing 137 means the process is not shutting down within its grace period.
How do I see why Kubernetes killed my pod?
Run kubectl describe pod and read the container's last state: OOMKilled with exit code 137 is the memory verdict, and the restart count shows the frequency. The node's dmesg holds the kernel's own record of the kill.
Can I catch or handle SIGKILL in my application?
No, by design. SIGKILL cannot be trapped, which is exactly why the durable fixes are upstream: sane limits, fixed leaks, container-aware runtimes, and clean SIGTERM handling so the kill never has to happen.
Closing thought
Exit code 137 is arithmetic with a verdict inside: 128 plus 9, a SIGKILL, and in containers that verdict nearly always reads out of memory. The investigation is short because the platforms keep receipts, an OOMKilled flag, a describe output, a dmesg line, and the fixes rank cleanly: evidence-based limits, dead leaks, runtimes configured for their cgroups, and shutdowns polite enough to end in 143.
The failure mode that actually hurts teams is not the kill but the camouflage: orchestrators restart the victim instantly, so a memory problem can cycle for weeks behind green dashboards, surfacing only as mysterious slow moments and dropped requests. Odown watches your services from the outside, catching the availability blips restarts cause and the response-time degradation that precedes them, and its guide to Docker container monitoring covers the container-side signals, memory trends and restart counts included, that turn exit code 137 from a recurring surprise into a graph you saw coming.



