Kubernetes Resource Limits and Requests

Khimananda Oli 8 min read Virtualization
Kubernetes Resource Limits and Requests

By Khimananda Oli | Last reviewed: August 2026

Misconfigured Kubernetes Resource Limits and Requests are the silent killers of production stability. You might see pods evicted randomly during traffic spikes, nodes running out of memory despite low CPU usage, or autoscalers failing to trigger because metrics don't match reality. This usually happens when teams treat resource specs as optional metadata rather than strict scheduling contracts. Getting these values right is the difference between a resilient platform and a fragile cluster that requires constant babysitting. For a deeper look at how automation can help manage this complexity, see my guide on using AI to write Terraform and Kubernetes YAML safely.

Pod Specrequests.cpu: 500mrequests.mem: 512Milimits.cpu: 1000mlimits.mem: 1GiKube-SchedulerFinds node with ≥ Request(Ignores Limit for placement)Bin Packing AlgorithmNode RuntimeReserves: 512Mi RAMHard Cap: 1Gi RAMThrottle: 1000m CPU
Kubernetes Resource Limits and Requests scheduling flow: requests drive placement, limits enforce runtime caps

What is the difference between Kubernetes Resource Limits and Requests?

The distinction between requests and limits is fundamental to how Kubernetes manages workloads, yet it remains one of the most misunderstood concepts in the ecosystem. A request is a reservation. When the scheduler places a pod, it sums the requests of all containers and finds a node with sufficient unallocated capacity. If a node has 4Gi of allocatable memory and existing pods request 3.5Gi, only pods requesting ≤512Mi can land there, regardless of actual current usage. Requests determine where a pod runs.

A limit is an enforcement boundary enforced by the container runtime (containerd/CRI-O) via Linux cgroups. CPU limits throttle the process using CFS quotas; memory limits trigger an OOM kill if exceeded. Limits determine how much a pod can consume at runtime. Crucially, the scheduler does not consider limits when placing pods. You can set a limit higher than available node capacity, but if actual usage hits that limit on a packed node, you risk instability. In production, I always recommend setting requests equal to limits for critical services to eliminate this gap and guarantee resources end-to-end.

CPU vs Memory Behavior Differences

CPU and memory behave differently under constraint, and confusing them causes outages. CPU is compressible: if a container exceeds its CPU limit, the kernel throttles it. The application slows down but stays alive. This makes CPU limits relatively safe to set tightly, though aggressive throttling can increase latency tails. Memory is incompressible: there is no way to "slow down" memory allocation. If a container exceeds its memory limit, the kernel invokes the OOM killer immediately. The pod terminates with exit code 137. This asymmetry means memory requests must be accurate to prevent scheduling onto nodes that cannot satisfy actual demand, while memory limits must include headroom for legitimate spikes.

How do you configure Kubernetes Resource Limits and Requests correctly?

Correct configuration starts with measurement, not guessing. Before writing any YAML, profile your application under realistic load. Use tools like kubectl top pods, Prometheus metrics (container_memory_working_set_bytes, rate(container_cpu_usage_seconds_total)), or VPA in recommendation mode. Set requests to the observed P95 usage plus a safety margin (typically 10–20%). Set limits based on worst-case spike tolerance or business SLAs.

<!-- Production-ready resource spec -->
resources:
  requests:
    memory: "512Mi"
    cpu: "250m"
  limits:
    memory: "768Mi"   # 1.5x request for burst headroom
    cpu: "1000m"      # Allow burst up to 1 core

Avoid common anti-patterns. Never set memory requests significantly lower than actual baseline usage; this leads to overcommitment and OOM kills during normal operation. Never set CPU limits below measured steady-state usage; this causes artificial latency. For Java/JVM applications, remember that heap size ≠ container memory. The JVM uses additional memory for metaspace, thread stacks, direct buffers, and GC overhead. A 512Mi heap typically needs 768Mi–1Gi container memory. Always use -XX:+UseContainerSupport (default in modern JDKs) and set -XX:MaxRAMPercentage=75.0 rather than fixed -Xmx values to respect container limits dynamically.

  1. Profile baseline usage under representative load for at least 24 hours
  2. Set memory request = P95 working set + 15% buffer
  3. Set memory limit = max observed spike + 25% headroom (or 1.5–2x request)
  4. Set CPU request = P95 usage + 10% buffer
  5. Set CPU limit = acceptable burst ceiling (often 2–4x request for web apps)
  6. Validate with load testing before promoting to production

How do QoS classes affect pod eviction priority?

Kubernetes assigns every pod a Quality of Service (QoS) class based solely on resource specifications. This class determines eviction order during node pressure. Understanding QoS is non-negotiable for reliable Kubernetes Resource Limits and Requests configuration.

QoS ClassCriteriaEviction PriorityUse Case
GuaranteedLimits == Requests for both CPU & memory (or only limits set)Last (lowest priority)Databases, critical APIs, payment services
BurstableAt least one request/limit set, but not matchingMiddle (evicted after BestEffort)Web apps, workers, batch jobs
BestEffortNo requests or limits specifiedFirst (highest priority)Dev/test only, never production

In practice, most production clusters should aim for Guaranteed QoS on tier-0 and tier-1 services. Burstable is acceptable for horizontally scalable stateless workloads where individual pod loss is tolerable. BestEffort pods are essentially free compute that vanishes first under pressure; they have no place in SLA-bound systems. During node memory pressure, kubelet evicts pods in reverse QoS order, then by usage relative to requests within the same class. This means a Burstable pod using 2x its request gets evicted before one using exactly its request. Setting accurate requests isn't just about scheduling—it directly impacts survival during resource contention.

BestEffortNo requests / limitsEVICTED FIRSTZero guaranteesConsumes leftover onlyUnpredictable performanceBurstableRequests ≠ LimitsEVICTED SECONDMinimum guaranteedCan burst to limitUsage > request = riskGuaranteedLimits == RequestsEVICTED LASTFull reservationPredictable performanceProduction-critical only
Kubernetes QoS classes determine eviction priority during node pressure for Kubernetes Resource Limits and Requests

How does resource management interact with autoscaling?

Autoscalers depend entirely on accurate resource specifications. The Horizontal Pod Autoscaler (HPA) calculates desired replicas using the formula: desiredReplicas = ceil[currentReplicas * (currentMetricValue / desiredMetricValue)]. If your requests are wrong, HPA scales incorrectly. Example: a deployment with 3 pods requesting 250m CPU each shows 60% utilization at 450m total usage. HPA targeting 70% sees headroom and doesn't scale. But if actual per-pod need is 400m, you're already overloaded. Conversely, inflated requests cause premature scaling and wasted spend. For advanced scenarios, explore predictive autoscaling with machine learning to anticipate demand patterns.

Vertical Pod Autoscaler (VPA) recommends request/limit adjustments based on historical usage. Run VPA in recommendation mode first—never auto-update production workloads without validation. Cluster Autoscaler adds nodes when pending pods cannot be scheduled due to insufficient resources. If requests are too high, CA provisions excess nodes. If too low, pods schedule but fail at runtime. Always align resource specs with observability data. Tools like anomaly detection for metrics help identify drift between configured values and actual behavior before it causes incidents.

Namespace-Level Governance with LimitRanges and ResourceQuotas

Individual pod specs aren't enough. Platform teams must enforce guardrails. LimitRange sets default requests/limits for containers that omit them and enforces min/max bounds per container or pod. ResourceQuota caps total namespace consumption. Without these, a single misconfigured deployment can starve an entire team or blow budgets. In multi-tenant environments—common in Nepal's growing tech sector where startups share clusters—these objects are mandatory for isolation and cost control.

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
spec:
  limits:
  - default:              # Applied if container omits limits
      memory: 512Mi
      cpu: 500m
    defaultRequest:       # Applied if container omits requests
      memory: 256Mi
      cpu: 250m
    max:                  # Hard ceiling per container
      memory: 4Gi
      cpu: 4
    min:                  # Floor per container
      memory: 64Mi
      cpu: 50m
    type: Container
Pod ManifestNo resources definedOR partial specLimitRangeInjects defaultsEnforces min/maxPer-container scopeRejects violationsResourceQuotaSums all podsCaps namespace totalPrevents overspendBlocks over-quotaOK
LimitRange and ResourceQuota enforce Kubernetes Resource Limits and Requests governance at namespace level

Implementing Reliable Kubernetes Resource Limits and Requests

Getting Kubernetes Resource Limits and Requests right is an ongoing discipline, not a one-time task. Start by auditing existing workloads with kubectl describe pod and Prometheus queries to find gaps between config and reality. Implement LimitRanges and ResourceQuotas in every namespace before onboarding teams. Use VPA recommendations as input, not gospel. Review values quarterly as applications evolve. Most importantly, treat resource specs as first-class infrastructure code—versioned, reviewed, and tested like any other production artifact. If your team needs help establishing these practices or auditing existing clusters, reach out to discuss your specific environment.

