Articles

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.

Chisato Chisato · · 4 min read
The Kubernetes logo

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

RequestsLimits
Used byScheduler, for node placementContainer runtime, at run time
EnforcementNot enforced directlyHard cap, actively enforced
CPU over the valueNot possible to request more than availableThrottled (process slows, doesn’t crash)
Memory over the valueNot possible to request more than availableProcess killed (OOMKilled)
Effect of setting too lowScheduler under-provisions the nodeFrequent throttling or kills
Effect of setting too highWasted, unusable cluster capacityNode 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 OOMKilled restart 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.

Chisato 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.

#Kubernetes #DevOps #Cloud
Chisato 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.

#Kubernetes #DevOps #Cloud