Duvaxa

10 min read · Updated 26 Oct 2026

Kubernetes requests vs limits: what they cost and how to set them

In short: You pay for the CPU and memory your pods request, not what they use. Here's how requests and limits work, why clusters overspend, and a safe way to right-size CPU and memory.

Free tool: Kubernetes waste calculator →

Every container in Kubernetes can declare two numbers for CPU and two for memory: a request and a limit. They look similar, but they do very different jobs, and getting them wrong is the most common reason clusters cost two or three times what they should, or fall over at night.

Requests: what the scheduler reserves

A request is what Kubernetes guarantees the container. The scheduler places a pod only on a node with enough unreserved capacity to meet its requests, and that capacity is then reserved whether the pod uses it or not. This is why requests drive cost: the cluster needs enough nodes to hold the sum of all requests. A pod that requests 2 CPU cores and uses 0.2 still occupies 2 cores of a node you pay for.

Limits: the ceiling

A limit is the most the container may use. What happens at the limit depends on the resource.

  • CPU is compressible. A container that wants more CPU than its limit is throttled: it gets less CPU time and runs slower, but keeps running. Throttling shows up as higher latency, often without anyone noticing why.
  • Memory is not compressible. A container that tries to use more memory than its limit is killed (OOMKilled) and restarted.

Quality of service classes

Kubernetes groups pods by how their requests and limits are set. Guaranteed pods have requests equal to limits for every container and every resource; they are the last to be evicted when a node runs short of memory. Burstable pods have requests below limits. BestEffort pods have neither, and are evicted first. Critical services are usually Guaranteed for memory.

Why clusters overspend

  • Requests are copied from templates or examples and never changed.
  • Values were set during a load test and kept for normal traffic.
  • Teams add a large margin "just in case", and every team does it.
  • Nobody owns the numbers, so nobody revisits them after the service changes.

The result is commonly a cluster whose requests are several times its real usage. Because nodes are added to satisfy requests, the bill follows the requests.

Right-sizing CPU

  1. Collect at least a week, preferably two, of CPU usage per container, including your busiest days.
  2. Set the CPU request near typical usage, for example the 90th or 95th percentile.
  3. Think carefully about CPU limits. Many teams leave them off for latency-sensitive services, or set them well above the request, to avoid throttling. If you do set them, watch the container's throttling metrics.

Right-sizing memory

  1. Use the peak memory over the same period, not the average.
  2. Set the memory request at the peak plus headroom, for example 15 to 25 percent.
  3. Set the memory limit equal to the request, so the scheduler's view matches what the container can actually use, and a leak is contained to that container.
  4. Watch OOMKilled events after the change, and raise the value if they appear.

From smaller requests to a smaller bill

Lower requests only save money when nodes go away. With the cluster autoscaler or Karpenter, pods are packed onto fewer nodes and empty nodes are removed. Without one, reduce node counts yourself. Check that nothing else keeps nodes alive: pod disruption budgets that can never be satisfied, pods with local storage, or DaemonSets with large requests.

Tools that help

  • kubectl top pods shows current usage (it needs metrics-server). It's a snapshot, useful for a first look.
  • Prometheus with container_cpu_usage_seconds_total and container_memory_working_set_bytes gives the history you need.
  • The Vertical Pod Autoscaler can recommend values; run it in recommendation-only mode first.
  • Our free Kubernetes waste calculator prices the gap between requests and usage from kubectl output in your browser.

A worked example

A web Deployment runs 10 replicas, each requesting 1 CPU and 2 GiB of memory, so it reserves 10 cores and 20 GiB. Over two weeks, each replica's CPU usage stays below 0.25 cores at the 95th percentile and its memory peaks at 700 MiB. Setting the CPU request to 0.3 and the memory request and limit to 900 MiB reserves 3 cores and about 9 GiB: 7 cores and 11 GiB released. At an example price of $0.034 per vCPU-hour and $0.0045 per GB-hour, that's roughly $210 a month for one Deployment, before counting the others. The saving only appears once the autoscaler removes the nodes that capacity occupied.

Common mistakes

  • Sizing from averages. Memory must cover peaks, and CPU should cover the busy hours.
  • Using a single kubectl top snapshot taken on a quiet afternoon.
  • Cutting memory below what the application's runtime reserves. JVMs, for example, need heap plus non-heap memory, and the heap setting must fit inside the limit.
  • Setting CPU limits equal to tiny requests and then chasing mysterious latency caused by throttling.
  • Right-sizing without an autoscaler that can remove nodes, so the bill doesn't change.

How to verify

After a change, watch three things for a week: OOMKilled events and restarts (memory too low), CPU throttling metrics such as container_cpu_cfs_throttled_periods_total (CPU limits too low), and node count (whether the saving is real). Compare request totals with usage totals per namespace before and after, and share the result with the team that owns each workload.

Namespaces, quotas and defaults

Right-sizing once doesn't keep a cluster right-sized. New workloads arrive with whatever values their authors copied. Two Kubernetes features help hold the line. A LimitRange in each namespace sets default requests and limits for containers that don't declare them, and can set minimums and maximums, so nothing runs with no requests at all or with absurd ones. A ResourceQuota caps the total CPU and memory a namespace may request, which makes each team see its own share of the cluster and gives them a reason to keep requests honest. Pair both with a monthly report of requests versus usage per namespace, sent to the team that owns it, and right-sizing becomes routine instead of a one-off project.

Checklist

  • Every container has CPU and memory requests.
  • Requests are based on at least a week of real usage.
  • Memory requests cover peak usage plus headroom; memory limit equals request.
  • CPU limits are deliberate, and throttling is monitored where they exist.
  • An autoscaler removes nodes when requests go down.
  • Someone owns the numbers and reviews them every quarter.

FAQ

Questions

Do I pay for requests or for usage?

For nodes, which are sized to hold requests. Unused requested capacity is still paid for.

Should I set CPU limits?

It depends. Limits prevent one container starving others but cause throttling. Many teams leave them off or set them well above requests for latency-sensitive services.

Why set the memory limit equal to the request?

So the container can't use memory the scheduler didn't account for, which could push other pods off the node.

Can the Vertical Pod Autoscaler do this automatically?

It can recommend and apply values, but applying them restarts pods. Start in recommendation mode.

How much can right-sizing save?

It depends on how far requests are from usage. Our free calculator estimates the monthly cost of the gap for your cluster.

What happens if a pod has no requests at all?

It's scheduled as BestEffort: it can land anywhere, uses whatever is free, and is the first to be evicted when a node runs short of memory. Set requests, or a namespace LimitRange that provides defaults.

Tools for this

Products that help