Two numbers in a Kubernetes manifest quietly decide whether your service is fast, throttled, or dead: the resource request and the limit. They look like a matched pair — a floor and a ceiling — but they do fundamentally different jobs, and treating them as interchangeable is the single most common reason pods mysteriously slow down or get killed in production. Worse, CPU and memory behave in opposite ways when you hit a limit. Getting this right is one of the highest-leverage things you can learn about running workloads on Kubernetes.
Requests and limits do two different jobs
A request is a reservation. It is what the scheduler uses to decide which node a pod lands on — the node must have at least the requested amount free. Critically, a request is not a cap. A container that requests 500m of CPU can happily burn 2 full cores if the node has spare capacity. The request also acts as a floor under contention: when multiple pods compete, the Linux scheduler hands out CPU time in proportion to their requests. Three pods requesting 200m, 400m, and 200m get CPU in a 1:2:1 ratio when the node is saturated.
A limit is the hard ceiling — the maximum a container is ever allowed to consume. This is where the two resource types split, because the kernel enforces those two ceilings in completely different ways.
CPU is compressible: you get throttled, not killed
CPU is a compressible resource — the kernel can simply give a container less of it. Kubernetes translates a CPU limit into two values for the Linux CFS (Completely Fair Scheduler): a quota and a period. The period defaults to 100 ms (100,000 microseconds), and the quota is your limit's share of it. A 500m CPU limit becomes 50,000 microseconds of quota per 100 ms period — the container may use 50 ms of CPU time every 100 ms window.
Here is the footgun: once a container burns its quota for the current period, it is throttled — frozen until the next period begins — even if the node has idle CPU sitting right there. Throttling doesn't crash anything, but it produces exactly the symptom teams chase for days: p99 latency spikes and sluggish requests with no obvious cause, because the container spent the back half of each 100 ms window paused. This is why many practitioners set CPU requests to guarantee a proportional share but deliberately omit CPU limits, letting workloads burst into spare capacity instead of being throttled against an artificial cap.
Memory is not compressible: you get OOMKilled
Memory works the opposite way. You cannot "throttle" memory — a process either has the bytes it needs or it doesn't. So when a container exceeds its memory limit, the kernel's OOM killer simply terminates the process. In Kubernetes this surfaces as an OOMKilled container status, usually with exit code 137. There is no graceful slowdown; the process is gone and the pod restarts.
This asymmetry drives a common rule of thumb: always set memory requests and limits, and set them equal. Memory has no burst headroom worth gambling on — under-provisioning just gets you killed, and setting request equal to limit makes your footprint predictable and your pod's quality-of-service class stronger (more on that next).
# Illustrative — the common production pattern
resources:
requests:
cpu: "250m" # scheduling reservation + proportional share
memory: "512Mi" # reserve exactly what you need
limits:
# cpu intentionally omitted to avoid CFS throttling
memory: "512Mi" # equal to request: predictable, no surprise OOM headroomQoS classes decide who dies first
How you set requests and limits also silently assigns each pod a Quality of Service class, which determines eviction order when a node runs out of memory:
- Guaranteed — every container sets CPU and memory requests equal to their limits. Least likely to be evicted.
- Burstable — at least one container has a request or limit, but not the full equal-everywhere setup. Can burst above its request, but isn't protected under pressure.
- BestEffort — no requests or limits set anywhere. Uses whatever is free, and is the first to be evicted when the node is squeezed.
When a node comes under memory pressure, the kubelet evicts in that order: BestEffort first, then Burstable, and Guaranteed last. If a pod matters, giving it explicit, equal memory requests and limits doesn't just make its footprint predictable — it moves it to the back of the eviction line.
Key Takeaways
- Requests reserve and schedule; limits cap. A request is a floor and a proportional share, not a maximum.
- CPU limits throttle via CFS — a 500m limit is 50 ms of CPU per 100 ms period, and you can be throttled even with idle CPU available.
- Memory limits kill — exceed one and the OOM killer terminates the process (
OOMKilled, exit 137); memory can't be throttled. - Common pattern: set CPU requests but often skip CPU limits; always set memory request equal to limit.
- Requests and limits assign a QoS class (Guaranteed/Burstable/BestEffort) that decides eviction order under node pressure.
