Killed for running out of memory
OOMKilled Exit Code: 137
What you see
$ kubectl describe pod orders-api-6b8f9c7d4-q7m2x -n shop
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Ready: False
Restart Count: 4
Limits:
memory: 512Mi
Requests:
cpu: 250m
memory: 512MiThe kernel's OOM killer stopped the container: usually because it reached its own memory limit, sometimes because the whole node ran out of memory. 137 is 128 + 9, the SIGKILL signal. The app gets no warning, so logs --previous usually just stops mid-stream.
Confirm
# How the last run ended, and the memory limit
kubectl get pod orders-api-6b8f9c7d4-q7m2x -n shop -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}{" "}{.spec.containers[0].resources.limits.memory}{"\n"}'
# Live usage per container (needs metrics-server); watch it climb before the kill
kubectl top pod -n shop -l app=orders-api --containersExit code 137 without OOMKilled is a SIGKILL from something else, most often the kubelet stopping a container that failed its liveness probe and didn't exit on SIGTERM in time (card 2). If the container was well below its limit, check kubectl describe node for a SystemOOM event: the node as a whole ran out of memory, and the fix is node sizing and requests that reflect real usage, not a higher limit.
Fix
- The limit is below what the app really needs: raise
resources.limits.memoryto the measured peak plus headroom. For important workloads set the memory request equal to the limit, so the node reserves all of it. - The runtime is sized past the limit: a JVM with
-Xmxequal to the limit goes over once threads, metaspace and buffers are added. Size the heap below the limit with-XX:MaxRAMPercentage=75, or cap Node.js with--max-old-space-size; the app then fails with its own out-of-memory error instead of a silent kill. A JVM that can't read cgroup v2 limits (JDK 8 older than 8u372, on Amazon Linux 2023 or Bottlerocket) sizes its heap from the node's memory, so upgrade it. - Memory grows until it dies: that's a leak, and a bigger limit only delays the next kill. Profile it or take a heap dump.
Prevent
- Set limits from measured usage (Container Insights, Prometheus
container_memory_working_set_bytes, or VPA recommendations), not guesses. - Alert on
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}from kube-state-metrics, so you see OOM kills before they become a crash loop.
Understand it
Whole container, not one process: on cgroup v2 nodes (Amazon Linux 2023, Bottlerocket), Kubernetes 1.28 and later kill every process in the container when it hits its limit. Earlier, only the largest process died, and a container whose worker process was killed could keep running half broken. Since 1.32 the kubelet setting singleProcessOOMKill: true brings the old behaviour back.