Node Resource Overcommitment in Kubernetes
How Kubernetes promises more CPU and memory than a machine physically owns, why that is both a clever money-saver and a risk, and how to control it with requests, limits, quality-of-service classes and eviction.
Airlines sell more tickets than there are seats on a plane. They do this because they know, from years of data, that a few passengers never show up. Most days the bet works, the plane is full, and the airline earns money from every seat. On a rare day, everyone shows up, and the airline must send some people home unhappy. Kubernetes nodes can behave in exactly the same way. A node can hand out promises for more CPU and memory than the machine really has, betting that not every application will use its full share at the same moment. That bet is called overcommitment.
This guide explains overcommitment from the ground up. You do not need to know Linux internals or Kubernetes scheduling before you begin. Every new word is explained when it first appears, and every big idea comes with an everyday comparison. By the end, you will understand how Kubernetes counts resources, what happens when the bet goes wrong, how to measure how much you are overcommitted, and how to choose a level that saves money without waking people up at night.
1What Overcommitment Really Means
Overcommitment means the total amount of resources promised to the pods on a node is larger than the amount the node can actually deliver.
A node is one machine in a Kubernetes cluster. It might be a physical server or a virtual machine in the cloud. Every node has a fixed amount of processing power, which we call CPU, and a fixed amount of memory, which we call RAM. Pods are the small packages that run your applications, and each pod asks for some of that CPU and memory. When the sum of what pods are allowed to use is higher than what the node owns, the node is overcommitted.
This sounds dangerous, and sometimes it is. But it is also normal and, in most companies, intentional. Applications rarely use all the resources they could. A web service might be sized for the busiest hour of the year but spend most days using a small part of its allowance. If every pod reserved its worst case, nodes would sit mostly idle and the bill would be huge.
Think of a shared office with fifty desks and eighty employees who each work from the office two or three days a week. The company gave every employee permission to use a desk, so on paper there are eighty promises for fifty desks. Most days it works perfectly. On a rare day when seventy people come in, some must stand or work in the cafeteria. Overcommitment is the same kind of planned sharing.
Capacity
The total CPU and memory that a machine physically has.
Request
The amount a pod asks Kubernetes to set aside for it when deciding where it should run.
Limit
The most a container is allowed to use, even if more is free on the machine.
Overcommit
Promising, through limits, more than the node can give if everyone asks at once.
Overcommitment is not a bug or a misconfiguration by itself. It is a trade-off between money saved and risk accepted, and the skill is choosing the right amount of it for each kind of workload.
2Requests and Limits: The Two Numbers That Matter
Every container can carry two numbers for each resource, and they do very different jobs.
When you describe a container, you can state a request and a limit for CPU and for memory. These two numbers are often confused, and the confusion causes most overcommitment problems. The request is a promise Kubernetes makes to the container about scheduling. The limit is a ceiling that the node enforces while the container is running. The first is about getting a seat. The second is about how much you may eat once you are seated.
| Number | Who Uses It | When It Matters | What It Controls |
|---|---|---|---|
| Request | The scheduler, and the node under pressure | When choosing a node, and when resources are scarce | Where a pod can be placed and how it is ranked for protection |
| Limit | The node’s kernel | While the container runs | The maximum it may use before being slowed or stopped |
At a restaurant buffet, the request is the table you reserved, which decides whether there is a place for you. The limit is the rule that says you may take at most three plates. The reservation does not stop you from eating less, and the plate limit does not guarantee food will be available if the buffet runs out.
The gap between request and limit
The distance between the request and the limit is called the burst room. A container that requests half a CPU and has a limit of two CPUs may use up to two CPUs when the node has spare capacity. When many containers use their burst room at the same moment, the node can run out, and that is when overcommitment turns from a saving into a problem.
Many people think a limit reserves resources. It does not. A limit only caps usage. Only the request is used to decide how much space a pod takes on a node when the scheduler places it.
3How the Scheduler Counts Resources
The scheduler is the part of Kubernetes that picks a node for each new pod, and it does arithmetic using requests only.
When a pod is created, the scheduler looks at every node and asks a simple question: after adding the requests of the pods already on the node, is there still enough room for this new pod’s requests? Room is measured against a number called allocatable, which we will explain in the next chapter. If the answer is yes, the node is a candidate. The scheduler then picks the best candidate using extra scoring rules.
Notice what the scheduler does not look at. It does not look at the limits when deciding whether a pod fits. It does not look at how much CPU or memory the existing pods are actually using right now. It only adds up the numbers people wrote down as requests. This is why requests are the most important numbers in a cluster, and why wrong requests cause so many surprises.
flowchart LR
A["New pod with requests and limits"] --> B["Scheduler reads requests only"]
B --> C["Sum requests of pods on each node"]
C --> D{"Fits within allocatable?"}
D -- "Yes" --> E["Node is a candidate"]
D -- "No" --> F["Node is skipped"]
E --> G["Best scoring node is chosen"]
A hotel booking system assigns rooms using the reservations written in its book. It does not walk through the corridors to see which guests are actually inside. If guests reserved big suites and use only a bed, the system still believes the suites are full, and new guests are turned away even though much of the hotel looks empty.
Two ways to get it wrong
If requests are too high
- Nodes look full while real usage is low
- The cluster scales up and the bill grows
- Capacity is wasted, but pods are safe
If requests are too low
- Too many pods are packed onto one node
- Real usage can exceed the machine’s capacity
- Slowdowns, evictions and restarts appear
The sum of requests on a node can never exceed what is allocatable. The sum of limits can, and that is exactly where overcommitment lives.
4Node Capacity, Allocatable and Reserved Space
Not all of a machine belongs to your pods. Some of it is kept back for the system itself.
Imagine a machine with sixteen units of memory. You might assume your pods can use all sixteen. In reality, the operating system needs memory to run. The Kubernetes helper program on each node, called the kubelet, needs memory. The container runtime, which actually starts containers, needs memory too. If your pods used every last unit, these essential programs would starve, and the whole node could become unresponsive.
Kubernetes handles this by subtracting reserved space from the total. The result is called allocatable, and it is the number the scheduler uses. Three kinds of reservation are commonly subtracted. One is set aside for Kubernetes system programs. One is set aside for the operating system’s own programs. The third is a safety margin called the eviction threshold, which is the amount of free memory the kubelet tries to keep so it has time to react before the machine runs dry.
flowchart TD
A["Node capacity: total physical resources"] --> B["Minus kube-reserved for Kubernetes programs"]
B --> C["Minus system-reserved for operating system"]
C --> D["Minus eviction threshold safety margin"]
D --> E["Allocatable: the space pods may be promised"]
A restaurant with fifty tables does not seat fifty parties. A few tables are kept for staff meals, a few are kept clear for the kitchen’s pass, and a few are left open in case of emergencies. The seating chart given to the host shows only the tables actually available for guests. Allocatable is that seating chart.
Why small nodes lose more
The reserved amounts are not a fixed percentage. Some of the reservation is a flat amount that every node pays no matter how big it is. That means small nodes lose a larger share to overhead than big nodes. A tiny machine might lose a quarter of its memory, while a large machine might lose only a few percent. This is one reason node size matters when you plan how much overcommitment you can safely allow.
Teams that set no reservations at all give pods the whole machine on paper. When pods use real memory up to the edge, the operating system or the kubelet can be starved, and the node stops responding even though no pod broke a limit. Always reserve space for the system.
5CPU: The Resource That Can Be Squeezed
CPU is called compressible, which means that when there is not enough, programs slow down but do not die.
CPU time is like a road shared by many cars. When there are more cars than lanes, everyone moves slower, but nobody is destroyed. This is why experts call CPU a compressible resource. If a node runs short of CPU, pods simply get less processing time each, and requests take longer to answer. That is annoying, but usually recoverable.
Kubernetes uses two different mechanisms for CPU, one for the request and one for the limit. The request is turned into a weight. When several containers want CPU at the same time, the kernel shares the processing time in proportion to those weights. A container with a request twice as large as its neighbor’s gets about twice the share when the machine is busy. When the machine is not busy, containers can use more than their request freely.
Picture a pizza being shared at a party. If only two people are hungry, each can eat as much as they like. If ten people arrive at once, everyone gets a slice in proportion to a fair share agreed earlier. The request is that agreed fair share. It only matters when the pizza is scarce.
The CPU limit works differently
The limit is not a weight. It is a hard cap enforced by dividing time into small windows, commonly one tenth of a second. In each window, the container receives a quota of CPU time. Once the container has used its quota, it is paused until the next window begins, even if the machine is completely idle. This pausing is called throttling. A throttled container does not crash, but it stops working for part of each window, and the pauses can add noticeable delay to every request it handles.
CPU request
Becomes a relative weight. Matters only when the node is busy and containers compete.
CPU limit
Becomes a time quota per window. Enforced even when the machine has idle CPU, causing throttling.
A container can be throttled while the node shows plenty of idle CPU. If your application feels slow but the node looks quiet, throttling caused by a tight CPU limit is a prime suspect.
Example: The slow checkout service
A shop’s checkout service has a CPU request of one core and a limit of one core. During a short burst of traffic, it needs two cores for a few moments but is capped, so it pauses repeatedly. Customers see slow pages even though the node is mostly idle. Raising or removing the limit fixes the delay.
6Memory: The Resource That Cannot Be Squeezed
Memory is incompressible. When it runs out, something must be killed.
Memory behaves very differently from CPU. A program that holds data in memory cannot work with less memory the way it can work with less CPU time. You cannot shrink a loaded photo or an open database table and expect the program to carry on. When the machine truly runs out of memory, the operating system has only one option: it must stop some program to free memory. This is why experts call memory an incompressible resource, and why memory overcommitment is far more dangerous than CPU overcommitment.
When a container tries to use more memory than its limit, the kernel stops it immediately. This event is called an out-of-memory kill, usually written as OOM kill. Kubernetes then notices the container died and restarts it according to the pod’s rules, and the pod’s status often shows the reason as out of memory. If the application keeps using too much memory, it will be killed again and again, and the pod will seem to be stuck in a restart loop.
A glass of water cannot hold more than its size. If you keep pouring, the water overflows no matter how politely you ask. CPU is more like a queue at a shop, where people just wait longer. Memory is the glass: when it is full, the next drop spills.
Two kinds of memory kill
Container-level kill
The container goes above its own memory limit. Only that container is stopped. Other pods on the node are not affected.
Node-level pressure
The whole node runs low on memory because too many pods are using real memory at the same time. The kubelet or the kernel must choose victims, even if no single pod broke its own limit.
The second case is the real danger of memory overcommitment. A node can have thirty pods, each safely under its own limit, and still run out of memory because the sum of their real usage exceeds the machine’s total. That is exactly the full-flight situation of our airline analogy.
Treat CPU overcommitment as a performance risk and memory overcommitment as a stability risk. Be generous with the first for non-critical workloads and careful with the second for everything.
7Quality of Service Classes
Kubernetes sorts every pod into one of three classes, and the class decides who is protected when things go wrong.
You never choose a quality-of-service class directly. Kubernetes assigns it automatically based on how you wrote the requests and limits. The class is then used by the node to decide which pods are most important to keep alive and which are the first to lose their place. Understanding the three classes is the key to predicting what will happen during a crunch.
Guaranteed
Every container in the pod has a CPU and memory request and limit, and the request equals the limit for each. These pods are the most protected and are never overcommitted individually.
Burstable
At least one container has a request or limit, but the pod does not meet the Guaranteed rule. These pods can use more than they asked for and are protected in proportion to their request.
BestEffort
No container has any request or limit at all. These pods get whatever is left over and are the first to be sacrificed.
On a crowded lifeboat, guests with a numbered seat ticket are the Guaranteed class: their seat is theirs. Guests with a ticket that says “a seat or standing room” are Burstable: they can stretch into free space but may be asked to give some back. People without tickets are BestEffort: they come aboard only if there is room and are the first to leave if the boat gets heavy.
| Class | How You Get It | Overcommit Exposure | When Resources Are Scarce |
|---|---|---|---|
| Guaranteed | Requests equal limits for CPU and memory in every container | None for the pod itself | Last to be stopped |
| Burstable | Some requests or limits, but not equal everywhere | Between request and limit | Stopped in order of how far above request they are |
| BestEffort | No requests or limits | Unbounded | First to be stopped |
Leaving requests and limits empty for many pods creates a node full of BestEffort pods. They look free but they are the first victims and they invisibly consume resources that other pods thought they had reserved.
Use Guaranteed for databases and critical services, Burstable for ordinary applications, and BestEffort only for work you do not mind losing, such as experiments.
8Node Pressure and Kubelet Eviction
When a node starts to run out of something, the kubelet steps in and removes pods on purpose to protect the machine.
The kubelet, the Kubernetes helper program on each node, constantly watches key signals such as how much memory is free, how much disk space is free, and how many process slots are left. You can set thresholds for these signals. When a signal crosses a threshold, the kubelet marks the node as under pressure and begins to evict pods, which means it stops them and asks the cluster to run them somewhere else.
flowchart TD
A["Kubelet watches signals"] --> B{"Threshold crossed?"}
B -- "No" --> A
B -- "Soft threshold" --> C["Wait for grace period"]
B -- "Hard threshold" --> D["Act immediately"]
C --> E["Mark node under pressure"]
D --> E
E --> F["Stop new pods from scheduling here"]
F --> G["Rank pods and evict until signal recovers"]
Soft and hard thresholds
A soft threshold gives pods a grace period. The kubelet waits to see whether the pressure goes away on its own, and if it does not, it begins a gentle shutdown that gives pods time to finish. A hard threshold has no waiting. The kubelet acts at once, because the machine is close to trouble. Most clusters have a default hard threshold for free memory, and teams that run overcommitted nodes usually tune these numbers carefully.
Who is evicted first?
When the kubelet must pick victims, it follows a clear order. Pods using more than they requested are considered before those within their request. Among those, lower priority pods go first. Among pods of equal priority, the ones using the most beyond their request go first. This is the practical meaning of the quality-of-service classes: BestEffort pods have no request, so any usage exceeds it, and they are first in line.
Pods using more than their request
They took more than they promised to need, so they are the first candidates.
Lower priority first
Among those candidates, pods with a lower priority value are removed earlier.
Largest overuse first
Among equals, the pod using the most beyond its request is removed first.
When a theater must clear seats for a fire drill, staff first ask people who took extra seats with their coats and bags to move, then work through the least important guests, and leave the guests who stayed neatly in their own seat for last. Eviction follows that same fairness rule.
An evicted pod is deliberately stopped by the kubelet and is usually recreated elsewhere by its controller. It still means users may see errors for a moment, so frequent evictions are a sign that nodes are packed too tightly.
9OOM Kills Versus Evictions
Two different systems can stop your pods for lack of memory, and they behave very differently.
People often use the words eviction and OOM kill as if they meant the same thing. They do not. An eviction is a polite, planned action by the kubelet. An OOM kill is a sudden action by the operating system kernel. Knowing which one happened tells you where to look for the cause.
| Question | Kubelet Eviction | Kernel OOM Kill |
|---|---|---|
| Who acts? | The kubelet | The Linux kernel |
| How fast? | Usually gradual, may give a grace period | Instant |
| What is removed? | Whole pod | One container process |
| What triggers it? | A node signal crossing a threshold | A container passing its memory limit, or the node truly running out |
| What do you see? | Pod shows an evicted status with a message | Container shows a terminated reason of out of memory |
| Can you prevent it? | Yes, by tuning thresholds and requests | Only by right sizing limits and usage |
An eviction is the building manager politely knocking on doors and asking tenants to leave because the building is too crowded. An OOM kill is the circuit breaker slamming off because too much power was drawn. One is organized and reasoned. The other is sudden and automatic.
The kernel’s scoring list
Linux keeps a score for each process that says how willing the system is to kill it when memory runs out. Kubernetes sets these scores according to quality-of-service class. Guaranteed pods receive a very protective score. BestEffort pods receive the highest score, so they are chosen first. Burstable pods receive a score that depends on how large their memory request is compared to the node, so pods with larger requests are somewhat safer. This is the second layer of protection after kubelet eviction and it matters when memory runs out faster than the kubelet can react.
The kubelet checks signals at intervals, so a sudden memory spike can outrun it. The kernel acts instantly as a last line of defense. Healthy clusters rarely need the kernel to step in at node level, because the kubelet acted earlier.
10Disk, Process Slots and Other Resources
CPU and memory get the attention, but nodes can run out of other things too.
A node has other limited resources that pods can exhaust. Pods use temporary disk space for logs, caches and scratch files, called ephemeral storage. Pods also create processes, and each node can only keep track of so many. If a single pod uses too many of these, the whole node suffers, and the kubelet reports it as disk pressure or process-slot pressure.
Ephemeral storage
Temporary disk used by a pod’s writable layer, logs and scratch folders. Can have requests and limits like CPU and memory.
Process slots
Each node can only run a limited number of processes. A runaway program creating thousands can starve neighbors.
Image storage
Downloaded container images take space, and a node with many different images can fill its image disk.
Network and device limits
Network bandwidth, special hardware and open file counts are other shared resources that can become bottlenecks.
A shared kitchen has limited stove burners, fridge space and counter room. If one cook fills the whole fridge with leftovers, nobody else can store anything, even though the stoves are free. Every shared resource needs its own fair rules, not just the glamorous ones.
Example: The chatty logger
An application writes huge debug logs to its own folder. Over several hours, it fills the node’s disk. The kubelet detects disk pressure and begins evicting pods that have nothing to do with the problem. A limit on ephemeral storage would have stopped the chatty pod first.
Teams carefully tune CPU and memory and forget storage. Disk pressure evicts pods just as readily as memory pressure does, and it is often harder to diagnose because dashboards focus on CPU and memory.
11Why Teams Overcommit on Purpose
If overcommitment is risky, why does almost every large cluster do it? Because the alternative is expensive waste.
Studies and field reports from many organizations repeatedly show that most Kubernetes clusters use a small fraction of the CPU and memory that is requested for them. Teams ask for generous amounts to feel safe, and then the applications use far less on a typical day. If the cluster honored every request as a true reservation of real usage, most of the money spent on machines would buy idle capacity.
A city does not build a road wide enough for every resident to drive at the same moment. It builds a road for the usual rush hour and accepts that on the rare day of a big event there will be a traffic jam. Building for the worst possible day would cost far more than the jam ever costs.
Benefits of a planned overcommit
Advantages
- Higher utilization means fewer nodes and a lower bill
- Bursty applications can use idle capacity during spikes
- Different workloads peak at different times, so sharing smooths demand
- Teams are not forced to size every pod for its worst second
- Faster scheduling because more pods fit
Disadvantages / Trade-offs
- Noisy neighbors can slow each other down
- Memory pressure can cause evictions and restarts
- Performance becomes harder to predict
- Capacity planning must rely on real usage, not only on paper numbers
- Incidents are harder to diagnose because many pods share a machine
Statistical multiplexing in plain words
The idea behind safe overcommitment has a fancy name, statistical multiplexing. It simply means that when many independent workloads share a resource, their peaks rarely line up. One service is busy in the morning, another at night, a batch job runs at midnight. Added together, the total demand is smoother and lower than the sum of every individual peak. The more independent the workloads, the safer the bet.
The bet fails when peaks line up, for example when a big sale makes every shopping service busy together, or when a failure makes every pod restart at once. Correlated demand is the enemy of overcommitment.
12Measuring How Overcommitted You Are
You cannot manage a risk you have not measured. A few simple ratios tell you exactly where you stand.
To measure overcommitment, you compare three totals for each node or for the whole cluster: the sum of requests, the sum of limits, and the real usage. All three are compared against allocatable. Each ratio answers a different question and together they give a complete picture.
Requests to allocatable
How full the node looks to the scheduler. Cannot exceed one hundred percent. Shows how much room is left for new pods.
Limits to allocatable
The overcommit ratio. Above one hundred percent means the node has promised more burst room than it owns.
Usage to allocatable
How busy the node really is. This is the number that predicts pressure and evictions.
Usage to requests
How accurate your requests are. Very low means waste. Above one hundred percent means pods are leaning on burst room.
flowchart LR
A["Sum of limits: promised burst room"] --> D["Allocatable: what the node can really give"]
B["Sum of requests: what the scheduler counted"] --> D
C["Real usage: what pods consume now"] --> D
D --> E["Compare the three to judge risk"]
A bank has three numbers worth watching: the total of all credit cards it has issued, the total that customers have actually borrowed, and the cash it holds. The bank survives as long as actual borrowing stays well within cash, even though the total credit limits are far larger. Limits are credit cards, usage is borrowing, and allocatable is cash.
Reading the results
| What You See | What It Usually Means | Typical Action |
|---|---|---|
| Requests high, usage low | Requests are padded | Right size requests downward |
| Requests low, usage high | Pods are squeezing onto nodes and bursting | Raise requests to match reality |
| Limits far above allocatable, usage low | Safe overcommit for now | Watch peaks, keep a safety margin |
| Usage close to allocatable | Pressure is near | Add capacity or lower density |
Averages hide danger. A node that averages forty percent memory usage but spikes to ninety-five percent for a minute every hour is a node that will produce evictions. Measure high percentiles over a long enough window.
13Noisy Neighbors and Cascading Failures
The worst overcommitment stories are rarely about one pod. They are about one pod hurting many others.
A noisy neighbor is a pod that uses so much of a shared resource that other pods on the same node suffer. With CPU, neighbors see slower response times. With memory, neighbors may be evicted. With disk and network, everyone’s input and output slows. Since the pods are different applications owned by different teams, nobody sees the connection until someone digs deep.
In an apartment building with thin walls, one resident’s loud party keeps everyone awake. The loud resident did nothing wrong by their own rules, and the building rules simply did not limit noise. Resource limits are the noise rules of a shared node.
The cascading failure
The most dangerous pattern is the cascade. A node comes under memory pressure and evicts several pods. Their controllers recreate them on other nodes. Those nodes now receive extra load and may also reach their thresholds. They evict pods, which move again, and the problem spreads across the cluster like a falling row of dominoes. Meanwhile, applications that restart need extra CPU and memory to start up, which adds further pressure.
flowchart TD
A["Node A under memory pressure"] --> B["Pods evicted from Node A"]
B --> C["Pods rescheduled onto Node B and C"]
C --> D["Startup spikes add load to B and C"]
D --> E["Node B reaches pressure"]
E --> F["More evictions and restarts"]
F --> C
Problem
Running every node at very high memory density with large gaps between requests and limits and no spare capacity anywhere.
Why It’s Harmful
Any single failure forces pods onto nodes that are already nearly full, which triggers more evictions and creates a chain reaction.
Correct Approach
Keep headroom across the fleet, set memory requests close to real usage, and test what happens when one node disappears.
Size the cluster so that the loss of one node, or one failure zone, still leaves enough room for the displaced pods. If you cannot survive one failure, your overcommit level is too high.
14The CPU Limit Debate
Should you set CPU limits at all? Experienced teams disagree, and understanding both sides is part of mastering overcommitment.
Because CPU limits cause throttling even when the machine is idle, many experienced engineers recommend setting CPU requests carefully but leaving CPU limits off for most workloads. Their reasoning is that CPU is compressible, requests already guarantee a fair share when the node is busy, and limits only add pauses when there is spare capacity that could have been used for free.
Other teams keep CPU limits for good reasons. Limits give predictable behavior, which is valuable for performance testing. They stop a runaway program from consuming a whole node. And they are required to reach the Guaranteed class, which has special benefits such as exclusive CPU cores when the node uses a static CPU management policy.
Reasons to Skip CPU Limits
- No throttling when the node has idle CPU
- Better latency for bursty services
- Requests still protect fair sharing under load
- Higher utilization from unused CPU
Reasons to Keep CPU Limits
- Predictable performance for testing and capacity work
- Protection against a runaway process
- Needed for the Guaranteed class
- Useful fairness in shared multi-tenant clusters
A highway can have a speed limit sign even on an empty road at midnight. The sign keeps everyone predictable but it also slows a driver who could have gone faster safely. Removing signs works well on a quiet road and badly in a busy city. The right choice depends on who shares your road.
A sensible middle path
Always set CPU requests
They drive scheduling and fair sharing, and they are never optional for serious workloads.
Use generous CPU limits or none for latency-sensitive services
Avoid throttling where response time matters most.
Use tight limits for batch and untrusted workloads
Protect neighbors from jobs that would otherwise consume everything.
Watch throttling metrics
Measure how often containers are paused before and after any change.
15Memory Strategy: Make Requests Match Reality
For memory, the safest overcommit strategy is to be honest in requests and generous but not unlimited in limits.
Because memory cannot be squeezed, overcommitting it is far riskier than overcommitting CPU. A widely used practice is to set the memory request equal to the memory limit for important workloads. This removes burst room for memory, so the pod’s maximum use is known to the scheduler, and the node cannot be surprised by the pod growing past what was counted. Such pods may still be Burstable if CPU is configured differently, but their memory behavior is fully predictable.
When you book a hotel room for a family, you tell the hotel exactly how many people are coming. If you say two and bring six, the hotel’s plan breaks. Setting memory request equal to limit is telling the hotel the real family size from the start.
How to choose the memory request
A good starting point is to observe the real memory usage of the application over a typical busy period, including startup and any scheduled heavy tasks, and set the request to cover its normal peak with a small margin. If you set it too low, the node counts too little and packs too many pods. If you set it too high, you waste money, but the pod is safe.
How to choose the memory limit
Choose a limit that is above the highest healthy usage but not so far above that a bug could eat a whole node. Programs written in languages that manage memory automatically need extra care. Their own settings for how much memory they may take should be aligned with the container limit, otherwise the program may believe it has more memory than the container allows and be killed unexpectedly.
Example: The runtime that did not know its limit
A service runs on a machine with plenty of memory, and its runtime sizes its heap based on the whole machine. Inside a container with a smaller limit, the heap grows beyond that limit and the container is killed again and again. Teaching the runtime about the container limit fixes the restarts.
Setting a memory limit far above the request on many pods creates large hidden overcommitment. Everything looks fine until usage grows together, and then nodes run out of memory with no warning from the scheduler.
Overcommit CPU freely for flexible workloads. Overcommit memory only with a clear reason, a measured margin and a plan for what happens when the bet loses.
16Guardrails: LimitRange and ResourceQuota
Good habits are easier when the platform enforces them. Kubernetes offers two namespace-level tools for exactly that.
Asking every developer to write sensible requests and limits works for a while, and then someone forgets. Two built-in tools help the platform team make good behavior automatic. A LimitRange sets defaults and boundaries for each pod or container in a namespace. A ResourceQuota sets a total budget for the whole namespace.
LimitRange
Gives containers default requests and limits when they forget, and sets minimum and maximum sizes, and can control the allowed ratio between limit and request.
ResourceQuota
Caps the total requests, total limits, number of pods and other objects that a namespace may use, so one team cannot take the whole cluster.
A LimitRange is the rule that every guest at a buffet gets a standard plate and may not take more than a certain amount in one go. A ResourceQuota is the budget given to each table so that one table cannot clear the whole buffet. Both rules together keep the meal fair.
Controlling the overcommit ratio
The LimitRange can set a maximum ratio between limit and request. For example, a platform team may say that no container may have a memory limit more than one and a half times its request. This is a very direct way to cap overcommitment, because it prevents the huge gaps that create hidden risk. Different namespaces can have different ratios, so a sandbox can be generous while production is strict.
Set defaults
Make sure no pod ends up as BestEffort by accident, because defaults fill in missing values.
Set minimum and maximum sizes
Prevent tiny, unrealistic requests and giant, wasteful ones.
Cap the limit-to-request ratio
Bound how much hidden overcommit any one pod can create.
Add a namespace quota
Give each team a total budget so the cluster stays fair overall.
A default applied to many pods silently becomes the real size of your cluster. A poorly chosen default, repeated across hundreds of pods, will skew every capacity number you have. Review defaults as carefully as any other important setting.
17Right-Sizing with Autoscalers
Humans are bad at guessing resource needs. Automatic tools can watch real usage and suggest better numbers.
Few people can predict how much CPU and memory an application will use. They usually guess high, and the guess becomes permanent. Kubernetes has several autoscaling tools that adjust capacity based on what really happens, and each works on a different level.
Horizontal Pod Autoscaler
Adds or removes copies of a pod based on usage compared with the request. It scales out when load grows.
Vertical Pod Autoscaler
Watches real usage and recommends, or applies, better request values for each container.
Cluster autoscaling
Adds or removes whole nodes when pods cannot be placed or nodes sit underused.
In-place resizing
A newer capability that lets a running pod’s resource values change without recreating it. Its maturity depends on your Kubernetes version, so check the release notes.
flowchart LR
A["Real usage over time"] --> B["Vertical autoscaler: right size each pod"]
A --> C["Horizontal autoscaler: right number of pods"]
B --> D["Accurate requests"]
C --> D
D --> E["Cluster autoscaler: right number of nodes"]
A restaurant adjusts three things: how big each table should be, how many tables are set out, and how many dining rooms are open. The vertical tool resizes tables, the horizontal tool adds tables, and the cluster tool opens or closes rooms. A smart manager uses all three.
Be careful when mixing them
The horizontal autoscaler uses usage as a percentage of the request. If the vertical autoscaler keeps changing the request on the same metric, the two can chase each other and cause unstable behavior. A common rule is to avoid letting both act on the same resource for the same workload, or to use the vertical tool only in recommendation mode and apply its advice through your normal review process.
Run the vertical autoscaler in recommendation mode first. It will show you which pods are wildly over-requested, which gives you a safe, evidence-based list of savings without any automatic restarts.
18Node Size, Bin Packing and Density
Choosing the size of your machines changes how much overcommitment you can survive.
Fitting pods onto nodes is a packing puzzle called bin packing. Think of nodes as boxes and pods as items of different sizes. Your goal is to fill the boxes well without breaking any. The size of the box matters a great deal. Small boxes waste more space on overhead and leave awkward gaps, and big boxes pack tightly but make each box failure more painful.
| Choice | Benefits | Risks |
|---|---|---|
| Many small nodes | A single node failure affects few pods, and scaling is fine grained | Reserved overhead is a larger share, and large pods may not fit |
| Few large nodes | Better packing, lower overhead share, room for big pods | A failure removes many pods at once, and noisy neighbors multiply |
| Mixed pools | Match node shape to workload type | More pools to operate and monitor |
Packing a moving truck is easier with a mix of large and small boxes, but if you put everything in one giant crate, losing that crate loses everything. Splitting belongings across several medium boxes is a sensible compromise between easy packing and safe travel.
Match nodes to workload shape
Some workloads need a lot of memory for each unit of CPU, such as caches and databases. Others need a lot of CPU for each unit of memory, such as encoding and calculation. If your nodes have a fixed ratio between the two that does not match the workload, one resource runs out while the other sits idle. This is called stranded capacity. Choosing machine families whose CPU to memory ratio fits the workload recovers real money.
Example: Stranded memory
A team runs CPU-heavy services on nodes with lots of memory. CPU requests fill each node while half the memory remains unused and unusable. Switching to nodes with less memory per CPU lowers cost without any change to the applications.
Pods per node is one of the simplest dials for overcommitment. Raising it saves money and raises risk. Measure the effect on latency, restarts and evictions every time you turn it.
19Priority and Preemption
When room runs out, some pods matter more than others, and Kubernetes lets you say which.
A priority class is a named number that you attach to pods. Higher numbers mean more important. Priority affects two moments. When the scheduler cannot find room for an important pod, it can preempt, which means it removes one or more lower-priority pods from a node to make room. And when a node is under pressure, priority is one of the factors the kubelet uses to decide whom to evict.
A hospital emergency room treats the most urgent patient first, even if others arrived earlier. If the beds are full and an ambulance brings a critical case, a stable patient is moved to a waiting area. Priority classes give your cluster the same triage rules.
System critical
Components that keep the cluster itself working. These should almost never be displaced.
Business critical
Customer-facing services and databases that must stay up.
Standard
Ordinary internal applications that can tolerate short disruption.
Opportunistic
Batch jobs and experiments that use spare capacity and are happy to be displaced.
Priority makes overcommit safer
A powerful pattern is to fill leftover capacity with low-priority work. These pods use space that would otherwise be idle, so utilization rises. When an important pod needs room, the low-priority pods are preempted and the important pod is placed quickly. You get the money savings of high utilization while the important workloads stay protected.
If every team labels its pods as the most important, the labels mean nothing, and preemption becomes chaos. Restrict who may use the highest priority classes, and keep the number of tiers small and well documented.
Problem
Giving all workloads the same high priority so that nothing is ever displaced.
Why It’s Harmful
When the node is pressured, the kubelet has no useful ordering to follow, and important pods are evicted at random alongside unimportant ones.
Correct Approach
Define a small set of tiers, give batch and experimental work low priority, and protect high tiers with approvals.
20Swap and Newer Memory Features
Kubernetes has long avoided swap, but the story is changing, and it is worth understanding why.
Swap is disk space that the operating system can use as emergency memory. When real memory runs low, less used data is written to the disk to make room. Disk is much slower than memory, so a heavy swapper feels sluggish, but it may survive where it would otherwise be killed. For years, Kubernetes required nodes to have swap turned off, because it made resource accounting unpredictable, and a pod using swap breaks the clean idea that a memory limit is a hard wall.
More recently, Kubernetes has added optional, controlled swap support that is designed to be safer, with swap offered mainly to pods in certain classes. Details such as which classes are eligible, default behavior and feature maturity have changed from release to release. Always check the documentation for your version before relying on it.
Swap is like keeping overflow boxes in the garage when the house is full. The house stays usable and nothing is thrown away, but fetching a box from the garage takes much longer than reaching a shelf. It is a good emergency valve and a bad everyday storage plan.
Advantages
- Smooths short memory spikes without immediate kills
- Lets rarely used memory pages move out of the way
- Can raise density for some workloads
Disadvantages / Trade-offs
- Disk is far slower than memory, so latency suffers
- Makes the meaning of a memory limit less simple
- Sustained swapping can hide a capacity problem until it becomes severe
- Feature behavior depends on your Kubernetes version
Use it, if at all, as a short-term cushion for bursts. Alert on sustained swap use, because it means the node needs more memory or fewer pods.
Kubernetes memory-related features, including swap support and improvements to how memory requests are protected by the kernel, are evolving. Treat the concepts here as stable and the exact feature status as something to verify in the release notes.
21Observability: What to Watch
Overcommitment is safe only when you can see the early warning signs.
Because overcommitment depends on real usage staying below real capacity, monitoring is not a nice extra. It is the safety system. The signals fall into four families: how full the node is, how hard containers are being squeezed, how often pods are being removed, and how accurate your requests are.
Node fullness
Memory and CPU usage as a share of allocatable, plus free disk and process slots. Watch high percentiles over time.
Squeeze signals
CPU throttling rates for containers and pressure indicators from the kernel that show how long tasks wait for resources.
Removal events
Counts of evictions, out-of-memory kills, node pressure conditions and pod restarts.
Request accuracy
Usage compared to requests for each workload, which shows padding and under-sizing.
A pilot watches fuel, altitude, engine temperature and weather together. One gauge alone can mislead, but all four together tell the whole story. Node fullness, squeeze, removals and accuracy are your cockpit gauges.
Pressure stall information
Modern Linux kernels can report how much time tasks spend waiting because a resource is short. This is called pressure stall information. It is more honest than a usage percentage, because a node can show moderate usage while tasks still wait. If CPU, memory or disk pressure numbers start rising, the node is already hurting even if it has not crossed an eviction threshold.
Dashboards for trends
Show requests, limits and usage per node and namespace over weeks to reveal slow growth.
Alerts for danger
Page people for node pressure conditions, repeated out-of-memory kills and rising eviction counts.
Reports for cost
Share a regular list of the most over-requested workloads so owners can fix them.
Combine resource alerts with latency and error alerts for your applications. A throttled service may show no node alarm at all but still make customers wait.
22Isolating Workloads with Node Pools
Not every workload should share a node. Separating them is the simplest way to control the blast radius of overcommitment.
A node pool is a group of nodes with the same shape and purpose. By using labels, taints and tolerations, you can steer pods onto the pool that suits them and keep unlike workloads apart. This lets you run aggressive overcommitment where failure is cheap and conservative settings where failure is costly.
flowchart TD
A["Cluster"] --> B["Critical pool: Guaranteed pods, no overcommit"]
A --> C["General pool: Burstable pods, moderate overcommit"]
A --> D["Batch pool: BestEffort and low priority, high overcommit"]
An airport has separate terminals for passengers, cargo and private jets. Each terminal has rules suited to its traffic, and a delay in cargo does not stop the passengers. Node pools are terminals for your workloads.
Pool for databases and critical services
Run Guaranteed pods with requests equal to limits. No burst room and no surprises. Reserve extra headroom on these nodes.
Pool for general applications
Run Burstable pods with moderate gaps between requests and limits. Overcommit CPU more than memory.
Pool for batch and experiments
Run low-priority work that can be interrupted. Use high density and accept preemption, since any loss is cheap.
Every extra pool adds management work and strands capacity inside small groups. Start with a handful of pools that match real differences in risk, and add more only when evidence demands it.
23Multi-Tenant Fairness and Cost Accountability
When many teams share a cluster, overcommitment is not only a technical question. It is a question of who pays and who is protected.
In a shared cluster, a team that over-requests wastes money that everyone else’s bill carries, and a team that under-requests pushes risk onto its neighbors. Without feedback, teams have little reason to improve. Cost visibility changes behavior: when each team can see how much of its requested capacity it actually uses, over-requesting becomes something people notice and fix.
Show requested versus used
Give every team a simple view of requests, usage and the money wasted by the difference.
Charge by request
Because requests decide how much of the node a pod blocks, chargeback based on requests aligns cost with the capacity taken from others.
Set namespace quotas
Budgets stop one team from absorbing the cluster and encourage planning.
Review regularly
A monthly review of the top over-requested workloads keeps padding from creeping back.
Housemates who split the electric bill by the number of lights each has installed will quickly learn to take fewer lights. Splitting by requested capacity does the same for pods: people ask for what they need when asking has a price.
Present waste reports as helpful suggestions with ready-to-apply numbers, not as blame. Teams respond to clear, easy savings far more than to scolding.
24Design Patterns and Anti-Patterns
Experienced teams keep meeting the same good and bad shapes. Learn them early.
Patterns that work
Honest requests
Set requests from measured usage so the scheduler’s picture of the node matches reality.
Memory equal, CPU flexible
Set memory request equal to limit for predictability, and allow CPU to burst.
Fill gaps with low priority
Use idle capacity for interruptible batch work that yields to important pods.
Reserve for the system
Always keep space for the operating system, the kubelet and the runtime.
Separate by risk
Use node pools so critical and experimental workloads do not share a fate.
Guardrails by default
Use LimitRange defaults and quotas so good behavior needs no effort.
Patterns that hurt
Problem
Leaving requests empty and relying on the cluster to sort it out.
Why It’s Harmful
The scheduler sees these pods as free, so it packs far too many on one node. They become first in line for eviction and still consume real resources.
Correct Approach
Require requests through defaults and policy so nothing runs as BestEffort by accident.
Problem
Copying the same request and limit values from a template into every service.
Why It’s Harmful
Services have very different needs. Some are starved and some are padded, and all capacity numbers become misleading.
Correct Approach
Size each workload from its own measured usage and revisit the numbers regularly.
Problem
Tuning once at launch and never looking again.
Why It’s Harmful
Applications change, traffic grows and numbers drift. A setting that was safe last year becomes either wasteful or dangerous.
Correct Approach
Schedule reviews and use autoscaling recommendations to keep requests current.
25A Step-by-Step Playbook for Safe Overcommitment
If you are starting from a messy cluster, here is a calm order of work that raises efficiency without breaking anything.
Measure the current state
Collect requests, limits and usage per workload and per node. Find the biggest gaps and the nodes closest to pressure.
Fix the dangerous items first
Find BestEffort pods and pods with tiny requests but large usage. Give them honest requests before anything else, since they create hidden risk.
Set up reservations and thresholds
Make sure every node reserves space for the system and has sensible eviction thresholds.
Add guardrails
Roll out default sizes, ratio limits and namespace quotas in warning-friendly steps.
Right-size the padded workloads
Lower over-large requests gradually, starting with non-critical services, and watch latency and restarts.
Introduce priorities and batch filling
Give low-priority work a place in the spare capacity so utilization rises safely.
Test failure
Drain a node in a test window and watch whether the cluster absorbs it without a cascade.
Repeat on a schedule
Keep measuring and adjusting. Overcommitment is a habit, not a one-time project.
Tuning a car engine is done in small steps, checking the results after each change. Nobody replaces ten parts at once and hopes for the best. The same patience applies to resource tuning.
When several settings change together, you cannot tell which one helped or hurt. Make one change, wait through a full traffic cycle, and compare before making the next.
26Troubleshooting Like a Detective
Most overcommitment problems show up as one of four complaints. Each has a short list of checks.
Complaint 1: “My pod keeps restarting”
Read the last termination reason
An out-of-memory reason means the container passed its memory limit. Other reasons point to application errors instead.
Compare usage with the limit
If usage climbs steadily to the limit, suspect a memory leak or a limit that is too small for the workload.
Check the runtime’s own memory settings
Make sure the application knows about the container limit and does not size itself to the whole machine.
Complaint 2: “My pod was evicted”
Read the status message, which names the signal, such as low memory or low disk. Then check node conditions and recent usage on that node. Look at the pod’s class and how far above its request it was running. If eviction repeats, either raise the request so the pod is better protected and the scheduler places it on a roomier node, or reduce density on the nodes.
Complaint 3: “My service is slow but nodes look idle”
Look for CPU throttling on the container. A tight CPU limit can pause a container even while the node is quiet. Compare the limit with the container’s peak needs. Consider raising the limit or removing it for latency-sensitive services, then watch the throttling metrics fall.
Complaint 4: “Pods are pending though the cluster looks empty”
The scheduler counts requests, not usage. Nodes may be fully requested even though they are mostly idle. Find the over-requested workloads and right-size them, or add capacity if the requests are accurate.
Restarts
Suspect memory limits, leaks or runtime settings.
Evictions
Suspect node pressure, low requests or high density.
Slowness
Suspect CPU throttling or noisy neighbors.
Pending pods
Suspect padded requests or true lack of capacity.
A good mechanic listens to the complaint, checks the simplest causes first, and only then opens the engine. Following the cheap checks in order saves hours of guessing.
Turn these four complaints into a one-page runbook for your support channel. Teams that can diagnose the common cases themselves free the platform team for the hard problems.
27Exclusive CPUs: When Overcommit Is Switched Off
For the most sensitive workloads, Kubernetes can give a pod its own private CPU cores that nobody else may touch.
Normally, many pods share all the CPU cores of a node, and the kernel moves their work around freely. That sharing is what makes CPU overcommitment possible. But some applications, such as trading systems, telephone switching and high-speed packet processing, suffer when their work keeps moving between cores. For them, a node can run a static CPU management policy. Under this policy, a pod in the Guaranteed class that asks for a whole number of CPUs is given exclusive cores, and other pods are kept off those cores.
A shared office has hot desks that anyone may use, which keeps costs low. A few specialists, such as a surgeon in a hospital, get their own private room with their own equipment because interruption would be too costly. Exclusive CPUs are the private rooms of a cluster.
Advantages
- Very steady performance with little interference
- No CPU throttling from neighbors competing for the same core
- Better use of processor caches for sensitive code
Disadvantages / Trade-offs
- Reserved cores cannot be shared even when idle
- Lower overall utilization and higher cost
- Only suits whole-number CPU requests in the Guaranteed class
- Needs separate nodes or careful planning
Exclusive cores are the opposite of overcommitment. Use them only where measured performance proves they are needed, and place those workloads in their own node pool.
28Headroom, Failure Zones and Capacity Planning
The right amount of overcommitment depends on how much spare room you keep for bad days.
Headroom is the unused capacity you deliberately keep. It absorbs traffic spikes, node failures and rollouts. A cluster with no headroom has no way to survive an unexpected event. A cluster with huge headroom wastes money. The art of capacity planning is picking the smallest headroom that still lets you survive your realistic worst days.
Think in failure zones
Cloud clusters are often spread across several failure zones, which are separate data center areas. If one zone fails, all the nodes there disappear together, and their pods must be placed on the remaining zones. If you have three zones and run each at high density, the loss of one zone requires the other two to absorb fifty percent more load. That is impossible if they were already near full. This simple arithmetic tells you how much headroom to keep.
| Number of Zones | Load Increase on Survivors if One Zone Fails | Maximum Safe Average Fullness |
|---|---|---|
| 2 | Doubles | About half |
| 3 | One and a half times | About two thirds |
| 4 | One and a third times | About three quarters |
If a restaurant has three dining rooms and one room must close, the other two rooms must hold all the guests. The restaurant can only promise that if each room is at most two thirds full on a normal night. Headroom is the empty seats that make the promise true.
Other uses of headroom
Traffic spikes
Sudden growth in demand needs spare room while new nodes are still starting.
Rolling updates
Updates briefly run old and new pods together, which needs temporary extra capacity.
Node maintenance
Draining a node for upgrades pushes its pods elsewhere.
Autoscaler delay
New nodes take minutes to appear. Headroom covers the gap.
Cluster autoscaling reacts to pods that are already pending, and adding a node takes time. If your overcommit plan assumes new nodes will arrive instantly, a sharp spike can still cause pressure and evictions before help arrives.
Some teams run low-priority placeholder pods that hold spare room. When real work arrives, the placeholders are preempted and real pods start instantly while the autoscaler adds new nodes behind the scenes.
29Best Practices and Common Mistakes
A short checklist of habits that separate calm clusters from stressful ones.
Best Practices
- Set requests for every container from measured usage
- Keep memory requests equal to limits for important workloads
- Reserve space for the system on every node
- Use priority classes and fill gaps with interruptible work
- Apply LimitRange defaults and namespace quotas
- Watch throttling, pressure, evictions and out-of-memory kills
- Keep headroom for losing a node or a zone
Common Mistakes
- Leaving requests empty and creating BestEffort pods
- Large gaps between memory request and limit
- Tight CPU limits on latency-sensitive services
- Ignoring disk and process-slot limits
- Sizing from averages instead of peaks
- Running every node near full
- Tuning once and never revisiting
Three questions before changing any number
What does the evidence say?
Look at real usage over a long enough window including peaks. Do not tune from a hunch.
What happens if the bet loses?
Decide whether the worst case is a slow page, a restart or lost data, and tune the risk to match.
How will we know quickly?
Make sure an alert or dashboard will show the effect of the change within minutes.
The easiest saving in most clusters is lowering padded CPU requests on non-critical services. It carries little risk, because CPU is compressible, and it frees real capacity quickly.
A cluster running at ninety percent usage can look wonderful on a cost dashboard and be one traffic spike away from an outage. Efficiency must always be balanced against reliability.
30Real-World and Industry Examples
Different industries accept different levels of overcommitment because their cost of failure differs.
Online retail: seasonal peaks
A shop runs its checkout and payment services with generous memory requests and low overcommit, while search, recommendations and image processing run with more burst room. Before a big sale, the team raises headroom, because correlated demand is exactly the situation that breaks overcommit bets.
Streaming media: batch encoding on spare capacity
A video company fills idle capacity with low-priority encoding jobs that use whatever CPU is free. When live-streaming pods need room, the encoding jobs are preempted. Utilization stays high, and viewers never notice.
Financial services: strict isolation for critical systems
A bank puts trading and payment systems on dedicated nodes with Guaranteed pods and sometimes exclusive CPUs. Reporting and analytics, which tolerate delay, share a heavily overcommitted pool. Risk is matched to the business cost of failure.
Software platform: internal developer environments
A company with hundreds of developers runs short-lived test environments with tight quotas and high overcommit. Idle environments are cleaned up automatically, and the cost of an occasional slow test is small compared with the savings.
Machine learning: expensive hardware and mixed jobs
A data team runs training jobs on costly accelerator nodes with exact requests and no overcommit for the scarce hardware, while data preparation steps use cheaper pools with relaxed settings.
31Inside the Node: How the Kernel Enforces Your Numbers
Kubernetes writes the numbers down, but the Linux kernel is the one that actually enforces them.
Kubernetes itself does not slow down or stop containers. It translates the requests and limits you wrote into settings for a Linux feature called control groups, usually shortened to cgroups. A control group is a labeled bucket that the kernel keeps around a set of processes. The kernel tracks how much CPU, memory and other resources each bucket is using and applies the rules attached to that bucket. Every container gets its own bucket, every pod gets a larger bucket that holds its containers, and quality-of-service classes get buckets above that.
flowchart TD
A["Node root bucket"] --> B["Guaranteed pods bucket"]
A --> C["Burstable pods bucket"]
A --> D["BestEffort pods bucket"]
C --> E["Pod bucket"]
E --> F["Container bucket with limit settings"]
Think of a school where each class has a locker room, each student has a locker, and the principal’s office keeps a rule sheet for each one. The teachers do not enforce locker size; the janitor, who is the kernel, checks every locker against its rule sheet. Kubernetes writes the rule sheets, and the kernel does the checking.
What each setting becomes
| Kubernetes Setting | What the Kernel Receives | What You Feel |
|---|---|---|
| CPU request | A relative weight for sharing time | Fair share when the node is busy |
| CPU limit | A time quota per short window | Throttling once the quota is used |
| Memory request | Mostly a scheduling number, plus an input to how likely a process is to be chosen as a victim | Better protection when memory is short |
| Memory limit | A hard memory ceiling for the bucket | Out-of-memory kill when exceeded |
Why a memory request is not a reservation
Many people believe that a memory request puts that memory aside for the pod. By default it does not. The kernel does not hold physical memory back for a container just because it asked. The request is used by the scheduler to count room and by the node to rank pods under pressure. This is one more reason why the real usage of all pods, not only the sum of requests, decides whether a node is safe.
Newer Kubernetes releases have been adding optional features that make the kernel protect memory requests more actively. Their status changes between versions, so confirm in the release notes before depending on them.
When a surprising slowdown or kill happens, knowing that the kernel is the real enforcer tells you where to look: kernel counters about throttling, memory events and pressure are the ground truth behind every dashboard.
32A Worked Example with Real Numbers
Numbers make the ideas stick. Let us follow one small node from empty to overcommitted.
Imagine a node with sixteen gigabytes of memory and eight CPU cores. The team reserves one gigabyte of memory for the system and the kubelet, and keeps a half gigabyte as an eviction margin. That leaves fourteen and a half gigabytes of allocatable memory. Suppose seven cores are allocatable on the CPU side after similar reservations.
Step 1: Place the pods
The team places ten identical pods on the node. Each asks for one gigabyte of memory and half a core as a request, and has a limit of two gigabytes and one core. The scheduler adds up requests: ten gigabytes of memory and five cores. That fits inside fourteen and a half gigabytes and seven cores, so all ten pods are accepted, and there is still room for a few more.
Step 2: Count the promises
Now add up the limits. Ten pods with two gigabyte limits make twenty gigabytes of memory limit. Ten pods with one core limits make ten cores of CPU limit. Compared with allocatable, the memory limit total is about one hundred and thirty-eight percent and the CPU limit total is about one hundred and forty-three percent. The node is overcommitted on both resources, even though requests show it is only about seventy percent full.
| Measure | Memory | CPU | Percent of Allocatable |
|---|---|---|---|
| Sum of requests | 10 GB | 5 cores | About 69 percent memory, 71 percent CPU |
| Sum of limits | 20 GB | 10 cores | About 138 percent memory, 143 percent CPU |
| Typical real usage | About 7 GB | About 2 cores | About 48 percent memory, 29 percent CPU |
Step 3: Watch a good day and a bad day
A normal day
Usage stays near seven gigabytes and two cores. Nothing is throttled, nothing is evicted. The overcommit saved money and nobody noticed it.
A busy CPU day
Traffic doubles and the pods want five cores in total. That fits within seven allocatable cores, so each pod is only slowed a little by sharing. CPU overcommit is forgiving.
A bad memory day
A bug makes six pods grow toward their two gigabyte limits at once. Together they want twelve gigabytes, plus the other four pods’ normal use, which brings the total above fifteen. That is more than allocatable. The node crosses its threshold and the kubelet begins evicting pods that are above their request.
Ten guests were each promised a seat at a table plus the right to borrow one extra chair. On a normal night, hardly anyone borrows. If six guests all borrow a chair at the same moment, the room runs out of chairs. The extra-chair rule worked until demand lined up.
The same node was perfectly safe for CPU and risky for memory. This is why many teams keep memory requests equal to limits and let only CPU have burst room.
33Overcommitment and Cloud Cost Strategy
Overcommitment is one lever in a larger cost story, and it works best together with the others.
Cloud bills depend on how many machines you rent, what kind they are and how you pay for them. Overcommitment reduces the number of machines by packing more work onto each one. But there are other levers, and they interact. Understanding how they fit together helps you choose where overcommitment belongs.
Right-sized requests
Cuts waste by making the scheduler’s numbers honest. The safest saving and the first to try.
Higher density
Allows more pods per node through overcommit. Larger saving, larger risk.
Discounted interruptible machines
Cloud providers rent spare machines at lower prices, with the right to take them back. Good for work that tolerates interruption.
Committed use discounts
Paying for a steady base load in advance lowers the unit price for capacity you know you will always need.
To lower a household budget you can buy less, share more, buy in bulk and shop sales. No single trick does it all. A clever family combines them: a steady monthly plan for what it always needs and cheap, flexible options for what it sometimes needs.
Matching levers to workloads
| Workload Type | Suggested Machines | Overcommit Level |
|---|---|---|
| Steady critical services | Committed, reliable machines | Low |
| Variable web services | Reliable machines with autoscaling | Moderate, mostly CPU |
| Interruptible batch work | Discounted interruptible machines | High, with low priority |
| Development environments | Small or discounted machines | High, with quotas |
Combining high overcommit with interruptible machines multiplies risk, because the provider can remove a machine at any time and the remaining machines must absorb the pods while already packed tightly. Use that combination only for work that restarts cheaply.
Measure the bill before and after each change, and watch reliability metrics at the same time. A saving that causes more incidents is not a saving.
34Resource Abuse and Security Considerations
Resources are also a security topic, because a program that can use unlimited resources can hurt everyone around it.
On a shared node, a program that consumes too much memory, too many processes, too much disk or too much processor time can deny service to its neighbors. This can happen by accident, as in a bug, or on purpose, as in an attack. In a cluster where teams do not fully trust each other, or where customers run their own code, resource limits are a security control as much as a cost control.
In an apartment building, one tenant who leaves every tap running can flood the floors below. Building rules about water use protect everyone, whether the tenant is careless or malicious. Resource limits are the water rules for a shared node.
Memory exhaustion
A pod without a memory limit grows until the node is under pressure and neighbors are evicted.
Process flooding
A program creating huge numbers of processes can use up the node’s process slots.
Disk filling
Writing endless data to temporary storage can trigger disk pressure and evictions.
CPU hogging
A busy loop can crowd out neighbors on nodes where requests are low and limits absent.
Defensive habits
Require limits for untrusted workloads
Any code you do not fully control should run with firm limits on memory, storage and processes.
Use namespace quotas
Cap total consumption per team or customer so one tenant cannot take the whole cluster.
Isolate in separate pools
Put the riskiest workloads on their own nodes so the blast radius is small.
Enforce rules with admission policy
Use an admission controller or policy engine to reject pods that arrive without requests and limits, so nobody can forget.
Problem
Allowing tenants or customers to run workloads with no memory limit on shared nodes.
Why It’s Harmful
One buggy or hostile workload can push the node under pressure, causing evictions of unrelated neighbors and turning a small mistake into a platform outage.
Correct Approach
Require limits through defaults and policy, apply quotas, and isolate unfamiliar code in dedicated pools.
35Reading the Units: Millicores and Mebibytes
Half the confusion about resources comes from the units. A short lesson in reading them removes a lot of mistakes.
Kubernetes measures CPU in cores and fractions of cores. One whole core is written as one. A tenth of a core is written as one hundred millicores, where a millicore is a thousandth of a core. So five hundred millicores is half a core, and two thousand millicores is two cores. A container asking for a tiny amount, such as ten millicores, wants only a hundredth of one core, which is enough for a very quiet helper process.
Memory is measured in bytes, with friendly suffixes. There are two families of suffixes, and mixing them up causes quiet errors. The decimal family uses powers of one thousand, and the binary family uses powers of one thousand and twenty-four. A mebibyte is slightly larger than a megabyte, and a gibibyte is slightly larger than a gigabyte. The difference is small for small numbers but grows as numbers grow, and a limit written in one family can be a few percent different from the same-looking number in the other.
| You Write | It Means | Plain Words |
|---|---|---|
| 500 millicores | 0.5 cores | Half of one core |
| 2 cores | 2000 millicores | Two full cores |
| 256 mebibytes | 268,435,456 bytes | A quarter of a gibibyte |
| 1 gibibyte | 1,073,741,824 bytes | About 7 percent more than a decimal gigabyte |
Imagine two shops that both sell a dozen eggs, but one counts a dozen as twelve and the other as thirteen. Everyone thinks they bought the same amount, yet the totals drift apart over many purchases. The two memory suffix families are like those two shops.
Why units cause real incidents
A small typing slip can change a number by a huge factor. A memory limit written with the wrong suffix can become a thousand times smaller than intended, and the container is killed the moment it starts. A CPU request written as a whole number when a fraction was meant can claim many times more room than needed and block other pods. Reviewing the units in a change is as important as reviewing the logic.
Lowercase and uppercase letters in suffixes can change the meaning of a number completely. Treat unit strings as precise code, not as casual text, and rely on automated checks to catch suspicious values.
Display resources in a consistent unit on dashboards, such as cores for CPU and gibibytes for memory, so humans can compare numbers at a glance without converting.
36Application Runtimes and Container Limits
The program inside the container must understand the box it lives in, or it will misbehave.
Many programs decide how much memory and how many worker threads to use by looking at the machine they run on. Inside a container, the machine they see may be much bigger than the slice they are allowed to use. A program that thinks it owns thirty-two cores may start thirty-two worker threads even though the container is limited to one core, and the result is heavy throttling. A program that thinks it owns a huge amount of memory may grow its internal storage far beyond the container limit and be killed.
A guest in a hotel suite looks out of the window at the whole city and assumes the city is theirs, but their key opens only one room. If they plan a party for hundreds of people in a room that holds ten, the party is stopped at the door. A runtime that looks outside its container makes the same mistake.
Managed-memory languages
Languages with automatic memory cleanup keep a heap. If the heap maximum is not tied to the container limit, the heap can outgrow the box. Modern versions of many runtimes read the container limit, but defaults still need checking.
Thread-heavy servers
Servers that create a worker thread per core can start far too many threads when they see the whole machine, causing throttling under a small CPU limit.
Native memory users
Programs that allocate memory outside the managed heap, such as libraries and caches, can exceed a limit even when the visible heap looks healthy.
Short-lived scripts and jobs
Small programs may use a burst of memory at startup that is much higher than their steady use, which must be covered by the limit.
Leave a gap between runtime and limit
A program’s total memory is more than its main heap. It also includes code, thread stacks, buffers and internal bookkeeping. If you set the heap maximum equal to the container limit, the extra pieces push the total over the limit and the container is killed. A common habit is to set the program’s own memory target to a comfortable fraction of the container limit and leave the rest as a safety margin for everything else.
Find out what the runtime sees
Check whether your language runtime reads the container’s limits or the machine’s.
Tie its settings to the container
Set heap size and thread counts as a fraction of the container’s limits.
Leave a margin
Keep part of the limit free for non-heap memory and for short spikes.
Test under load
Run a realistic load test inside the real limits and watch for kills and throttling.
An application that runs perfectly on a laptop may fail inside a small container. Always test inside the same limits you plan to use in production.
37Startup Spikes, Probes and Rolling Updates
Many resource incidents happen not in steady state but in the moments when pods start or change.
An application often needs far more CPU and memory in its first seconds than later. It loads code, warms caches, opens connections and sometimes compiles parts of itself. If its request is sized for the quiet steady state, the node does not count that startup burst, and when many pods start at once the node can be overwhelmed. This is common during a rolling update, a node drain or after a failure, when many replacement pods start together.
A kettle uses lots of power for the first few minutes while it heats, then very little to keep warm. If the whole office switches on its kettles at the same moment every morning, the fuse trips, even though the average power use is low. Startup spikes are the morning kettles of a cluster.
How probes make things worse
Kubernetes uses probes to check whether a container is alive and ready. If a container is slow to start because the node is busy, and the probe gives up too early, Kubernetes restarts the container. The restart causes another startup spike, which keeps the node busy, which slows the next start, and a restart loop can feed on itself. This is a classic way for a mild shortage to become a long outage.
flowchart TD
A["Node is busy"] --> B["Container starts slowly"]
B --> C["Probe times out"]
C --> D["Container is restarted"]
D --> E["Another startup spike"]
E --> A
Startup probes
Give slow starters a dedicated, generous startup allowance before normal health checks begin.
Gentler rolling updates
Limit how many pods are replaced at once so startup spikes are spread out.
Realistic requests
Size requests to cover startup, or allow generous CPU burst room so starts finish quickly.
Disruption budgets
Control how many pods of a service may be unavailable during maintenance and evictions.
Example: The Monday morning stampede
After a weekend node upgrade, forty pods restart at once on a handful of nodes. Each needs triple its normal CPU for the first minute. The nodes slow down, probes time out, and pods restart again. Spreading the update into small batches and adding a startup probe ends the stampede.
Capacity plans built on average usage miss startup spikes completely. Look at the busiest minutes during deployments and recoveries, not just calm hours.
38Frequently Asked Questions
Short, direct answers to the questions people ask most often when they first meet node overcommitment.
No. It is a deliberate trade-off that saves money by letting workloads share idle capacity. It becomes bad when it is accidental, unmeasured or applied to memory without a safety margin.
No. The scheduler fits pods using requests only. Limits are enforced later by the node while the container runs, which is exactly why the sum of limits can exceed what the node owns.
It is throttled, meaning it is paused for part of each time window. It is not killed, but it can become noticeably slow.
The kernel kills it with an out-of-memory event, and Kubernetes restarts it according to its restart rules.
Pods using more than their request go first, and among them the lower priority ones, so BestEffort pods and heavily bursting Burstable pods are the first victims. Guaranteed pods are the most protected.
Every container in the pod must have both a CPU and a memory request and limit, with the request equal to the limit for each.
Many teams remove them for latency-sensitive services to avoid throttling, while keeping accurate requests. Keep them for batch, untrusted or exclusive-core workloads. Measure throttling before and after any change.
The scheduler counts requests, not usage. Nodes can be fully requested yet nearly idle if requests are padded. Right-size the requests to free the room.
There is no universal number. It depends on how independent your workloads are, how spiky they are and how costly failure is. Keep memory requests close to real usage and overcommit memory only with a measured margin.
Yes. A LimitRange can set a maximum ratio between limit and request for containers in a namespace, which directly caps hidden overcommitment.
They help, but they do not replace good requests. They react to usage and pending pods, and they take time. You still need accurate requests, headroom and monitoring.
The request. It decides placement, protection under pressure and the scheduler’s picture of every node, so honest requests make nearly everything else easier.
39Summary and Key Takeaways
Node overcommitment is the practice of promising, through limits, more CPU and memory than a machine can deliver at once, betting that workloads will not all peak together. It saves money because most applications use far less than they could. It is safe when requests are honest, memory is treated with respect, system space is reserved, priorities protect what matters, headroom survives a node or zone failure, and monitoring watches the warning signs. It is dangerous when requests are empty, memory gaps are huge, nodes run near full, and nobody looks at the numbers after launch.
Key Takeaways
- The scheduler counts requests only — limits and live usage are ignored when placing pods, so the sum of limits is where overcommitment hides.
- CPU squeezes, memory kills — CPU shortage slows programs, while memory shortage forces something to be stopped, so be bold with CPU and careful with memory.
- Quality-of-service classes decide who is protected — Guaranteed pods are safest, Burstable pods are ranked by how far they exceed requests, and BestEffort pods go first.
- Reserve space for the system — allocatable is capacity minus reservations and thresholds, and skipping reservations can starve the node itself.
- Measure three totals — compare requests, limits and real peak usage against allocatable to see real risk, and judge by peaks rather than averages.
- Use guardrails and priorities — LimitRange, ResourceQuota, priority classes and node pools make good behavior automatic and contain the blast radius.
- Keep headroom and revisit often — plan for losing a node or zone, test failures, and review numbers regularly because applications and traffic keep changing.