Active Nerds
Module #27 · intermediate Article

Resource Requests vs. Limits: CPU/Memory Scheduling and Throttling Policies

Requests tell the scheduler how much to reserve; limits cap actual usage — without requests, pods land on random nodes causing resource contention; without l...

4 min read+15 XPPublished 2026-06-30

Quick Answer: Requests tell the scheduler how much to reserve; limits cap actual usage — without requests, pods land on random nodes causing resource contention; without limits, one pod can consume all node resources and starve neighbors.

Detailed Answer:

containers:
- name: app
  image: myapp:v1
  resources:
    requests:
      cpu: "250m"       # 0.25 CPU cores — used for SCHEDULING decisions
      memory: "256Mi"   # 256 MiB — used for SCHEDULING decisions
    limits:
      cpu: "1000m"      # 1 CPU core — THROTTLED (not killed) if exceeded
      memory: "512Mi"   # 512 MiB — container KILLED (OOMKilled) if exceeded

# CPU units:
# 1000m = 1 core = 1 vCPU
# 500m  = 0.5 cores
# 250m  = 0.25 cores (quarter core)
# 100m  = 0.1 cores

# Memory units:
# Ki = kibibytes (1024 bytes)
# Mi = mebibytes (1024 Ki)
# Gi = gibibytes (1024 Mi)
# 256Mi ≈ 268MB

What Happens Without Requests/Limits:

# Without requests:
# - Scheduler has no idea how much CPU/memory the pod needs
# - Pod may be scheduled on an already-busy node
# - Pod gets BestEffort QoS — evicted FIRST under memory pressure
# - No guarantee of any CPU/memory allocation

# Without limits:
# - A memory leak can consume all node memory
# - Other pods on same node get OOMKilled
# - A CPU-intensive pod can starve all neighbors
# - Node may become NotReady (kubelet itself starved)

# Check pods without resource limits (find the dangerous ones)
kubectl get pods -A -o json | jq -r '
  .items[] |
  select(.spec.containers[].resources.limits == null) |
  [.metadata.namespace, .metadata.name] |
  @tsv'

Right-Sizing Strategy:

# Step 1: Deploy without limits, measure actual usage for 1 week
kubectl top pods -n production --containers --sort-by=cpu

# Step 2: Use VPA in recommendation mode
kubectl describe vpa my-app-vpa | grep -A10 "Recommendation"

# Step 3: Set requests at P50 usage, limits at P99 usage
# requests = typical usage (scheduler uses this)
# limits   = peak usage (safety cap)

# Rule of thumb starting points:
# Web/API service:  cpu request=250m, limit=1000m | memory request=256Mi, limit=512Mi
# Java/JVM app:     cpu request=500m, limit=2000m | memory request=512Mi, limit=2Gi
# Node.js:          cpu request=100m, limit=500m  | memory request=128Mi, limit=256Mi
# Python Flask:     cpu request=100m, limit=500m  | memory request=128Mi, limit=256Mi

The CPU Limits Controversy:

# CPU throttling is often WORSE than no CPU limit
# If your app is CPU-throttled, it appears slow/unresponsive
# even when the node has idle CPU available

# Many production teams omit CPU limits:
resources:
  requests:
    cpu: "250m"         # Keep request — needed for scheduling
    memory: "256Mi"
  limits:
    # No CPU limit — allow bursting on idle nodes
    memory: "512Mi"     # Always keep memory limit — OOMKill is better than node death

# Monitor with: rate(container_cpu_throttled_seconds_total[5m])
# If throttling > 25% of time → increase limit or remove it

Key Takeaway: Always set memory limits (OOMKill is recoverable); CPU limits are optional and sometimes harmful — but always set both requests so the scheduler can make intelligent placement decisions.


Finished reading?

Mark as complete to claim your +15 XP and track progress.