Frequently Asked Questions

Requests guarantee minimum CPU and memory for scheduling, while limits cap maximum usage. Pods exceeding memory limits get OOMKilled; CPU throttling occurs when hitting CPU limits. Both are defined per container in the pod spec to ensure fair cluster resource allocation and prevent noisy neighbor issues.

Add resources.requests and resources.limits under each container spec using kubectl edit or Helm values. Specify cpu in millicores and memory in Mi or Gi. Validate configurations with kubectl apply --dry-run=client before deploying to production clusters to catch syntax errors early.

The Linux OOM killer terminates the container immediately, restarting it according to the restart policy. Frequent OOMKills indicate insufficient memory limits or application leaks. Check events with kubectl describe pod and monitor memory usage via metrics-server or Prometheus to right-size allocations and prevent recurring crashes.

Yes, but Kubernetes defaults requests to match limits, potentially over-reserving resources. Best practice is setting both explicitly to allow scheduler flexibility and bin-packing efficiency. Relying on implicit defaults often leads to fragmented cluster capacity and higher infrastructure costs due to poor node utilization across workloads.

CFS quota enforcement pauses containers exceeding CPU limits, causing latency spikes even if average usage stays low. Bursty workloads suffer most. Monitor container_cpu_throttled_seconds_total in Prometheus to detect throttling. Increase limits or adjust application concurrency rather than removing limits entirely to maintain predictable performance and cluster stability.

Start with 250m CPU request, 500m limit, 256Mi memory request, and 512Mi limit for typical Laravel deployments. Adjust based on actual usage from vertical pod autoscaler recommendations or Prometheus metrics. Queue workers often need higher memory limits than web pods due to long-running job processing and cached state.

Scheduler places pods only on nodes with sufficient unallocated requested resources. Overestimating requests causes unschedulable pods and wasted capacity; underestimating risks contention. Use kubectl top nodes and descheduler tools to identify imbalances. Right-sizing requests ensures optimal bin-packing and reduces the number of required nodes in 2026 clusters.

No, except for latency-sensitive databases requiring guaranteed resources. Setting limits higher than requests allows burst capacity during traffic spikes while maintaining scheduling predictability. A 2:1 limit-to-request ratio works well for most web applications, balancing cost efficiency with performance headroom during unexpected load increases.

Deploy LimitRange objects to set default and max/min constraints per namespace. Combine with ResourceQuota to cap total namespace consumption. Use Kyverno or OPA Gatekeeper policies to reject deployments missing resource specs. This prevents runaway pods from starving other tenants in multi-team Kubernetes environments.

Vertical Pod Autoscaler in recommendation mode analyzes historical usage without auto-applying changes. Goldilocks provides dashboard visualizations for VPA suggestions. Kubecost and OpenCost map actual spend to resource allocation. Always validate automated recommendations against application SLAs before applying changes to avoid performance regressions in critical production services.

Scheduler considers the highest init container request, not the sum, since they run sequentially. However, the effective pod request equals max(init requests, sum(app container requests)). Misunderstanding this causes scheduling failures when large init containers precede small app containers. Always verify total pod requests with kubectl describe pod.

Guaranteed requires equal requests and limits for all containers. Burstable has at least one request set below limit. BestEffort has neither. Guaranteed pods get priority during eviction; BestEffort dies first. Set appropriate QoS by aligning resource specs with workload criticality to control termination order during node pressure events.

Requests may exceed individual node capacity even if aggregate cluster resources suffice. Fragmentation from mixed instance sizes worsens this. Check kubectl get events for Insufficient cpu/memory messages. Enable cluster autoscaler with balanced scaling groups or use topology spread constraints to distribute pods evenly across available node pools.

Limits cap CPU and memory abuse but cannot stop attacks alone. Combine strict limits with network policies, read-only root filesystems, and seccomp profiles. Resource quotas prevent attackers from spawning unlimited pods. Defense in depth is essential since determined adversaries exploit misconfigurations beyond just resource exhaustion vectors.

Review quarterly or after major releases using VPA recommendations and cost reports. Application updates change resource profiles significantly. Automate alerts for sustained over-provisioning or frequent throttling. Treat resource tuning as continuous optimization, not one-time setup, to maintain cost efficiency and performance as workloads evolve throughout 2026.