Coding Agents & Terminal · recorded 2026-08-31 · applies to 1 tool(s) ✓ Verified

Problem: container process killed suddenly (OOMKilled / exit code 137) — memory limits and troubleshooting —— Trove problem library

Verified 2026-08-31container killed with exit 137 — check `docker inspect` OOMKilled: true = raise memory limits; false = killed by someone else.

Quick fix

# 1. Confirm it was OOM
docker ps -a --filter "status=exited"          # Exited (137)
docker inspect <container> --format "{{.State.OOMKilled}} {{.State.ExitCode}}"
# true 137 = OOM; false 137 = killed externally

# 2. See actual memory usage
docker stats --no-stream                      # MEM USAGE / LIMIT columns

# 3. Kernel-side evidence (optional)
dmesg | grep -i "oom-kill" | tail
# compose: set a limit + crash-restart fallback
services:
  app:
    image: your-image
    mem_limit: 512m          # or deploy.resources.limits.memory: 512M
    restart: unless-stopped  # restarts after OOM (but repeated restarts = fix the root cause)

What you will run into

A process inside a container dies mid-run for no visible reason: docker ps -a shows Exited (137), docker inspect reports State.OOMKilled=true;

the service "randomly" restarts or disappears, and the logs are empty (the kernel SIGKILLs the process, so it never gets to log anything).

The container exceeded its cgroup memory limit and the kernel OOM killer took it out — this is not the host running out of RAM, it is the container's own quota running out.

Why this happens

docker run -m 64m (compose mem_limit / deploy.resources.limits.memory) sets a cgroup memory cap;

when the process's cumulative memory (including attributed page cache) exceeds the cap, the kernel OOM killer picks a process inside that cgroup and kills it — the killed container's exit code is 137 (128 + SIGKILL 9).

A container without -m has no memory cap and can eat the whole host, taking other services down with it.

Docker implements the cap with cgroup v2 memory.max (reproduced: inside a -m 128m container, cat /sys/fs/cgroup/memory.max = 134217728).

On OOM the kernel records oom-kill:constraint=CONSTRAINT_MEMCG,...task=python3 (dmesg verbatim, reproduced) — this is the deterministic signature of a cgroup-level OOM, distinct from host-wide memory pressure (constraint=CONSTRAINT_NONE).

Two defaults worth remembering: --memory-swap defaults to --memory × 2 (i.e. the container can use up to 2x its memory limit of swap; -1 = unlimited swap), and docker stats's MEM USAGE / LIMIT columns are the observation entry point.

The troubleshooting chain is deterministic: docker ps -a shows 137 → docker inspect <container> --format "{{.State.OOMKilled}}" confirms → docker stats quantifies usage → dmesg | grep oom gives the kernel record.

Deterministic takeaway: exit 137 is not always OOM (a manual kill -9 also gives 137) — check the OOMKilled field before concluding;

for runtimes with their own memory pools (Java/Python/Node), size the container limit by "process RSS + runtime overhead", not by the app heap config alone.

Step by step

1. Confirm OOMKilled — Must do

docker inspect <container> --format "{{.State.OOMKilled}} {{.State.ExitCode}}"

true 137 → cgroup OOM (this pitfall); false 137 → external kill -9 or platform reclamation, look elsewhere.

2. Quantify memory usage — Must do

docker stats --no-stream <container>
# MEM USAGE / LIMIT: usage close to LIMIT = quota too small

3. Set/adjust the limit — By diagnosis

  • No -m set: give it a sane cap first (-m 512m / compose mem_limit) so one container cannot eat the host;
  • Limit set but still OOM: raise it, or fix the root cause (step 4);
  • Swap: --memory-swap defaults to --memory × 2; --memory-swap -1 = unlimited swap (be careful: can exhaust host swap).

4. Root cause: the process's memory model — By diagnosis

Java: -Xmx covers the heap only; the container limit must add metaspace/thread stacks/direct memory (common approach: -XX:MaxRAMPercentage=75); Python: large one-shot allocations (bytearray, caches — reproduced: a 256MB allocation dies at a 64m limit);

Node: --max-old-space-size.

Rebuild the container after fixing code/args.

5. Verify — Must do

docker run --rm -m 128m your-image your-app     # runs steadily
docker stats --no-stream                        # usage well below LIMIT
docker inspect <container> --format "{{.State.OOMKilled}}"   # false

How to confirm it works

docker ps shows the container steadily Up (no more repeated Exited 137); docker inspect reports OOMKilled=false; docker stats shows headroom below the limit (reproduced: 128m limit, 96MB allocation → 100.1MiB / 128MiB 78.22%).

Other environments

ScenarioBehavior
No -m limitContainer can exhaust host RAM and drag down other services (no cap by default)
-m 64m + 256MB allocationProcess killed: exit 137, OOMKilled=true, dmesg records constraint=CONSTRAINT_MEMCG (reproduced)
--memory-swapDefaults to memory × 2; -1 = unlimited swap; larger values buffer peak usage
exit 137 but OOMKilled=falseExternal kill -9 (e.g. platform reclamation), not this pitfall

Symptom

$ docker ps -a
oomdemo2   ...   Exited (137) 4 seconds ago

$ docker inspect oomdemo2 --format "{{.State.OOMKilled}} {{.State.ExitCode}}"
true 137

# kernel side (dmesg excerpt)
oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=docker-...scope,task=python3,pid=24719,uid=0
Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB

Signature: container exits 137 with OOMKilled=true; no error in the app log (SIGKILL bypasses the app); docker stats usage is pinned at LIMIT.

Cause

The container's cumulative memory exceeded the cgroup (memory.max) cap, so the kernel OOM killer killed a process inside the cgroup; exit code = 137 (SIGKILL). No -m means no cap. --memory-swap defaults to 2×--memory.

Solution

  1. docker inspect to confirm OOMKilled=true;
  2. docker stats to quantify usage; dmesg | grep oom-kill for the kernel record;
  3. Set/raise -m (compose mem_limit); tune --memory-swap if needed;
  4. Fix the root cause per runtime model (Java MaxRAMPercentage / Node max-old-space-size / Python large allocations) and rebuild;
  5. Verify OOMKilled=false and stats headroom.
Verification Evidence
  • Local reproduction (2026-08-31, custom prebuilt WSL, docker 29.1.3):
  • Baseline: unlimited container allocates 256MB successfully (alloc ok 268435456);
  • -m 64m + 256MB allocation → process killed; docker run exit code 137; docker inspect → OOMKilled=true ExitCode=137;
  • dmesg verbatim: oom-kill:constraint=CONSTRAINT_MEMCG,...,task=python3,pid=24719 + Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB;
  • -m 128m container: cat /sys/fs/cgroup/memory.max = 134217728 (128MiB); docker stats shows 100.1MiB / 128MiB 78.22%;
  • --memory-swap default = 2×memory per docker help docs.

Evidence details kept locally (2026-08-31)

Related canonicals

Applicable tools (1)

This problem is linked from the tool pages below — reachable from either side.

docker

Source & evidence

Recorded Verified ✓ VerifiedApplies to 1 tool(s)
Entries contain locally measured or reviewable-source solutions; unmeasured items are explicitly labeled.

← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk