Resource Management

4 minute read

Without resource limits, a single runaway Pod can consume all CPU and memory on a node, starving other workloads. Resource management in Kubernetes ensures fair sharing of cluster resources and prevents noisy neighbors from causing outages.

Requests vs. Limits

Every container can specify two resource parameters:

Parameter Description Impact
Requests Minimum guaranteed resources Used by the Scheduler to decide which node can run the Pod
Limits Maximum allowed resources Enforced by the kubelet. Container is throttled (CPU) or killed (memory) if exceeded.
resources:
  requests:
    cpu: "250m"       # Guaranteed 0.25 CPU cores
    memory: "128Mi"   # Guaranteed 128 MiB memory
  limits:
    cpu: "500m"       # Max 0.5 CPU cores
    memory: "256Mi"   # Max 256 MiB memory

CPU Resources

CPU is measured in cores or millicores:

Value Meaning
1 1 full CPU core
0.5 or 500m Half a CPU core
0.25 or 250m Quarter of a CPU core
100m 0.1 CPU cores

CPU is compressible — when a container hits its CPU limit, it’s throttled (slowed down), not killed.

Memory Resources

Memory is measured in bytes with standard suffixes:

Value Meaning
128Mi 128 mebibytes (134,217,728 bytes)
256Mi 256 mebibytes
1Gi 1 gibibyte
128M 128 megabytes (128,000,000 bytes)

Memory is incompressible — when a container exceeds its memory limit, it’s OOMKilled (Out of Memory Killed) and restarted.

Tip: Use Mi and Gi (binary units) consistently. Avoid mixing M (decimal) and Mi (binary).

Setting Resources in a Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: nginx:1.27
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "250m"
            memory: "256Mi"
      - name: sidecar
        image: busybox
        command: ["sh", "-c", "sleep infinity"]
        resources:
          requests:
            cpu: "50m"
            memory: "64Mi"
          limits:
            cpu: "100m"
            memory: "128Mi"

Total Pod resources = sum of all containers:

  • Requests: 150m CPU, 192Mi memory
  • Limits: 350m CPU, 384Mi memory

QoS Classes

Kubernetes assigns a Quality of Service class to each Pod based on its resource settings. This determines eviction priority when a node is under resource pressure.

QoS Class Condition Eviction Priority
Guaranteed All containers have requests = limits for both CPU and memory Last to be evicted (highest priority)
Burstable At least one container has a request or limit set Evicted after BestEffort
BestEffort No resources set on any container First to be evicted (lowest priority)

Guaranteed

resources:
  requests:
    cpu: "500m"
    memory: "256Mi"
  limits:
    cpu: "500m"         # Same as request
    memory: "256Mi"     # Same as request

Burstable

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"         # Different from request
    memory: "256Mi"     # Different from request

BestEffort

# No resources specified at all
containers:
- name: app
  image: nginx
  # No resources block
# Check a Pod's QoS class
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'

What Happens When Limits Are Exceeded

CPU Limit Exceeded

  • Container is throttled — limited to the specified CPU
  • No crash, no restart, just slower performance
  • Monitor with kubectl top pods

Memory Limit Exceeded

  • Container is OOMKilled — terminated immediately
  • Kubernetes restarts it based on restartPolicy
  • Status shows: OOMKilled
# Demonstrate OOMKill
kubectl run mem-hog --image=polinux/stress --restart=Never \
  --limits='memory=128Mi' -- stress --vm 1 --vm-bytes 256M

# Watch it get killed
kubectl get pod mem-hog -w
# STATUS: OOMKilled

LimitRange (Namespace Defaults)

Set default resource constraints for all Pods in a namespace:

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: development
spec:
  limits:
  - type: Container
    default:                 # Default limits if not specified
      cpu: "500m"
      memory: "256Mi"
    defaultRequest:          # Default requests if not specified
      cpu: "100m"
      memory: "128Mi"
    max:                     # Maximum allowed
      cpu: "2"
      memory: "2Gi"
    min:                     # Minimum required
      cpu: "50m"
      memory: "64Mi"
  - type: Pod
    max:
      cpu: "4"
      memory: "4Gi"

Now any Pod in the development namespace without explicit resources gets the defaults.

ResourceQuota (Namespace Totals)

Limit the total resources a namespace can consume:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: development
spec:
  hard:
    requests.cpu: "4"           # Total CPU requests
    requests.memory: "8Gi"      # Total memory requests
    limits.cpu: "8"             # Total CPU limits
    limits.memory: "16Gi"       # Total memory limits
    pods: "20"                  # Max Pods
    services: "10"
    persistentvolumeclaims: "5"
    configmaps: "20"
    secrets: "20"
kubectl describe resourcequota team-quota -n development

Output:

Name:            team-quota
Namespace:       development
Resource         Used    Hard
--------         ----    ----
limits.cpu       750m    8
limits.memory    768Mi   16Gi
pods             3       20
requests.cpu     300m    4
requests.memory  384Mi   8Gi

Monitoring Resource Usage

# Node resource usage
kubectl top nodes

# Pod resource usage
kubectl top pods

# Pod usage in a specific namespace
kubectl top pods -n development

# Sort by CPU usage
kubectl top pods --sort-by=cpu

# Sort by memory usage
kubectl top pods --sort-by=memory

Note: kubectl top requires the Metrics Server to be installed. In Minikube: minikube addons enable metrics-server.

Resource Sizing Guidelines

Workload Type CPU Request CPU Limit Memory Request Memory Limit
Web server 100m–250m 500m–1000m 128Mi–256Mi 256Mi–512Mi
API service 250m–500m 1000m–2000m 256Mi–512Mi 512Mi–1Gi
Database 500m–2000m 2000m–4000m 512Mi–2Gi 2Gi–8Gi
Worker/Batch 100m–500m 1000m–2000m 128Mi–512Mi 512Mi–2Gi

Rule of thumb:

  • Start with conservative requests, generous limits
  • Monitor actual usage with kubectl top and adjust
  • Set limits 2-4x of requests for burstable workloads
  • Keep requests = limits for critical services (Guaranteed QoS)

Summary

  • Requests = guaranteed minimum; used for scheduling
  • Limits = enforced maximum; throttle (CPU) or kill (memory)
  • QoS Classes: Guaranteed > Burstable > BestEffort (eviction order)
  • LimitRange sets namespace defaults
  • ResourceQuota caps total namespace consumption
  • Always set resources in production — avoid BestEffort QoS
  • Use kubectl top to monitor actual usage

What’s Next?

Now let’s learn about Health Checks — using liveness, readiness, and startup probes to ensure Kubernetes only routes traffic to healthy Pods and automatically restarts unhealthy ones.

Next: Health Checks →

Updated: