Kubernetes Resource Limit Management
Kubernetes Resource Limit Management controls container resource usage with CPU and memory constraints, ensuring optimal cluster performance and preventing overconsumption.
Kubernetes Resource Limit Management is the practice of declaring and maintaining the resources.limits field on container specifications — the ceiling Kubernetes enforces on how much CPU and memory a container may actually consume at runtime, backed by the Linux kernel's cgroup mechanism. Where requests inform scheduling and guarantee a baseline reservation, limits are enforced continuously during the container's execution: exceeding a CPU limit results in throttling, while exceeding a memory limit results in the container being terminated by the kernel's OOM killer, making limit management fundamentally about runtime behavior rather than placement.
Because CPU and memory are enforced through fundamentally different kernel mechanisms — CPU throttling is a soft, reversible slowdown, while memory limit violation is a hard, unrecoverable termination — limit management practices differ meaningfully between the two resource types, even though both are declared through the same resources.limits syntax.
CPU Limits and Throttling
How CPU Limits Are Enforced
A CPU limit translates into a CFS (Completely Fair Scheduler) quota and period on the container's cgroup — the container is allowed to consume its limit's worth of CPU time within each period, and any attempt to exceed that within a given period results in the container being throttled (paused) rather than killed, resuming once the next period begins.
The Throttling Latency Problem
A container whose CPU limit is set too tightly relative to its actual workload can experience throttling even while its average CPU usage appears well within the limit, because CFS quota enforcement operates on short, fixed periods (typically 100ms) — a bursty workload that briefly needs more CPU than its per-period quota allows gets throttled during that burst, manifesting as latency spikes that are easy to misdiagnose as an application-level problem rather than a CPU limit configuration issue.
kubectl exec codartium-app -- cat /sys/fs/cgroup/cpu.stat | grep throttled
Memory Limits and OOM Termination
Hard Enforcement, No Graceful Degradation
Unlike CPU, memory has no equivalent "throttle and continue" mechanism — a container exceeding its memory limit is terminated immediately and unconditionally by the kernel's OOM killer, with no opportunity for the application to gracefully free memory and continue running. This asymmetry is why memory limits require more conservative headroom than CPU limits typically do.
Diagnosing OOMKilled Containers
kubectl describe pod codartium-app | grep -A 5 "Last State"
A Last State: Terminated, Reason: OOMKilled entry confirms the container exceeded its memory limit; resolving this requires either raising the limit (if the workload's actual peak need genuinely exceeds it) or addressing an application-level memory leak or inefficiency (if the limit was reasonable but usage grew unbounded over time).
Setting Limits Relative to Requests
The Burstable Pattern
Setting a limit meaningfully higher than the request (Burstable QoS) allows a container to absorb occasional spikes above its guaranteed baseline, using spare node capacity when available, while still guaranteeing the request amount at minimum — this is the most common and generally recommended pattern for typical application workloads with variable, bursty resource needs.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
The Guaranteed Pattern
Setting requests equal to limits (Guaranteed QoS) removes burst capacity entirely but provides the strongest protection against eviction under node resource pressure — appropriate for workloads where predictable, consistent resource allocation matters more than the ability to occasionally burst, such as latency-sensitive services with strict, well-understood resource needs.
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"
Avoiding Limits Without Requests
Setting a limit without a corresponding request is technically permitted, but produces a request implicitly defaulted to equal the limit under most LimitRange configurations, or in the absence of any LimitRange, an inconsistent state where the container's actual scheduling behavior may not match its intended resource profile — explicit requests alongside every limit are generally the clearer, more maintainable practice.
Extended Resource Limits
GPUs and Other Extended Resources
Extended resources like GPUs are typically requested only as a limit (with the request implicitly equal to the limit, since extended resources generally do not support the burstable overcommit model that CPU and memory do) — a container either gets the whole extended resource unit it requested, or it does not get scheduled at all.
resources:
limits:
nvidia.com/gpu: 1
Bounding Limits via LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: codartium-limit-bounds
spec:
limits:
- max:
cpu: "2"
memory: "2Gi"
min:
cpu: "100m"
memory: "128Mi"
type: Container
A LimitRange max/min bound prevents any single container in a namespace from declaring an unreasonably large or small limit, protecting shared namespace capacity from a single misconfigured workload.
Example
apiVersion: v1
kind: Pod
metadata:
name: codartium-limit-example
spec:
containers:
- name: app
image: codartium/app:latest
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "750m"
memory: "384Mi"