I'm trying to right-size resource settings for several Kubernetes services and keep them accurate over time. My current CPU requests are around 200–500m, but steady-state usage is closer to 20m, which seems to be driving unnecessary node capacity. CPU limits are around 800–1000m, and Java services sometimes start slowly or crash-loop because JVM startup needs a large burst for JIT compilation, class loading, and framework initialization. I also see occasional request latency that may be caused by CPU throttling.
Memory requests of roughly 600Mi–1000Mi seem to match actual usage. However, I'm questioning whether setting memory limits higher than requests is useful, since memory consumed above the request may remain allocated—especially with JVM heaps—and can increase the risk of node-level OOM pressure.
I'm considering lowering CPU requests to something closer to observed usage, setting a much higher CPU limit such as 3–4 cores or removing the CPU limit entirely, and making memory requests and limits approximately equal. My main concern is whether a pod without a CPU limit could consume all available node CPU during a spike and starve other workloads. How do you handle this in production, particularly for Java services, and how do you continuously adjust the values?
3 Answers
For memory, setting the request equal to the limit is a common stability-focused choice. It prevents the pod from appearing to have inexpensive burst capacity that could contribute to node memory pressure and gives it the guaranteed QoS behavior many teams want. The application still needs a sensible heap limit and headroom for non-heap memory, because matching the container limit to observed heap usage too tightly can cause the JVM itself to hit the container limit.
Removing a CPU limit does not automatically let one pod permanently take over the node. CPU requests are used for scheduling and influence proportional CPU allocation under contention; workloads with requests have their requested capacity protected. Any unused CPU can then be shared during bursts, which is useful for JVM startup. Keep CPU requests high enough for the service’s real performance needs, though—using only a tiny steady-state number may leave the pod with poor throughput during sustained load. For applications that dynamically size threads or worker pools based on available CPUs, make sure the JVM or runtime is configured with an appropriate CPU ceiling or cgroup-aware settings.
A practical starting point is to use historical metrics—often around the 90th or 95th percentile—for requests, then adjust them as workload patterns change. Many teams omit CPU limits because throttling can hurt latency and startup performance. Keep alerts for sustained CPU or memory usage above the configured values, and periodically check whether the long-term average is far below the request so you can right-size again.
This is especially important for workers and queue consumers, whose usage can vary considerably. Vertical Pod Autoscaler recommendations or tools such as Goldilocks can help collect the data and suggest values, while automated right-sizing products can handle the recurring maintenance if you have many workloads.
If you need Kubernetes’ guaranteed QoS class, matching requests and limits is the stricter option, but it generally costs more because the scheduler has less room to overcommit the node.

The key distinction is that a request reserves scheduling capacity, while a CPU limit caps execution and can cause throttling. Removing the limit is usually safer for latency-sensitive services than setting it just slightly above the request, provided the node has sensible capacity planning and every important workload has an appropriate request.