Kubernetes Resource Requests & Limits: What to Set
Set CPU requests based on your container’s steady-state usage (typically 50–70% of its observed peak under normal load) and set memory requests to the average working set size, while setting CPU limits only when you need strict throttling and memory limits to the maximum you are willing to let the container use before it is killed (usually 1.5–2× the request). These values are not one-size-fits-all; they depend on your workload, how you scale, and your tolerance for latency versus cost.
Background: Why Requests and Limits Exist
Kubernetes uses requests and limits to decide two separate things: scheduling and enforcement.
- Requests are the amount of CPU or memory you guarantee a container. The scheduler uses requests to place pods on nodes that have enough free capacity. The kubelet also uses requests to calculate how much of a node’s resources are already committed.
- Limits are the maximum a container is allowed to use. For CPU, the kernel enforces this via CFS (Completely Fair Scheduler) quotas, which throttle the process. For memory, exceeding the limit causes the kernel to invoke the OOM killer, terminating the container.
If you set no requests, the scheduler assumes the container needs zero resources. If you set no limits, the container can consume all available node resources, which may starve other pods.
How Kubernetes Calculates Node Pressure
When you create a pod, the kubelet adds up the requests of all containers on the node. This sum is compared against the node’s allocatable capacity (total capacity minus reserved system resources). If the sum exceeds capacity, the pod cannot be scheduled. Limits do not affect scheduling—only requests do. However, the kubelet also tracks the sum of limits to decide if it should evict pods when the node is under memory pressure.
For CPU, the kernel uses a bandwidth control mechanism: each container gets a quota equal to its limit (in milliseconds of CPU time per period, default 100ms). If the container uses more than its limit, it is throttled, not killed. For memory, there is no throttling—the OOM killer picks a process to kill when the cgroup exceeds its limit.
A common misconception is that setting a limit lower than the request is invalid. In fact, Kubernetes allows it, but it is almost always a mistake: the scheduler will reserve the request amount, but the container will be killed or throttled at the limit, which is lower than what you promised. Always keep limits ≥ requests.
How to Choose CPU Values
CPU is compressible: you can throttle it without killing the process. This makes CPU requests more about quality of service than hard guarantees.
- Measure your workload under realistic production traffic. Use a monitoring tool (Prometheus, CloudWatch, etc.) to record CPU usage per container over a week.
- Set the request to the 50th–70th percentile of observed usage. This ensures you get a fair share without over-reserving. If your workload is latency-sensitive, aim higher (80th percentile).
- Set the limit only if you want to cap burst usage. A common practice is to set the limit to 2–4× the request, or to omit the limit entirely for CPU if you trust the node’s overall capacity. Omitting the limit means the container can use any idle CPU, which is fine for many batch jobs.
Example: A web server that typically uses 0.2 CPU and spikes to 0.8 CPU briefly. Set request: 0.3, limit: 1.0. This gives it headroom while preventing a single pod from hogging the node.
How to Choose Memory Values
Memory is incompressible. Exceeding the limit kills the container, so you must be generous.
- Measure the working set size (the memory actually in use, not just allocated). Look at the 95th or 99th percentile of usage over a week.
- Set the request to the average or slightly above the median. This is what the scheduler uses to pack pods. If you set it too low, the node may overcommit and cause OOM kills under pressure.
- Set the limit to the maximum you are comfortable losing. A typical rule of thumb is 1.5–2× the request. If your application has a known hard cap (e.g., a JVM heap), set the limit to that cap plus overhead.
Example: A Java app with a 512MB heap and 100MB overhead. Request: 512Mi, limit: 1Gi. This allows GC spikes without killing the pod.
Common Mistakes and Pitfalls
- Setting limits too low causes CPU throttling, which can increase latency dramatically. Memory limits too low cause OOM kills and restarts.
- Setting requests too high wastes capacity and increases cost, because you pay for reserved resources even if unused.
- Forgetting to set requests leads to poor scheduling: the scheduler may place many pods on one node, causing resource contention.
- Using the same values for all environments—dev, staging, prod—ignores different traffic patterns. Re-evaluate after each major release.
- Not accounting for memory page cache: the kernel counts page cache as part of the container’s memory usage. If your app reads large files, the cache may inflate usage. Use the
memory.working_setmetric instead ofmemory.usage.
Cost and Efficiency Trade-offs
Requests directly affect your cloud bill because most providers charge based on the requested resources, not actual usage. If you set a request of 1 CPU and 1Gi memory, you pay for that even if the container only uses 10%. Over-provisioning is the most common cause of wasted spend in Kubernetes clusters. On the other hand, under-provisioning can cause performance issues and downtime, which may cost more in lost revenue. The optimal point is where your actual usage hovers around your request, with a small buffer.
Using the Vertical Pod Autoscaler (VPA) can automate this process: it recommends requests based on historical usage and can even update them automatically. However, VPA does not work well with the Horizontal Pod Autoscaler (HPA) because both may fight over replicas and resources. A common pattern is to use VPA in “off” mode to get recommendations, then apply them manually.
Alternatives and Advanced Patterns
Beyond simple requests/limits, consider these approaches:
- Quality of Service (QoS) classes: Kubernetes assigns a class based on requests/limits. Guaranteed (requests = limits) gives the highest priority. Burstable (requests < limits) is typical. BestEffort (no requests/limits) is lowest priority and most likely to be evicted.
- Resource quotas at the namespace level to cap total requests/limits across all pods.
- Limit ranges to enforce default values and maximums per container.
- CPU manager policies for exclusive CPU allocation, useful for latency-critical workloads.
For stateful workloads like databases, you often want to set requests equal to limits (Guaranteed QoS) to avoid eviction and ensure predictable performance. For stateless web services, Burstable is usually fine.
Frequently Asked Questions
What happens if I don’t set any requests or limits?
Your container gets BestEffort QoS. It can use any available CPU and memory, but it is the first to be evicted when the node runs out of resources. This is acceptable for non-critical batch jobs, but risky for production services.
Should I set CPU limits at all?
Many experts advise against CPU limits for latency-sensitive workloads because throttling can cause request timeouts. If you set a limit, make it high enough to cover bursts. Alternatively, omit the limit and rely on node-level isolation and HPA to keep total usage in check.
How do I find the right values for my existing containers?
Use the kubectl top pods command to see current usage, or set up Prometheus to collect metrics over time. The VPA can give recommendations based on a week of data. For a quick start, set requests to the 90th percentile of observed usage and limits to 2× that.
Can I change requests/limits without restarting the pod?
No. Requests and limits are immutable for a running container. You must recreate the pod (e.g., via a rolling update) to apply changes. This is why it’s important to get values right initially or use VPA in auto mode.
What’s the difference between memory request and limit in terms of OOM?
The request is only used for scheduling. The limit is the hard cap. If the container exceeds the limit, the kernel kills it. If the node is under memory pressure, the kernel may also kill containers that exceed their request but are below their limit, depending on QoS class.
Should I set requests equal to limits for databases?
Yes, for databases and other stateful services, setting requests = limits (Guaranteed QoS) is recommended to avoid eviction and ensure stable performance. The downside is you cannot overcommit, so you may waste some capacity, but the reliability gain is worth it.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.