Resource Requests, Limits & QoS Classes
Requests and limits do more than reserve CPU and memory — they decide your Pod's QoS class and who gets evicted first when a node runs hot.
By now you know that requests reserve capacity and limits cap it. The senior insight is that the relationship between those two numbers silently assigns each Pod a Quality of Service (QoS) class, and that class decides who the kubelet kills first when a node runs out of memory.
The three QoS classes
| Class | How you get it | Eviction order |
|---|---|---|
| Guaranteed | Every container sets requests equal to limits, for both CPU and memory | Killed last |
| Burstable | At least one request is set, but not equal to limits | Killed in the middle |
| BestEffort | No requests or limits at all | Killed first |
The CPU-limit trap
Memory is incompressible: exceed the memory limit and the container is OOMKilled. CPU is compressible: exceed the CPU limit and you are merely throttled. That difference matters. A tight CPU limit on a latency-sensitive service causes mysterious p99 spikes under load even though the node has spare cores. Many seasoned teams set CPU requests but leave CPU limits off (or generous) while always pinning memory requests equal to memory limits.
Check what you actually got
Never assume — read the class back from the live object with kubectl get pod NAME -o jsonpath='{.status.qosClass}'.
Example
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-api
spec:
containers:
- name: api
image: myorg/api:2.4.1
resources:
requests:
cpu: "500m" # scheduler reserves half a core
memory: "512Mi" # request == limit => Guaranteed for memory
limits:
# no cpu limit: burst into idle cores instead of being throttled
memory: "512Mi"
---
# Verify the class the scheduler actually assigned:
# kubectl get pod guaranteed-api -o jsonpath='{.status.qosClass}'
# -> Burstable (no cpu limit) — intentional; memory is still protectedWhen to use it
- A platform team sets equal requests and limits on critical microservices to give them Guaranteed QoS, ensuring they are never throttled or evicted under node pressure.
- A batch job is configured with requests only (no limits) to get Burstable QoS, allowing it to consume spare capacity without blocking higher-priority pods.
- An operator uses kubectl top pods to measure actual CPU and memory consumption before tightening resource requests to reduce cluster costs.
More examples
Guaranteed QoS class
Setting requests equal to limits gives the pod Guaranteed QoS — the highest priority class that is last to be evicted.
spec:
containers:
- name: api
image: myapi:1.0
resources:
requests:
cpu: "500m"
memory: "256Mi"
limits:
cpu: "500m" # Equal to request
memory: "256Mi" # Equal to request
# QoS: Guaranteed — never throttled or evicted firstBurstable vs BestEffort QoS
Compares Burstable QoS (requests set, limits higher) with BestEffort (no resources set) — BestEffort pods are evicted first.
# Burstable: requests set, limits higher
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "1"
memory: "512Mi"
# BestEffort: no resources set at all
# resources: {} # Evicted first under node pressureCheck actual usage with kubectl top
Shows live CPU and memory usage for pods and nodes, enabling data-driven right-sizing of resource requests.
kubectl top pods -n production --sort-by=memory
# NAME CPU(cores) MEMORY(bytes)
# api-7b9f-xkp 45m 128Mi
# worker-abc-12 230m 380Mi
kubectl top nodes
Discussion