Guide
What does Docker exit code 137 mean, and was it out of memory?
137 is 128 plus 9: the main process was killed with SIGKILL. The kernel's out-of-memory killer is one sender, and `docker stop` running out of patience after 10 seconds is the other. They need opposite fixes, and the obvious check, `OOMKilled`, is wrong in two common cases.
What does Docker exit code 137 mean?
Exit code 137 means the container's main process was killed with SIGKILL (128 plus signal 9). Either the kernel killed it for exceeding its memory limit, or docker stop sent SIGKILL because the process ignored SIGTERM for the 10-second grace period. Check docker inspect -f '{{.State.OOMKilled}}' <name>, and if a stop took exactly ten seconds, it was the timeout. A memory kill also leaves a Memory cgroup out of memory: Killed process line in the kernel log.
Docker exit code 137 means the container's main process was killed with SIGKILL. Bash reports a process ended by signal N as 128 plus N, Docker reports the same number, and SIGKILL is signal 9. It does not by itself mean out of memory. Two things send SIGKILL to a container: the kernel's OOM killer when the container hits its memory limit, and Docker itself when docker stop has waited 10 seconds for a process that ignored SIGTERM. Run docker inspect -f '{{.State.OOMKilled}}' <name> first, but read the two cases below before you trust its answer.
Two answers circulate, and the common one is half right#
Most answers say 137 is an out-of-memory kill and tell you to raise the limit. Docker's resource constraints page does say that when memory runs out "the kernel kills processes in a container".
The other sender is more common on a home server, because it fires on every stop, every docker compose down and every update. Docker's stop reference is exact: "The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL", and the default grace period "is 10 seconds for Linux containers". A process that never reacts to SIGTERM is killed at second ten and exits 137. Raising the memory limit changes nothing, and the real cost is a process that never shuts down cleanly.
What each way of stopping a container reports, measured#
We ran each case on Docker Engine 28.1.1 with Compose 2.35.1, Linux 5.4, cgroup v1, using stock alpine, python:3.12-alpine and nginx:1.27-alpine images.
| What happened | Exit code | Time to stop | OOMKilled |
|---|---|---|---|
docker stop, PID 1 is sleep | 137 | 10.25 s | false |
docker stop, PID 1 is a Python script | 137 | 10.32 s | false |
docker stop -t 3, same sleep | 137 | not timed | false |
docker stop, same sleep with --init | 143 | 0.23 s | false |
docker stop, shell that traps TERM and exits 0 | 0 | 1.34 s | false |
docker stop, nginx (image sets STOPSIGNAL SIGQUIT) | 0 | 0.49 s | false |
docker kill | 137 | immediate | false |
kill -9 on the container's PID from the host | 137 | immediate | false |
PID 1 exceeds -m 64m | 137 | at the limit | true |
A child process exceeds -m 64m, PID 1 survives | none, still running | true |
Five of the six 137s in that table had nothing to do with memory. The tell for a stop timeout is the duration: a stop that takes the full grace period to the tenth of a second was a SIGKILL from Docker. Compose shows the code and not the cause: an OOM-killed service printed hog-1 exited with code 137 in the foreground, and both an OOM kill and a stop timeout read Exited (137) in docker compose ps -a.
docker kill -s TERM on the sleep container did nothing at all; it was still running a second later. That is the root of the stop timeout. The run reference says "a process running as PID 1 inside a container is treated specially by Linux: it ignores any signal with the default action". sleep and a Python script have no SIGTERM handler, so as PID 1 the signal is simply dropped.
When OOMKilled lies, in both directions#
Moby's source shows what the flag records. In daemon/monitor.go, the runtime's OOM event sets c.OOMKilled = true and logs an oom event. In container/state.go, starting a container sets s.OOMKilled = false. Both produce wrong answers.
It says true when the 137 was a stop timeout. The OOM event is raised when any process in the container's cgroup is killed, not only PID 1. Our child-process test kept running with OOMKilled true, its shell printing child=137. When we then ran docker stop, the shell ignored SIGTERM, Docker killed it at ten seconds, and inspect reported ExitCode 137 with OOMKilled true. The flag was describing a different process from earlier.
It says false when the container was OOM-killed and restarted. With restart: unless-stopped, the container we limited to 64 MB was killed and restarted twice in eight seconds. Inspected while running, it showed ExitCode 0, OOMKilled false and RestartCount 2. The restart wiped both fields. With a restart policy on every service, as Docker Compose conventions recommends, a container crash-looping on memory looks healthy to docker inspect.
Three checks that settle it#
docker inspect -f '{{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}} {{.State.FinishedAt}}' app
journalctl -k --since today | grep -i 'out of memory'
docker events --since 1h --until 0s --filter event=oom --filter event=dieThe kernel log survives a restart. A container limit kill reads Memory cgroup out of memory: Killed process 2321668 (python) total-vm:1394176kB, anon-rss:60740kB, which names the process and its resident memory when it died. If the name is a child, such as a worker or a transcoder, you are in the first case above. If there is no line from around FinishedAt, the kernel did not kill it, and the 137 came from Docker or from someone's kill.
docker events --since replays recent events from the daemon, and each sender leaves a different trail. Our OOM kills produced oom followed by die with exitCode=137. A stop timeout produced kill with signal=15, kill with signal=9 ten seconds later, then stop and die. A kill -9 from the host produced only die, with no kill event at all. On cgroup v2 hosts the kernel also keeps a counter: the container's memory.events file has an oom_kill field, which the kernel documentation defines as "the number of processes belonging to this cgroup killed by any kind of OOM killer". We did not test cgroup v2.
Fix a stop timeout: let the process hear SIGTERM#
Make SIGTERM reach something that acts on it:
- Shell form or a wrapper script. The Dockerfile reference warns that with shell form, the executable "will not be the container's
PID 1, and will not receive Unix signals". If you control the entrypoint script, end it withexec "$@"orexec /usr/bin/appso the program replaces the shell. - An application with no handler as PID 1. Add
init: trueto the service. Compose describes it as "an init process (PID 1) inside the container that forwards signals and reaps processes". In our test it turned a 10.25-second stop into 0.23 seconds. - An application that shuts down on a different signal. Set
stop_signal. nginx's image already declaresSIGQUIT, which is why it exited 0 in half a second. - An application that needs longer than 10 seconds to shut down cleanly. Raise
stop_grace_period, which Compose documents as defaulting to 10 seconds. A database flushing to disk is the usual case.
services:
app:
image: example/app:1.4.2
init: true
stop_grace_period: 30sDo not lower the grace period to make updates faster. A database killed mid-shutdown is a database that skipped its clean shutdown, and the next start relies on its crash recovery. Backing up a running database explains why the same thing makes copying a running database's files unsafe.
Fix a real OOM kill: measure the peak, then set the limit#
A confirmed memory kill means the limit was below the container's peak. docker stats --no-stream is a snapshot, usually taken when nothing is happening; the peak is the photo import, the library scan or the nightly job. Record memory over a day with something from The minimum viable monitoring stack; Beszel vs Netdata covers two light options that both record memory over time.
Then set the limit above that peak, and check the host can afford it. Stack planner adds services up at ordinary use rather than idle, which is the figure a limit has to clear. A local model in Ollama is the usual example, and GPUs, transcoding and local AI covers how the quantization you pull sets how much memory it needs.
Two things not to do. Do not set oom_kill_disable to stop the kills. Docker's own page says to disable the OOM killer only on containers "where you have also set the -m/--memory option". And do not remove the limit to make the error go away. Without one, the container is competing with the whole host for memory, and the kernel picks a victim from everything running on it.
Other exit codes you will see next to 137#
| Code | Meaning | Usual cause |
|---|---|---|
| 0 | Exited on its own, successfully | Clean shutdown on its stop signal |
| 1 | Application error | Read docker logs; often bad config |
| 125 | Docker could not run the container | Docker's docs: "the error is with Docker daemon itself" |
| 126 | Command found but not executable | Missing execute bit on an entrypoint script |
| 127 | Command not found | Wrong path in command: or a shell missing from a slim image |
| 130 | SIGINT, 128 plus 2 | Ctrl+C on an attached container |
| 139 | SIGSEGV, 128 plus 11 | The program crashed |
| 143 | SIGTERM, 128 plus 15 | Died of the stop signal; a clean, fast stop |
Apart from 125, 126 and 127, Docker's run reference says every code is "the exit code of the provided container command".
When this answer stops applying#
- Host-wide out of memory. We tested only a container hitting its own limit. A host that runs out of memory overall chooses a victim across all its processes, and we did not measure what Docker records in that case.
- cgroup v2 and Engine 29. The measurements above are from cgroup v1 on Engine 28.1.1. The source lines quoted are from that release.
- Docker Desktop, Podman and Kubernetes. None were tested. Docker vs Podman covers how Podman differs.
- A 137 from inside the container. A supervisor or an application's own watchdog can send SIGKILL to PID 1. The kernel log will show no OOM line and Docker will show no
killevent.
What to do next#
Look at how long the last stop took. If it was ten seconds, add init: true or an exec. If the kernel log names a process, measure that container's peak and size the limit to it, and keep a memory history so the next kill is visible after a restart hides it. An update strategy that does not lose data covers why updates are when most of these surface.
Questions#
Does exit code 137 always mean out of memory?
No. It means SIGKILL, and Docker sends SIGKILL itself whenever a container does not exit within its stop timeout, 10 seconds by default. In our tests a plain sleep or a Python script as PID 1 exited 137 after 10.3 seconds of docker stop, with OOMKilled false. docker kill and kill -9 from the host give 137 too. Only the OOMKilled flag, a Docker oom event or a kernel log line shows it was memory.
What is the difference between exit code 137 and 143?
143 is 128 plus 15, SIGTERM: the process received the polite stop signal and died of it, which is a clean, fast stop. 137 is SIGKILL, which cannot be caught. With --init, our sleep container stopped in 0.23 seconds with 143; without it, the same container took 10.25 seconds and exited 137. A container that handles SIGTERM and exits on purpose usually reports 0.
Why does my container take 10 seconds to stop?
Its main process is ignoring SIGTERM, so Docker waits the full grace period and then sends SIGKILL. The usual causes are an application that is PID 1 with no signal handler, since Linux gives PID 1 no default signal behavior, or a shell-form CMD or entrypoint script that does not exec the real program. Add init: true, switch to exec form, or put exec before the final command in the script.
Why does docker inspect say OOMKilled false after an out of memory crash?
Because the container restarted. Docker resets the flag to false every time a container starts, so with restart: unless-stopped the evidence is gone within seconds. In our test, a container killed twice in eight seconds showed ExitCode 0 and OOMKilled false, with only RestartCount 2 left as a clue. Check the kernel log or docker events --filter event=oom instead.
How do I see how much memory a container uses before it is killed?
docker stats --no-stream shows current usage against the limit for every running container. That is a snapshot, so for a container that dies at a peak, record memory over time with a monitoring tool, then set the limit above the peak you observed, not above the idle figure.
Sources#
- GNU Bash manual, exit status (128 plus N for a fatal signal)
- Docker docs, docker container stop (SIGTERM, 10-second grace period, SIGKILL)
- Docker docs, docker container run (PID 1 signal behavior, --init)
- Docker docs, running containers (exit codes 125, 126 and 127)
- Docker docs, resource constraints (OOM killer, --memory)
- Dockerfile reference (STOPSIGNAL, shell and exec form)
- Compose file reference, services (stop_grace_period, stop_signal, init, mem_limit)
- Moby source, OOM event handling (daemon/monitor.go, v28.1.1)
- Moby source, container state and OOMKilled reset (container/state.go, v28.1.1)
- Linux kernel docs, cgroup v2 memory.events
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.