Kubernetes Resource Requests vs Limits
Requests reserve CPU and memory for scheduling; limits cap what a container can use. Getting the two wrong causes throttling or evictions.
Resource requests tell the Kubernetes scheduler how much CPU and memory a container needs in order to place it on a node; resource limits cap how much it’s allowed to actually use once running. They’re set together in a pod spec, but they do genuinely different jobs — requests are a scheduling promise, limits are a runtime ceiling — and confusing the two is one of the most common causes of both wasted cluster capacity and mysterious pod failures.
What requests actually do
When you set a CPU or memory request, you’re telling the scheduler: reserve at least this much on whichever node this pod lands on. The scheduler sums up the requests of everything already running on a node and only places a new pod there if the node has enough unreserved capacity left, similar in spirit to how Kubernetes deployments declare what a workload needs before the control plane decides where it runs.
Requests don’t limit anything at runtime by themselves — a container can burst above its CPU request if the node has spare cycles available, and Kubernetes will let it. What requests do determine is priority under contention: if a node runs out of memory, pods using more than their memory request are evicted before pods staying within their request, and the Quality of Service class Kubernetes assigns a pod (Guaranteed, Burstable, or BestEffort) is derived directly from how its requests and limits compare.
What limits actually do
A limit is a hard ceiling enforced by the container runtime. The two resource types are enforced very differently, and that difference is the part most people get wrong:
- CPU limits are throttled, not killed. If a container tries to use more CPU than its limit, the kernel’s CFS (Completely Fair Scheduler) bandwidth controller throttles it — the process keeps running but gets fewer CPU cycles per scheduling period. This shows up as latency spikes and slow responses, not crashes, which makes it easy to miss in basic health checks.
- Memory limits are enforced by killing the process. Memory can’t be throttled the way CPU can — there’s no way to “slow down” a process that’s allocating more RAM. When a container exceeds its memory limit, the kernel’s OOM killer terminates it, and Kubernetes reports the pod as
OOMKilled. This interacts directly with liveness and readiness probes: a container killed for memory shows up as a restart, not a probe failure, so the two failure modes need to be diagnosed differently.
Requests vs limits at a glance
| Requests | Limits | |
|---|---|---|
| Used by | Scheduler, for node placement | Container runtime, at run time |
| Enforcement | Not enforced directly | Hard cap, actively enforced |
| CPU over the value | Not possible to request more than available | Throttled (process slows, doesn’t crash) |
| Memory over the value | Not possible to request more than available | Process killed (OOMKilled) |
| Effect of setting too low | Scheduler under-provisions the node | Frequent throttling or kills |
| Effect of setting too high | Wasted, unusable cluster capacity | Node can be oversubscribed if many pods burst at once |
Common misconfigurations
- No requests set at all. The pod becomes BestEffort QoS and is the first thing evicted under any node pressure, even if it’s actually important. Every production workload should set at least a request.
- Limits set far above requests, with many pods doing this on the same node. Each individual pod’s request looks fine, but if they all burst toward their limits simultaneously, the node runs out of real capacity — horizontal and vertical autoscaling can help absorb this, but it doesn’t eliminate the underlying oversubscription risk.
- CPU limits set too tight on latency-sensitive services. Since CPU limits throttle rather than crash, a too-tight limit produces intermittent slow responses that are easy to misdiagnose as an application bug rather than a resource ceiling. Many teams deliberately leave CPU unlimited (request only) for latency-sensitive workloads and rely on requests plus node-level capacity planning instead.
- Memory requests and limits set to the same value (“Guaranteed” QoS) without headroom. This maximizes scheduling priority but leaves zero slack for legitimate memory growth, so any minor leak or traffic spike triggers an
OOMKilledrestart instead of graceful degradation.
Setting sane values
Start from actual observed usage, not guesses — metrics from a staging environment or a canary rollout under real traffic are a far better starting point than picking round numbers. Set memory requests and limits close together, since memory can’t be throttled and a container killed mid-request is worse than one that’s merely slow. Give CPU more headroom between request and limit, since CPU overuse degrades gracefully via throttling rather than crashing the process. And revisit both numbers periodically — a workload’s real resource footprint drifts as code changes, and static values set once at launch tend to become wrong.
The takeaway
Requests are what the scheduler uses to decide where a pod fits; limits are what the runtime enforces once it’s running there. CPU limits throttle, memory limits kill — that asymmetry is the single most important thing to internalize, because it changes how you diagnose a slow pod versus a crashing one. Set both from real usage data, give memory tighter headroom than CPU, and never ship a production workload with no requests set at all.
Tagged
Keep reading
Chisato · · 4 min read Kubernetes Init Containers Explained
Init containers run to completion before a pod's main containers start, making them the standard way to handle setup steps and startup ordering.
Chisato · · 4 min read Kubernetes Service Types: ClusterIP vs NodePort vs LoadBalancer
Kubernetes offers four Service types for exposing pods on the network. How ClusterIP, NodePort, LoadBalancer, and ExternalName each route traffic.
Chisato · · 4 min read Helm vs Kustomize for Kubernetes Config
Helm templates and packages Kubernetes manifests with a templating language; Kustomize patches plain YAML declaratively, with no templates at all.