Requests & Limits
Summary
requestsreserve capacity and drive scheduling;limitscap runtime usage and drive throttling or OOM kills.- Insight: CPU and memory behave differently at the limit. Over-CPU is throttled and the container survives; over-memory is killed. Treat the two units with different strategies.
- Key Concepts: millicores (
m), binary memory units (Mi/Gi), the three QoS classes, scheduling by requests, and cgroup enforcement. - When to Use: always set requests. Set memory limits routinely; be deliberate about CPU limits.
- Limitations: requests are a promise to the scheduler, not a guarantee of app behavior. Set them from real usage data, not guesses.
Recipe
Set a CPU request and memory request/limit from observed usage; leave CPU unlimited unless you have a specific reason to cap it.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
memory: "256Mi" # equal request/limit for memory = predictableWorking Example
A Deployment with resources sized for a Guaranteed-QoS memory profile and burstable CPU.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments
spec:
replicas: 4
selector:
matchLabels: { app: payments }
template:
metadata:
labels: { app: payments }
spec:
containers:
- name: payments
image: registry.example.com/payments@sha256:4fa2...
resources:
requests:
cpu: "300m"
memory: "512Mi"
limits:
cpu: "1"
memory: "512Mi"Memory request equals its limit, so under memory pressure this Pod is among the last evicted. CPU can burst from 300m up to a full core.
Deep Dive
CPU units
CPU is measured in cores. 1 means one vCPU/core; 500m (500 millicores) means half a core.
A CPU request reserves scheduling weight. A CPU limit is enforced by the kernel through CFS quota, which throttles the container when it exceeds the limit.
Throttling does not kill the container. It just slows it, which can inflate tail latency.
Memory units
Memory uses binary suffixes: Mi = 1024^2 bytes, Gi = 1024^3 bytes. M/G are decimal and rarely what you want.
Memory has no throttling. When a container exceeds its memory limit, the kernel OOM-kills it and the kubelet restarts it.
Because of this asymmetry, set memory limits close to real peak usage plus headroom, and prefer request == limit for predictability.
QoS classes
The scheduler and kubelet derive a Quality-of-Service class from your requests and limits.
Guaranteed: every container sets requests == limits for both CPU and memory. Highest protection from eviction.
Burstable: at least one request is set but it is not fully Guaranteed. Can use spare capacity but is evicted before Guaranteed Pods.
BestEffort: no requests or limits at all. First to be evicted under node pressure. Avoid for anything that matters.
Scheduling impact
The scheduler places a Pod only on a node whose allocatable capacity can satisfy the sum of the Pod's container requests.
Limits do not affect placement. A node can be scheduled to the point where requests are fully committed even if actual usage is low.
Set requests too high and Pods go Pending for lack of a fitting node. Set them too low and nodes get oversubscribed and noisy.
Gotchas
No requests means BestEffort. A Pod with no requests is first to be evicted and gets minimal CPU share under contention. Always set at least requests.
CPU limits cause surprise latency. A tight CPU limit throttles bursty workloads even when the node is idle. Many teams omit CPU limits and rely on requests plus autoscaling.
Memory limit too low OOM-kills under load. The container dies mid-request when traffic spikes. Size the memory limit from observed peaks plus headroom.
Requests set from wishful thinking. Guessed values either waste capacity or cause oversubscription. Measure with metrics-server, Prometheus, or the VPA in recommendation mode.
Init containers count too. The scheduler considers the max of init-container requests and the sum of app-container requests. Large init requests can block scheduling.
Alternatives
Vertical Pod Autoscaler (VPA). Use VPA in "recommendation" mode to derive right-sized requests from real usage, then apply them. Avoid VPA auto-update alongside HPA on the same metric.
Horizontal Pod Autoscaler (HPA). Scale replica count on CPU or custom metrics instead of oversizing each Pod. Requests must be set for CPU-based HPA to work.
LimitRange and ResourceQuota. A LimitRange sets namespace defaults so Pods without explicit resources still get sane values; ResourceQuota caps total namespace consumption.
FAQs
What happens when a container exceeds its CPU limit? It is throttled by CFS quota. The container keeps running but its CPU is capped, raising latency.
What happens when it exceeds its memory limit? The kernel OOM-kills the container and the kubelet restarts it, incrementing the restart count.
Should I always set CPU limits? Not necessarily. Requests handle fair sharing and scheduling. Many teams skip CPU limits to avoid throttling, but limits help enforce hard multi-tenant boundaries.
How do I get the Guaranteed class? Set requests equal to limits for both CPU and memory on every container in the Pod.
Do limits affect scheduling? No. Only requests determine which node a Pod fits on. Limits are enforced at runtime.
What units should memory use? Use Mi/Gi (binary). M/G are decimal and give slightly less memory than most people intend.
Related
- Container Spec
- Liveness, Readiness & Startup Probes
- Pod Disruption Budgets
- Pods Basics
- Pods Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).