On this page
  1. What are CPU and memory requests and limits?
  2. What happens when you set neither?
  3. How do I set them?
  4. Why does my pod get OOMKilled?
  5. Where AI coding assistants get this wrong
  6. Checklist
  7. FAQ
    1. What is the difference between OOMKilled and evicted?
    2. Should a CPU limit always be set?
    3. How do I find out how much memory my app actually uses?
  8. Related topics
  9. Sources
concept

Kubernetes Requests vs Limits: Why Your Pod Gets OOMKilled

Resource requests tell the scheduler what to reserve; limits tell the runtime when to throttle or kill. Setting them wrong causes OOMKills, throttling, and eviction.

Quick answer

  • A request is what the scheduler reserves for a container; a limit is the ceiling the runtime enforces.
  • Hitting a memory limit gets the container OOMKilled; hitting a CPU limit gets it throttled, which looks like slowness.
  • No requests means the scheduler is guessing, and the node can be oversubscribed until something is evicted.
  • No limits means one leaky container can starve every other workload on the node.
  • Set requests to normal usage and limits to the point where the app is genuinely broken, not to the smallest number that still boots.

What are CPU and memory requests and limits?

A request is a reservation: the scheduler only places your pod on a node with that much capacity free, and it shapes how much CPU your container is guaranteed. A limit is a ceiling enforced at runtime. For memory, exceeding the limit terminates the container with an OOMKill. For CPU, exceeding the limit throttles the container, which the user experiences as unexplained latency. Requests affect placement and fairness; limits affect behavior under load.

What happens when you set neither?

With no requests, the scheduler treats the pod as needing almost nothing and happily packs nodes beyond their real capacity. When memory pressure arrives, the kernel’s OOM killer picks a victim, and pods with no requests are first in line. With no limits, one process with a memory leak grows until it consumes the node and everyone else suffers. Generated manifests frequently include neither, which is why an app that is fine for weeks can fall over the day a second service is added.

How do I set them?

Start from measured usage, not from a guess. Requests should reflect typical steady-state consumption; limits should leave headroom above your observed peak so normal spikes do not trigger a kill. CPU is expressed in millicores (1000m = one core) and memory in bytes with suffixes such as Mi and Gi.

resources:
  requests:
    cpu: 100m      # 0.1 CPU reserved
    memory: 128Mi  # scheduler reserves this much
  limits:
    cpu: 500m      # throttled above half a core
    memory: 256Mi  # OOMKilled above this

Why does my pod get OOMKilled?

OOMKilled means the container’s memory usage exceeded its limit and the kernel terminated it. The usual causes are a limit set below what the app actually needs at peak, a genuine memory leak, or a single large request (a big upload, an unindexed query loading millions of rows) that briefly needs far more than steady state. Before raising the limit, check whether usage is climbing over time — that is a leak, and a bigger limit only buys time. Compare the observed working set against the limit, then decide which is wrong.

Where this bites vibecoders

Generated Deployments often omit the resources block entirely, because the manifest still applies and the pod still runs; the consequences show up later, on a shared node, as evictions and mysterious slowdowns. When an assistant does include limits, it tends to pick round numbers that are too low, producing OOMKills on the first real traffic spike, or sets limits equal to requests, which eliminates bursting and throttles the app under load. The right numbers come from watching actual usage, which no assistant can do for you.

Where AI coding assistants get this wrong

  • Omitting the resources block from generated Deployments.
  • Setting memory limits so low the app is OOMKilled under normal load.
  • Setting limits equal to requests, removing the ability to burst.
  • Confusing CPU throttling with a network or database problem.
  • Raising a memory limit to mask a leak instead of fixing the leak.

Checklist

  • Set requests and limits on every container.
  • Base requests on measured steady-state usage.
  • Leave memory headroom above observed peaks before setting limits.
  • Treat a rising memory curve as a leak, not a sizing problem.
  • Watch for evictions and throttling after any workload change.

FAQ

What is the difference between OOMKilled and evicted?

OOMKilled is the kernel killing a container that exceeded its memory limit. Evicted is the kubelet removing a pod from a node under memory pressure to protect the node. Both point at memory limits that are set too low or absent.

Should a CPU limit always be set?

Not necessarily. Without a CPU limit, a container can burst into idle cores, which is often desirable for latency-sensitive workloads. It still gets a guaranteed share based on its request. Many teams set CPU requests and omit CPU limits to avoid throttling.

How do I find out how much memory my app actually uses?

Check kubectl top pod for current usage and your monitoring for the peak over a full traffic cycle, including deploy days and batch jobs. Size limits against the peak, not the average, or you will be OOMKilled exactly when traffic is highest.

Sources

Share: