OOMKilled (exit code 137): fixing out-of-memory pods

By Hackademy. Updated . Output from a real Kubernetes v1.35 cluster.

The short answer

OOMKilled means the Linux kernel's out-of-memory killer stopped a container, almost always because it tried to use more memory than its resources.limits.memory. The exit code is 137: 128 plus 9, the number of SIGKILL, so the process had no chance to clean up or log anything. Kubernetes then restarts the container, and repeated kills turn into CrashLoopBackOff. The usual causes are a limit set below what the application really needs, a memory leak, a sudden spike in load or data size, and a runtime such as the JVM sizing its heap without regard to the container's limit. To diagnose it, run kubectl get pods to see the restarts, kubectl describe pod POD to read Last State Terminated, Reason OOMKilled, Exit Code 137, and kubectl top pods to measure real usage. Fix it by setting the limit from measured usage with headroom, for example with kubectl set resources, or by bringing the application's memory down.

Confirm the container was OOMKilled

This pod runs a busybox command that allocates about 300MiB, with a memory limit of 64Mi. kubectl get pods shows the reason straight in STATUS, and RESTARTS climbing:

$ kubectl get pod hog
NAME   READY   STATUS      RESTARTS      AGE
hog    0/1     OOMKilled   2 (23s ago)   25s

STATUS only shows the latest state, and between restarts it soon changes to CrashLoopBackOff. kubectl describe pod keeps the evidence in the container's Last State, beside the limit it broke:

$ kubectl describe pod hog
    Last State:     Terminated
      Reason:       OOMKilled
      Exit Code:    137
      Started:      Sun, 27 Sep 2026 20:14:41 +0100
      Finished:     Sun, 27 Sep 2026 20:14:41 +0100
    Ready:          False
    Restart Count:  2
    Limits:
      memory:  64Mi
    Requests:
      memory:     64Mi

Exit code 137 on its own only says the process received SIGKILL. Reason OOMKilled is what ties it to memory. A 137 without that reason has another source, such as a container killed after its termination grace period ran out.

Know what the limit and the request do

A container's memory has two settings, and they do different jobs:

  • The request is what the scheduler reserves. A pod is placed only on a node with that much unreserved memory, and it is what an autoscaler measures utilisation against.
  • The limit is a hard ceiling enforced by the kernel. Go over it and the container is killed. Memory, unlike CPU, cannot be throttled, so there is no slowing down first.
$ kubectl explain pod.spec.containers.resources
  limits	<map[string]Quantity>
    Limits describes the maximum amount of compute resources allowed. More info:
    https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/

  requests	<map[string]Quantity>
    Requests describes the minimum amount of compute resources required. If
    Requests is omitted for a container, it defaults to Limits if that is
    explicitly specified, otherwise to an implementation-defined value. Requests
    cannot exceed Limits. More info:
    https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/

A pod can also be killed when the node itself runs short of memory, even under its own limit. The kubelet then evicts pods, starting with those using the most memory beyond their requests, and the pod's status says Evicted rather than OOMKilled. Setting requests close to real usage protects a pod from that.

Measure real usage, then set the limit

Guessing a bigger number works until the next spike. Measure first. kubectl top pods reads current usage from the metrics server, so watch it under realistic load, not just at idle:

$ kubectl top pods
NAME                   CPU(cores)   MEMORY(bytes)
api-5657664566-2g8dz   1m           9Mi
api-5657664566-v2jh8   1m           9Mi

Then set the limit above the peak you saw, with headroom, and the request near typical usage. kubectl set resources changes the Deployment's pod template, which rolls out new pods with the new values:

$ kubectl set resources deployment api --limits=memory=256Mi
deployment.apps/api resource requirements updated
$ kubectl get pods -l app=api
NAME                   READY   STATUS    RESTARTS   AGE
api-5657664566-2g8dz   1/1     Running   0          15s
api-5657664566-v2jh8   1/1     Running   0          14s

Check RESTARTS over the following hours, not just the first minute. If usage keeps growing until the kill whatever the limit, the problem is a leak, and a bigger limit only delays it.

Make the application fit its limit

  • Size language runtimes from the container: modern JVMs read the cgroup limit, and -XX:MaxRAMPercentage sets the heap as a share of it. Leave room for memory outside the heap.
  • Stream large files and query results instead of loading them whole.
  • Bound caches and in-memory queues by size, not only by time.
  • Split memory-hungry batch work into a Job with its own, larger limit rather than raising the limit for every replica of a web service.

To practise the whole loop, the missions below give you an API whose memory limit is too tight and check that its pods stay up afterwards. They run in the browser with nothing to install.

Practise it on a simulated cluster

These missions in the Kubernetes Simulator break a cluster in this way and leave you to fix it. Free in your browser, nothing to install.

Start Out of memory

Questions

Why is the exit code 137?

When a process is ended by a signal, its exit code is 128 plus the signal number. SIGKILL is signal 9, so a container killed by the out-of-memory killer exits with 137. SIGKILL cannot be caught, which is why the application usually logs nothing about it.

Should I just remove the memory limit?

Without a limit a container can use as much memory as the node has, and a leak in one pod can then push the node into memory pressure and get other pods evicted. Keep a limit, set from measured usage, and treat repeated OOM kills as a signal to investigate.

Does OOMKilled always mean the container's own limit was hit?

Nearly always. If the node runs out of memory before the kubelet can evict pods, the kernel's out-of-memory killer can also pick a process in a container that was under its own limit. kubectl describe node and the node's memory pressure condition help tell the two apart.

AboutGuidesCompare

PrivacyCookiesTerms

Hackademy is a trading name of Mather Software Ltd, registered in England and Wales, company number 17443861. ICO registration ZC256628.

Contact: support@mathersoftware.com© 2026 Mather Software Ltd