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

ClassHow you get itEviction order
GuaranteedEvery container sets requests equal to limits, for both CPU and memoryKilled last
BurstableAt least one request is set, but not equal to limitsKilled in the middle
BestEffortNo requests or limits at allKilled 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

Example · yaml
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 protected

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

Example · yaml
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 first

Burstable vs BestEffort QoS

Compares Burstable QoS (requests set, limits higher) with BestEffort (no resources set) — BestEffort pods are evicted first.

Example · yaml
# 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 pressure

Check actual usage with kubectl top

Shows live CPU and memory usage for pods and nodes, enabling data-driven right-sizing of resource requests.

Example · bash
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

  • Be the first to comment on this lesson.