Resource Requests and Limits in Kubernetes

Resource Requests and Limits in Kubernetes

A plain-English guide to telling Kubernetes how much CPU and memory your containers need, how much they may use, and what happens when they ask for too much.

Imagine ten families moving into the same apartment building on the same day. Nobody has told them how much water, electricity, or parking each family may use. Within a week, one family runs a huge workshop in the basement, drains the shared water tank, and leaves everyone else with dry taps. Nobody was cruel. There were simply no rules.

A Kubernetes cluster is exactly such a building. Many applications, written by many different teams, share the same machines. Without rules, one greedy or buggy application can starve its neighbors, slow the whole cluster, or crash a machine. Kubernetes solves this with two simple ideas: requests and limits. A request says how much an application needs to run well. A limit says how much it is allowed to use at most.

In this guide you will learn what these two settings mean, how Kubernetes uses each one, and what happens when an application goes over its allowance. You will also learn how to choose sensible numbers, and how the same numbers quietly control scheduling, autoscaling, and even your cloud bill. No earlier knowledge is needed. Every new word is explained the first time it appears.

1Why Shared Machines Need Rules

Before we learn the settings, let us understand the problem they were invented to solve.

A node is one machine in a Kubernetes cluster. It has a fixed amount of processing power, called CPU, and a fixed amount of working memory, called memory or RAM. Kubernetes places many small programs, packed in containers, onto each node so that the machine is used well. This is efficient, but it creates a sharing problem.

Suppose a node has four CPUs and sixteen gigabytes of memory. If ten containers each try to use as much as they can, they will fight. Some programs will run slowly. Others may be killed by the operating system when memory runs out. The worst part is that the fight is unpredictable: a program that worked yesterday might fail today because of a noisy neighbor.

Everyday analogy

Picture a shared kitchen in a student hostel. If nobody books time on the stove, the loudest and fastest student cooks all evening, and everyone else eats cold food. A booking sheet fixes this. Each student writes down how long they need the stove, and the warden also sets a maximum time per person. The booking is the request. The maximum is the limit.

The noisy neighbor problem

The technical name for this problem is the noisy neighbor problem. One container on a node consumes so much of a shared resource that other containers suffer. It is common in every shared system, from apartment buildings to cloud data centers. Kubernetes cannot remove the sharing, but it can make sharing fair and predictable.

Without rules

Unpredictable speed

An application is fast when neighbors are quiet and slow when they are busy, and nobody can explain why.

Without rules

Surprise crashes

A neighbor uses up memory and the machine starts killing programs, sometimes the wrong ones.

Without rules

Blind placement

Kubernetes cannot know how big your program is, so it may pack too many onto one node or waste whole machines.

Without rules

Unfair cost

No team can be charged fairly, because nobody knows who used what.

i
Good to know

You can run a cluster without setting requests or limits at all, and many beginners do. It works while the cluster is quiet. The trouble starts later, when it is busy and hard to fix.

2What Are Requests and Limits?

A request is a promise Kubernetes makes to your container. A limit is a ceiling your container must respect.

Every container in a pod can be given a request and a limit for each resource. The two settings sound similar, but they answer two very different questions.

Request: the minimum

  • How much does this container need to run well?
  • Used by the scheduler to choose a node.
  • Space that is reserved for the container on that node.
  • Also decides who is treated first when resources are scarce.

Limit: the maximum

  • How much may this container use at most?
  • Enforced on the node while the container runs.
  • Going over has consequences: slowdown for CPU, being killed for memory.
  • Protects neighbors from a greedy container.
Everyday analogy

Think of a restaurant. When you book a table for four, that is your request: the restaurant holds four seats for you, whether or not your friends all show up. The restaurant also has a rule that no group can bring more than eight people. That is the limit. You may use anything from four to eight seats when the restaurant has room, but you are never guaranteed more than four, and you are never allowed more than eight.

What it is, why it exists, where it is used

What it is: two numbers per resource, per container, that describe the minimum needed and the maximum allowed.

Why it exists: so the scheduler can place pods wisely, the node can enforce fairness, and the cluster can survive greedy programs.

Where it is used: in every production cluster. It influences scheduling, autoscaling, eviction, cost reports, and quota systems.

!
Common trap

Requests and limits are set per container, not per pod. A pod’s total is the sum of its containers. Forgetting this leads to pods that are much bigger than anyone intended.

3A Quick Kubernetes Refresher

A few basic words are enough to follow the rest of this guide.

Kubernetes is a system that runs your containers across many machines and keeps them healthy. You say what you want, and Kubernetes makes it true and keeps it true. Here are the parts that matter for resources.

Building block

Cluster

The whole group of machines managed together. Think of it as the entire apartment building.

Building block

Node

One machine in the cluster. Each has a fixed amount of CPU and memory. These are the individual flats.

Building block

Pod

The smallest unit Kubernetes runs. A pod holds one or more containers that live and die together.

Building block

Scheduler

The part of Kubernetes that decides which node should run each new pod. It reads the requests.

Building block

kubelet

The manager program on every node. It starts containers and makes sure they obey their limits.

Building block

cgroups

A feature of the Linux operating system that measures and limits what a group of programs may use. Kubernetes relies on it to enforce limits.

Two kinds of resources

Kubernetes treats resources in two families, and the difference is the key to almost every surprise later in this guide.

A compressible resource can be squeezed without breaking anything. CPU is compressible. If a program wants more CPU than it can get, it simply runs slower. It waits its turn.

An incompressible resource cannot be squeezed. Memory is incompressible. If a program needs memory that is not available, nothing can be slowed down to make up for it. Something must be stopped. Remember this pair of words, because they explain why CPU and memory behave so differently when a limit is crossed.

4Architecture and Components

Several parts of Kubernetes cooperate to turn two numbers in a file into real behavior on a real machine.

The diagram below follows a pod from the moment you submit it until it runs inside its boundaries. Notice that different parts read different numbers. The scheduler cares about requests. The node cares about limits. Autoscalers care about actual usage compared with requests.

flowchart LR
    Dev["Developer submits a pod with requests and limits"] --> API["API Server"]
    API --> Adm["Admission checks with LimitRange and ResourceQuota"]
    Adm --> Sched["Scheduler"]
    Sched -->|picks a node with enough room| Kubelet
    subgraph Node["Worker Node"]
        Kubelet["kubelet"] --> Runtime["Container runtime"]
        Runtime --> CG["Linux cgroups"]
        CG --> Cont["Container"]
    end
    Metrics["Metrics server"] -.->|reads usage| Kubelet
    Auto["Autoscalers"] -.->|reads usage| Metrics
    
Figure 1: Requests guide placement. Limits are enforced by the operating system on the node.

Meet the components

Control plane

API server

Receives your pod description, stores it, and hands it to the other parts. It is where defaults are filled in and rules are checked.

Control plane

Admission rules

Two special namespace rules, LimitRange and ResourceQuota, can fill in missing numbers or reject pods that ask for too much.

Control plane

Scheduler

Adds up the requests of pods already on each node and only places a new pod where its requests still fit.

On every node

kubelet

Translates requests and limits into settings for the operating system and watches over the node’s health.

On every node

Container runtime and cgroups

Actually create the container and apply the fences that keep it within its limits.

Add-on

Metrics server

Collects live CPU and memory usage from the nodes. It powers the top command and autoscalers.

i
Key idea

The scheduler works from the numbers you wrote down, not from what programs really use. This one fact explains many “why is my pod not scheduled?” mysteries.

5CPU: Measuring and Limiting Processing Power

CPU is measured in a simple unit, and limiting it works very differently from limiting memory.

How CPU is counted

Kubernetes measures CPU in cores. One CPU means one full core, whether it is a physical core or a virtual one in the cloud. You can ask for fractions. Because decimals are awkward, Kubernetes also lets you write in millicores, where a thousand millicores make one core.

You writeIt meansPlain meaning
1One coreOne whole core, always available
500m500 millicoresHalf a core
250m250 millicoresA quarter of a core
0.1100 millicoresOne tenth of a core
2Two coresTwo whole cores

The smallest amount you can ask for is one millicore. Half a core does not mean the program only runs half the time on a slow chip. It means the program is entitled to the equivalent of half a core’s worth of time.

What a CPU request does

The CPU request is turned into a weight for sharing. When the node has spare CPU, a container may use much more than it requested, and nobody minds. When the node is busy and many containers want CPU at once, the operating system divides the processor in proportion to their requests. A container that requested twice as much gets twice the share. This is how a request becomes a real, guaranteed minimum under pressure.

What a CPU limit does

A CPU limit is a hard ceiling, enforced by time slicing. The operating system works in short windows of one hundred milliseconds. A limit of half a core means the container may run for fifty milliseconds of CPU time in each window, counted across all its threads together. When the fifty milliseconds are used, the container is paused until the next window begins. This pausing is called throttling.

Everyday analogy

Imagine a road with a toll gate that lets each company send only fifty trucks through every hundred seconds. If a company has ten trucks that all arrive at once, they may spend the whole allowance in the first ten seconds, and then sit idle for ninety seconds, even though the road is empty. That is why a program with many threads can feel slow under a CPU limit even when the average usage looks low.

!
Common trap

Throttling does not kill the program and does not write an error. It just makes the program slower, in bursts. Requests may look slow, timeouts may appear, and no message tells you why. You have to look at throttling numbers to see it.

6Memory: The Resource That Cannot Bend

Memory is counted in bytes, and going over the limit has a much sharper consequence than going over a CPU limit.

How memory is counted

Memory is written in bytes, with helpful suffixes. Two families exist, and confusing them is a classic beginner mistake. The binary family uses suffixes like Ki, Mi, and Gi, which are powers of 1,024. The decimal family uses K, M, and G, which are powers of 1,000. They differ by a few percent. Most people prefer Mi and Gi, because they match how computers actually count.

You writeIt meansNote
128MiAbout 134 million bytesA common size for a small helper program
512MiHalf a gibibyteTypical for a small web service
1GiAbout 1.07 billion bytesTypical for a medium service
500MExactly 500 million bytesSlightly less than 500Mi
400mFour tenths of one byteAlmost always a typing mistake
!
Famous typing mistake

A small letter “m” means “milli” in Kubernetes, not “mega”. Writing 400m for memory asks for less than one byte. The container then gets killed almost at once. If you mean four hundred megabytes, write 400Mi.

What a memory request does

The memory request is mainly used for placement. The scheduler makes sure that the node has that much memory left unreserved. The request is not a fence at runtime. A container may use more than it requested if memory is free. But the request has another job: when the node runs short of memory, containers that use more than they requested are the first candidates to be removed.

What a memory limit does

A memory limit is a hard wall. If a container tries to use more memory than its limit, the operating system’s memory guard steps in and kills the offending process. Kubernetes reports this as OOMKilled, where OOM stands for out of memory. The container is then restarted according to the pod’s restart rules. Its exit code is usually 137, a number worth remembering because it appears in many incident reports.

Everyday analogy

CPU is like a queue at a counter: if too many people arrive, everyone waits a little longer. Memory is like a suitcase: if you keep stuffing things in beyond its size, it does not get slower. It bursts. The airline staff then remove the bag from the flight. The container is that bag.

flowchart TD
    U["Container uses more than its request"] --> Q1{"Which resource"}
    Q1 -->|CPU| C1{"Above its limit"}
    C1 -->|Yes| T["Throttled and slowed but keeps running"]
    C1 -->|No| OK1["Fine if CPU is free and shares fairly when busy"]
    Q1 -->|Memory| M1{"Above its limit"}
    M1 -->|Yes| K["OOMKilled so the container is killed and restarted"]
    M1 -->|No| M2{"Node under memory pressure"}
    M2 -->|Yes| E["Pod may be evicted and over-request pods go first"]
    M2 -->|No| OK2["Keeps running"]
    
Figure 2: What happens when a container uses more than it asked for.

7Beyond CPU and Memory: Storage and Special Devices

CPU and memory get all the attention, but a container can also fill a disk or need special hardware.

Ephemeral storage

Every container writes some files while it runs: temporary files, logs, caches, and scratch space. This space is called ephemeral storage because it disappears when the pod goes away. It lives on the node’s disk, which is shared by everyone. A single container that fills the disk with logs can make the whole node unhealthy.

You can give ephemeral storage a request and a limit, just like CPU and memory. The request helps the scheduler avoid nodes with little free disk. The limit protects the node: if a pod writes more than its limit, Kubernetes removes the pod. Unlike CPU, there is no gentle slowdown. It is closer to the memory case.

Extended resources such as GPUs

Some nodes have special hardware, most famously graphics processors used for machine learning. Kubernetes handles these as extended resources. A device plug-in announces the hardware to the cluster, and pods ask for it by name and count. There are some special rules: devices are counted in whole numbers, they cannot be shared in fractions by default, and the amount is normally written as a limit, which then also serves as the request.

ResourceSqueezable?When the limit is crossedTypical unit
CPUYes, compressibleContainer is throttledCores or millicores
MemoryNo, incompressibleContainer is killed and restartedMi or Gi
Ephemeral storageNoPod is evictedMi or Gi
GPU and other devicesNoNot possible to exceed. Pod stays pending until availableWhole devices
i
Practical hint

If your application writes logs to files inside the container, give it an ephemeral storage limit. A runaway log file is a very common way for one pod to hurt a whole node.

8How the Scheduler Uses Requests

The scheduler is a bookkeeper. It counts reservations, not real usage.

When a new pod arrives, the scheduler looks at each node and asks a simple question: after adding this pod’s requests to the requests of the pods already there, do we still stay within the node’s allocatable capacity? If yes, the node is a candidate. If no, the node is skipped. Among the candidates, the scheduler picks the best one using other rules like spreading and preferences.

flowchart TD
    N1["Node A has 4 CPU allocatable and 3.5 CPU already requested"] --> R1{"New pod requests 1 CPU. Does it fit"}
    R1 -->|No because only 0.5 CPU is unreserved| X1["Node A is skipped"]
    N2["Node B has 4 CPU allocatable and 2 CPU already requested"] --> R2{"New pod requests 1 CPU. Does it fit"}
    R2 -->|Yes because 2 CPU is unreserved| OK["Pod can be placed on Node B"]
    
Figure 3: The scheduler only compares requests against allocatable capacity.
Everyday analogy

A hotel booking desk counts reservations, not who is actually in the room. If a hotel has a hundred rooms and a hundred are reserved, the desk turns new guests away, even if half the guests are out sightseeing and the rooms are empty right now. Reservations are promises, and the hotel must be able to honor them all at once.

Reserved is not the same as used

This creates two opposite problems. If you request far more than you use, the node looks full while its processors sit idle. You pay for machines that do nothing. If you request far less than you use, the scheduler crams too many pods on one node, and when they all become busy, they compete and suffer. Right-sizing your requests is therefore the heart of good resource management.

When nothing fits

If no node can satisfy a pod’s requests, the pod stays in the Pending state, and its event log says something like “insufficient cpu” or “insufficient memory”. This is not an error in the usual sense. It is Kubernetes telling you honestly that there is no room. The cure is one of three things: ask for less, free up space by removing other pods, or add a bigger or additional node.

!
Watch out

A request larger than the biggest node in your cluster can never be satisfied. The pod will stay pending forever. Check that your largest single request fits on your largest node type.

9Node Capacity and Allocatable Space

A node never offers all its resources to your pods. Some are kept for the machine itself.

A machine with sixteen gigabytes of memory does not have sixteen gigabytes to give your applications. The operating system needs some. Kubernetes’ own helper programs need some. And the node keeps a safety buffer so that it can react before memory runs out completely. What remains for pods is called allocatable.

PartWhat it isWho uses it
CapacityEverything the machine physically hasThe starting point
System reservedA slice kept for the operating systemLogin services, kernel work, monitoring agents on the host
Kube reservedA slice kept for Kubernetes programs on the nodeThe kubelet and the container runtime
Eviction bufferA safety margin below which the node starts removing podsThe kubelet’s protection system
AllocatableCapacity minus the three parts aboveThe pods’ requests must fit inside this

On small machines this hidden reservation is a noticeable share of the total. A tiny node might offer only around three quarters of its memory to pods. On very large machines the share is smaller. When you plan how many pods fit, always start from allocatable, not from the number on the machine’s price sheet.

i
Practical hint

Ask Kubernetes to describe a node, and you will see both capacity and allocatable side by side, plus how much of the allocatable space is already requested. This one screen answers most “why does it not fit” questions.

10How It Works Inside the Node

The numbers you write become settings in the Linux kernel. Following that path removes all the mystery.

sequenceDiagram
    participant D as Developer
    participant A as API Server
    participant S as Scheduler
    participant K as kubelet
    participant R as Runtime and cgroups
    D->>A: Create pod with requests and limits
    A->>A: Fill in defaults and check quota
    S->>A: Pick a node with enough unreserved room
    K->>A: See the pod assigned to this node
    K->>R: Create the container with CPU weight, CPU time limit, memory limit
    R-->>K: Container running inside its boundaries
    K->>A: Report status and usage
    
Figure 4: From a pod description to fences inside the operating system.
1

Defaults and rules are applied

When the pod is submitted, the API server fills in any missing numbers from the namespace’s default rules and rejects the pod if it breaks a quota or a minimum or maximum.

2

The scheduler reserves room

It adds the pod’s requests to the node’s booked total. From this moment, that space is promised.

3

The kubelet creates a group for the pod

On the node, the kubelet makes a control group for the whole pod and a smaller one for each container. Control groups are the Linux way of measuring and limiting resource use.

4

CPU request becomes a weight

The container’s CPU request is written as a sharing weight. It only matters when several containers compete.

5

CPU limit becomes a time budget

The limit is written as a quota of CPU time per hundred milliseconds. Once used, the container waits.

6

Memory limit becomes a hard wall

The memory limit is written into the group. If the container’s processes reach it and cannot free anything, the kernel’s memory guard kills a process inside the container.

7

Priority for the guard is set

Each container gets a score that tells the kernel who to sacrifice first if the whole machine runs out of memory. The score is derived from the pod’s quality class, which is the topic of the next chapter.

i
Key idea

Kubernetes itself does not police your containers moment by moment. It configures the operating system once, and the kernel does the enforcing at full speed.

11Quality of Service Classes

Kubernetes sorts every pod into one of three classes, based purely on how you set requests and limits.

You never choose the class directly. Kubernetes works it out from your numbers. The class matters most when the node is in trouble, because it decides who is protected and who is removed first.

flowchart TD
    S["Look at every container in the pod"] --> Q1{"Is any request or limit set anywhere"}
    Q1 -->|None at all| BE["BestEffort"]
    Q1 -->|Yes| Q2{"Does every container have CPU and memory limits equal to requests"}
    Q2 -->|Yes| G["Guaranteed"]
    Q2 -->|No| B["Burstable"]
    
Figure 5: How Kubernetes assigns a quality of service class.
ClassHow to get itProtectionBest for
GuaranteedEvery container sets CPU and memory, and limit equals requestHighest. Removed lastDatabases, critical services
BurstableSome requests or limits set, but not the strict Guaranteed patternMedium. Depends on how far above request it isMost ordinary services
BestEffortNo requests and no limits at allLowest. Removed firstThrowaway or low-value jobs
Everyday analogy

Think of an airline. Guaranteed passengers hold a confirmed seat and are the last to be bumped. Burstable passengers hold a ticket but may be moved if the plane is overbooked, and those who took more than their share of luggage allowance go first. BestEffort passengers are on standby: they fly only if there is room, and leave the plane first if trouble appears.

What happens during memory trouble

When a node begins to run out of memory, the kubelet steps in before the kernel has to. It starts evicting pods to free space. It first picks pods whose usage exceeds their requests, then considers pod priority, and among those it takes the ones with the largest overuse first. A BestEffort pod has no request, so any usage counts as overuse, which is why these pods vanish first. A Guaranteed pod never uses more than its request, so it sits at the back of the queue.

If memory runs out so fast that the kubelet cannot react, the kernel’s memory guard takes over and picks victims by that score we saw earlier. The same ordering applies: BestEffort first, Burstable in between, Guaranteed last.

i
Good to know

Setting limits equal to requests gives the strongest protection, but it also gives up flexibility. A Guaranteed pod cannot borrow spare CPU beyond its number. Choose this deliberately for the pods that matter most.

12Guardrails: LimitRange and ResourceQuota

Asking every developer to remember to set sensible numbers does not scale. Namespace-level rules do the remembering for you.

A namespace is a named section of a cluster, often given to one team or one project. Kubernetes lets an administrator attach two kinds of rules to a namespace. They sound alike but do very different jobs.

Per container or pod

LimitRange

Sets defaults and boundaries for each individual container or pod. It can fill in missing requests and limits, set a minimum, set a maximum, and limit how far a limit may exceed its request.

Per namespace total

ResourceQuota

Sets a budget for the whole namespace. It caps the total of all requests, all limits, and even counts of objects such as pods or claims.

Everyday analogy

Imagine an office building with several tenant companies. The LimitRange is the house rule for each desk: every desk gets a standard chair and a monitor unless the tenant asks otherwise, and no desk may have more than two monitors. The ResourceQuota is the tenant’s total budget: this company may use at most forty desks in the building, no matter how it divides them.

QuestionLimitRangeResourceQuota
ScopeEach container or podThe whole namespace
Fills in missing values?Yes, defaults for requests and limitsNo
Rejects too-large requests?Yes, above the maximumYes, when the total would pass the budget
Stops too many pods?NoYes, it can cap object counts
Typical ownerPlatform teamPlatform team, per team budget

How they work together

If a namespace has a quota on CPU or memory, then every new pod there must state its requests and limits for those resources, or the API server refuses it. A LimitRange with defaults solves this neatly: developers who forget still get pods with sensible numbers, and the quota can count them. The two rules form a pair.

!
Watch out

These rules check pods when they are created. Changing a LimitRange does not alter pods that already exist. Old pods keep their old numbers until they are recreated.

13Pods With Many Containers

A pod is more than one container, and its total size is not always a simple sum.

Many pods hold a main application and one or more helpers, called sidecars. A sidecar might collect logs, handle encryption, or route network traffic. Each container has its own requests and limits, and the pod’s total is built from them.

The basic rule: add them up

For containers that run side by side, the pod’s request is the sum of the containers’ requests. A pod with a main container asking for half a core and a helper asking for a quarter of a core needs three quarters of a core. The scheduler uses this total. Small helpers therefore matter: ten pods each carrying an oversized sidecar can quietly consume a whole node.

Init containers: a special case

An init container is a short-lived helper that runs to completion before the main containers start. It might prepare data or wait for a database to be ready. Because it runs first and then exits, it does not add to the total in the same way. The pod’s effective request is the larger of two numbers: the sum of all main containers, or the biggest single init container. This is like a moving crew that arrives, unpacks, and leaves before the residents move in. You only need room for whichever is bigger, the crew or the residents.

Extra costs hiding around a pod

Overhead

Runtime overhead

Some sandboxed runtimes, used for stronger isolation, need extra CPU and memory just to run a pod. Kubernetes can add this fixed overhead to the pod’s total automatically.

Sidecars

Long-running helpers

Modern Kubernetes lets a helper be marked as a sidecar that starts before the main container and stops after it. Its resources count toward the pod for as long as it runs.

Changing

Resizing running pods

Recent Kubernetes versions can change a running container’s CPU and memory numbers in place, without restarting the pod. Availability depends on your version, so check your cluster’s documentation.

i
Practical hint

Review the sidecars in your cluster. A logging or proxy helper copied into every pod with generous defaults can add up to more resources than the applications it supports.

14Requests and Autoscaling

Your request numbers quietly steer every autoscaler in the cluster.

Kubernetes can grow and shrink automatically. There are three main kinds of automatic scaling, and all three depend on the numbers you set.

AutoscalerWhat it changesHow requests matter
Horizontal Pod AutoscalerNumber of pod copiesUtilization is measured as a percentage of the request. No request, no percentage
Vertical Pod AutoscalerSize of each pod’s requestsIt watches real usage and suggests better request numbers
Cluster autoscaler or node provisionerNumber of nodesIt adds machines when pods cannot be placed because requests do not fit

The percentage trap

When you tell the horizontal autoscaler, “keep average CPU at seventy percent”, seventy percent of what? The answer is: seventy percent of each container’s CPU request. If you request a tiny amount, the pod looks overloaded at once and the autoscaler creates far too many copies. If you request a huge amount, the pod never looks busy, and the autoscaler never adds a copy even when users complain. The request number is therefore also a dial on your scaling behavior.

Everyday analogy

Imagine a supermarket that opens a new checkout lane whenever the queue exceeds seventy percent of what a cashier “should” handle. If the store defines a cashier’s normal load as five customers an hour, the lanes open constantly. If it defines it as five hundred, no new lane ever opens. The definition of “normal load” is the request.

How nodes are added

When pods stay pending because no node has room for their requests, a cluster autoscaler notices and asks the cloud for another machine. Once the machine joins, the pending pods are placed. So oversized requests do not just waste room on existing nodes. They also make the cluster buy more machines than needed.

!
Watch out

Do not let two automatic systems fight. If the vertical autoscaler changes CPU requests while the horizontal autoscaler is using CPU percentage, their signals can chase each other. Use them together only with care, for example on different metrics.

15What Happens When a Container Goes Over

Every crossing of a boundary has a different symptom. Learning them is like learning to read a car’s dashboard lights.

1

CPU above the limit: throttling

The container is paused for the rest of the current time window. Symptoms are slow responses, timeouts, and uneven speed. No restart and no error message. The pod looks healthy.

2

Memory above the limit: OOMKilled

The container is killed at once. The pod’s status shows the reason OOMKilled and exit code 137. Kubernetes restarts the container, and if this repeats, it waits longer between tries.

3

Repeated kills: CrashLoopBackOff

After several quick failures, the pod enters a state named CrashLoopBackOff. This is the visible sign of a restart loop. The cause may be a memory limit that is simply too low for startup.

4

Node memory pressure: eviction

Even with no limit crossed, if the node itself runs short, the kubelet evicts whole pods. Over-request pods and low-priority pods go first. Evicted pods are recreated elsewhere if a controller owns them.

5

Disk above the limit: eviction

A pod that writes more ephemeral storage than allowed is removed, and its event log says so.

6

No room to start: Pending

If no node can hold the requests, the pod never starts. It waits, and its events explain which resource is short.

Why memory kills but CPU only slows

Remember the two families from the refresher. CPU time is renewed every instant, so a shortage can be handled by making programs wait. Memory is a stock that is filled and held. If a program holds memory and the limit is reached, there is nothing to wait for. The only way forward is to take something away, so the guard kills a process.

i
Key idea

An OOMKilled container did not do anything “wrong” by itself. It simply asked for more than its allowance. The fix is either to raise the limit or to make the program use less, and finding out which requires measuring.

16Choosing the Right Numbers

There is no magic formula. There is a method: measure, choose, watch, adjust.

Beginners often copy numbers from a tutorial, and those numbers fit the tutorial’s toy program, not your real one. A sound approach follows four steps.

1

Measure real usage

Run the application under realistic load, in a test setting, and record how much CPU and memory it uses at quiet times, at typical times, and at peak times.

2

Set the request near typical use

Choose a request that covers everyday usage, for example a high percentile of normal load. This is the amount you want reserved for the container.

3

Set the limit with headroom

For memory, choose a limit comfortably above the peak, because going over means being killed. For CPU, decide your policy, discussed below.

4

Watch and adjust

Real traffic changes. Compare actual usage with your numbers every few weeks, and correct them. Automatic recommendation tools can help.

Different programs, different shapes

Kind of workloadCPU behaviorMemory behaviorSensible approach
Simple web serviceSpiky with trafficSteady with slow growthModerate request, memory limit above peak
DatabaseSteady with burstsLarge and grows with cacheGuaranteed class, limits equal to requests
Batch jobHigh for a short timeDepends on data sizeRequest what it needs while running, allow it to end quickly
Background workerLow most of the timeSmall and steadySmall request, small limit
Machine learning trainingVery high, sustainedVery largeDedicated nodes, requests near the true need

The great debate about CPU limits

Experienced teams disagree about whether to set CPU limits at all. Both sides have good reasons, and a beginner should understand each.

Reasons to set CPU limits

  • They protect neighbors from a runaway program.
  • They make performance more predictable, because the container always runs at a similar ceiling.
  • They are required in some shared clusters and by some quota setups.
  • They make tests behave like production.

Reasons to skip them

  • Throttling can slow programs even when the node has lots of idle CPU.
  • Requests already guarantee a fair share under pressure.
  • Unused CPU is wasted if a container cannot borrow it.
  • Throttling is hard to notice and easy to misdiagnose.

A common middle path is this: always set CPU requests, set a generous CPU limit for untrusted or shared environments, and monitor throttling either way. For memory the advice is simpler: always set a limit, and for important programs, make it equal to the request so that the pod is well protected and behaves predictably.

17Performance and Scalability

Resource settings are also performance settings. A wrong number can make a fast program slow.

100 ms
length of each CPU time window used for limits
Requests
decide how many pods fit per node
Limits
decide how far a pod can burst

Threads and throttling

A program with many threads can use up a small CPU limit very quickly. Imagine a limit of one core and a program with eight busy threads. All eight run at once and consume the hundred-millisecond budget in about twelve milliseconds. Then the program stalls for the remaining eighty-eight. Average CPU usage looks low, yet latency is terrible. Increasing the limit, or reducing the number of threads, often fixes a mystery slowdown.

Language runtimes need to know the limit

Some programming platforms decide how many threads to run, or how much memory to use, by looking at the machine they think they are on. If they see the whole node instead of their container’s slice, they overreach. Modern versions of popular runtimes read container limits correctly, but older versions may not. A program that sizes its memory pool as a fraction of the machine and ignores its container limit will be OOMKilled regularly. Always check how your runtime reads its limits, and leave headroom between the program’s own memory ceiling and the container’s limit for non-heap memory.

Pinning CPUs for special workloads

For latency-sensitive programs, Kubernetes has an optional feature called the static CPU manager policy. When a Guaranteed pod asks for whole cores, for example exactly two, it is given exclusive use of those cores. No other pod runs on them. This avoids the small delays caused by sharing and switching, at the price of less flexible packing.

Packing and overcommit

Requests decide how tightly pods are packed. Limits may add up to more than the node has. This deliberate gap is called overcommit. It works because pods rarely all peak at once. Overcommit on CPU is fairly safe since the worst outcome is slowness. Overcommit on memory is riskier since the worst outcome is kills and evictions. Many teams therefore overcommit CPU freely and memory cautiously.

i
Practical hint

Measure the fraction of time each container is throttled. A container that is throttled more than a few percent of the time is telling you its limit is tight.

18Reliability, Priority, and Headroom

Good resource settings are a quiet form of insurance against outages.

Priority and preemption

Not all pods are equally important. Kubernetes lets you assign a priority class to a pod. When the cluster is full, and an important pod is pending, the scheduler may remove lower-priority pods to make room. This is called preemption. It is separate from quality of service classes, but they work together: priority decides who gets a seat, and the service class decides who is removed first under memory pressure.

Headroom for failure

If you pack nodes to the very brim, then losing one node means its pods have nowhere to go. They sit pending until a new node is added. A sturdy cluster keeps spare room, often enough to absorb the loss of a node or a whole zone. This spare room is not waste. It is the cost of surviving failures without a wait.

Practice

Reserve failure capacity

Plan node counts so that losing one still leaves enough unreserved space to reschedule its pods.

Practice

Protect critical pods

Give essential pods the Guaranteed class and a high priority so they are evicted last and never wait long.

Practice

Spread replicas

Combine sensible requests with spreading rules so that copies of an application land on different nodes.

Practice

Limit disruption

Use disruption budgets so that maintenance never removes too many copies at once.

!
Watch out

A cluster that looks fine on a quiet Tuesday can collapse on a busy Friday if too many pods depend on borrowed, unreserved capacity. Requests are what keep promises true on the worst day, not the average day.

19Security and Fair Sharing

Resource limits are also a defense. A program with no ceiling can be turned into a weapon against its neighbors.

In a cluster shared by many teams, or running code from customers, resource control is part of security. An attacker who takes over a container can try to use every bit of CPU and memory to starve other workloads. This is a form of denial of service. Limits contain the blast.

Defense

Always set memory limits

A container without a memory limit can push a whole node into trouble. Make limits mandatory in shared clusters.

Defense

Use quotas per namespace

A team’s budget stops one compromised or careless project from using the entire cluster.

Defense

Set maximums with LimitRange

Even inside a budget, cap the size of any single container, so nobody asks for an entire node.

Defense

Cap process counts

Runaway programs can also exhaust the number of processes allowed on a node. Cluster settings can limit this per pod.

Defense

Enforce with policy

Admission policies can reject pods that lack requests and limits, so the rule does not depend on memory or goodwill.

Defense

Separate sensitive workloads

For strong isolation, run critical or untrusted workloads on dedicated nodes rather than sharing with everyone.

i
Key idea

Resource controls do not stop a break-in. They limit what an intruder or a bug can destroy. They are a seat belt, not a lock.

20Monitoring, Logging, and Metrics

You can only right-size what you can see. A few key graphs answer most questions.

Metrics

Usage against request

Plot each container’s real CPU and memory beside its request. A wide gap shows waste. Usage above the request shows risk.

Metrics

Usage against limit

Memory approaching its limit warns of coming OOM kills. Track the working set of memory, which is the number the memory guard cares about.

Metrics

Throttling ratio

Container monitoring reports how many time windows were throttled out of the total. A high ratio means the CPU limit is hurting.

Metrics

Node reserved versus used

Compare the sum of requests on each node with real usage. Big differences mean packing could improve.

Events

Termination reasons

Pod status records why a container last ended. Count OOMKilled reasons across the cluster to find weak spots.

Health

Node pressure conditions

Nodes report when they are under memory, disk, or process pressure. These are early warnings before evictions begin.

Alerts worth setting up

  • Any container restarted because of OOMKilled.
  • A container throttled for more than a small share of its time over an hour.
  • A container whose memory stays above ninety percent of its limit.
  • Pods pending for longer than a few minutes because of insufficient resources.
  • A namespace using more than eighty percent of its quota.
  • Nodes reporting pressure conditions, or pods being evicted.
i
Practical hint

Build one dashboard per application showing requests, limits, and real usage on the same chart. Most right-sizing decisions can be made by looking at it once a month.

21Advantages, Disadvantages, and Trade-offs

Resource settings bring order. They also bring decisions you cannot avoid.

Advantages

  • Fair sharing. One program cannot starve the others.
  • Smart placement. The scheduler can pack pods without guessing.
  • Predictable behavior. Programs behave the same on quiet and busy days.
  • Automation works. Autoscalers and quota systems rely on these numbers.
  • Cost visibility. You can attribute cost to teams by what they reserve.
  • Safer failures. Eviction is orderly instead of random.

Disadvantages

  • Guessing is hard. Right numbers need measurement and time.
  • Waste is easy. Padding requests to be safe wastes money.
  • Failure can be sharp. Too tight a memory limit causes kills.
  • Hidden slowness. Throttling is silent.
  • Drift over time. Numbers that were right last year become wrong as traffic changes.
  • Extra process. Teams must maintain and review the numbers.

The main trade-offs

Safety versus cost. Large requests keep applications safe and cost more. Small requests save money and raise the risk of noisy neighbors. There is no free choice; you pick a point on the line.

Predictability versus efficiency. Setting limits equal to requests makes behavior predictable but stops pods from borrowing spare capacity. Letting pods burst uses machines better but makes performance vary with the neighbors.

Strictness versus developer speed. Requiring numbers on every pod protects the cluster, and it also adds a step for developers who just want to try something. Namespace defaults soften this.

Requests are what you promise your neighbors you need. Limits are what you promise your neighbors you will never exceed.

22Design Patterns and Anti-patterns

Some habits keep clusters healthy. Others create silent trouble.

Patterns that work

Pattern

Namespace defaults

Give every namespace a default request and limit, so forgetful developers still produce well-behaved pods.

Pattern

Tiered classes

Run critical services as Guaranteed, ordinary services as Burstable, and disposable jobs as low priority.

Pattern

Recommendation loop

Run a recommendation tool in observe-only mode, review its suggestions, and apply them in scheduled changes.

Pattern

Team budgets

Give each team a quota and show them their usage. People manage what they can see.

Pattern

Dedicated node pools

Put heavy or sensitive workloads on their own node groups, so neighbors are predictable.

Pattern

Load test before launch

Measure real usage under expected traffic, then set numbers, instead of guessing.

Anti-patterns to avoid

ANTI-PATTERN 01 · NO REQUESTS OR LIMITS AT ALLAvoid
What people do

They deploy pods with nothing set, because it works during testing and nobody complains.

Why it hurts

Every such pod is BestEffort. The scheduler thinks it needs nothing and stacks them on one node. When load arrives, they fight and are the first to be evicted.

What to do instead

Set at least memory and CPU requests, and a memory limit. Use namespace defaults so it happens automatically.

ANTI-PATTERN 02 · LIMITS FAR ABOVE REQUESTSAvoid
What people do

They request very little to fit easily, and set enormous limits to be safe, for example a request of a hundred megabytes and a limit of eight gigabytes.

Why it hurts

The scheduler believes the pod is tiny and packs dozens onto one node. When they all grow toward their limits, the node runs out of memory and pods are evicted or killed unpredictably.

What to do instead

Keep the gap between request and limit modest, especially for memory. Set requests near real typical usage.

ANTI-PATTERN 03 · COPY AND PASTE NUMBERSAvoid
What people do

They copy resource values from an example, another team, or an old project, and never revisit them.

Why it hurts

The numbers fit some other program at some other time. Either money is wasted on idle reservations, or the program is constantly throttled or killed.

What to do instead

Measure your own application, set values from data, and review them on a schedule.

23Best Practices and Common Mistakes

A short list of habits prevents most resource problems.

Best practices

Do

Always set memory requests and limits

Memory is the resource that kills. Give every container a request and a limit.

Do

Always set CPU requests

Without them, scheduling is blind and fairness under pressure is lost.

Do

Use binary units for memory

Write Mi and Gi. Avoid the lowercase m for memory.

Do

Add defaults and quotas

Namespace rules protect the cluster from forgetfulness.

Do

Review numbers regularly

Compare real usage to requests monthly and adjust.

Do

Leave headroom for failures

Keep spare capacity so a lost node does not cause a long wait.

Common mistakes

Confusing request with limit

Thinking the request is a ceiling. It is a reservation. A container can use more than its request when space is free.

Typing the wrong unit

Writing 400m for memory, which means less than a byte, or writing 1000 for CPU instead of 1000m.

Ignoring startup spikes

Many programs use much more memory while starting than while running. A limit that suits steady state may kill the program at boot.

Sizing from averages

Setting numbers from average usage. Averages hide peaks, and peaks are what kill containers.

Forgetting sidecars

Adding up only the main container and being surprised that pods are bigger than expected.

Leaving limits unchanged after growth

Traffic grows, but the limits stay as they were, and one day the program gets throttled or killed at the busiest moment.

24Real-World and Industry Examples

The same ideas appear at every scale, from a small startup to a very large platform.

Cost control in the cloud

Cloud bills are driven by the number and size of machines. Because machines are added when requests do not fit, inflated requests are one of the biggest hidden costs in Kubernetes. Many organizations run regular right-sizing reviews, comparing real usage to requests, and often find that a large fraction of reserved capacity is never used.

Multi-team platforms

Internal platforms that host many product teams typically give each team a namespace with a quota and default limits. Teams see their own usage and cost, which encourages good habits without central policing.

Databases on Kubernetes

Teams running databases usually choose the Guaranteed class, with memory and CPU limits equal to requests. A database that is evicted or throttled at a bad moment is costly, so predictability wins over flexibility.

Batch and analytics jobs

Data processing jobs are often run as low-priority pods that can fill spare capacity. When the cluster is needed for user-facing services, the scheduler can preempt them. This raises overall usage without hurting important traffic.

Machine learning platforms

Training jobs request GPUs as whole devices and large amounts of memory. Dedicated node pools keep these expensive machines from being wasted on small pods, and quotas stop any one team from taking all the accelerators.

Managed cluster defaults

Managed Kubernetes services and node provisioning tools decide how many machines to start by reading pod requests. In these systems your numbers directly become your machine count, and therefore your invoice.

i
Pattern to notice

In every setting the story is the same: honest requests make placement and scaling work, and sensible limits make failures small.

25When It Does Not Work: A Troubleshooting Path

Match the symptom to its cause, then apply the fix.

SymptomLikely causeWhat to checkTypical fix
Pod stays PendingNo node has enough unreserved roomThe pod’s events, and each node’s requested versus allocatableLower the request, add nodes, or free space
Container restarts with OOMKilledMemory limit too low or a leakLast termination reason, memory graph before the killRaise the limit or fix the leak
Slow but healthy podCPU throttlingThrottled share of timeRaise or remove the CPU limit, reduce threads
Pod evictedNode under pressure, or disk limit crossedEviction message and node conditionsSet proper requests, reduce usage, add capacity
Pod rejected on creationQuota or LimitRange violationThe error text from the API serverAdjust the numbers or ask for a larger budget
Autoscaler scales wildly or neverWrong request as the base for percentageRequest versus real usageCorrect the request
1

Read the pod’s events first

Most resource problems announce themselves in the events: insufficient cpu, OOMKilled, evicted. Start there, not with guesses.

2

Compare usage with numbers

Look at live and recent usage beside the request and limit. The gap tells you which side to change.

3

Check the node

Is the node full of requests, or under pressure? A healthy pod on a struggling node still suffers.

4

Check namespace rules

A default from a LimitRange may have given your pod numbers you never wrote. Look at the final values the pod actually received.

5

Change one thing at a time

Adjust a single number, watch for a while, and only then adjust another. Changing many at once hides the cause.

26Frequently Asked Questions

Quick answers to the questions beginners ask most often.

Q1Can a container use more than its request?
Yes, as long as the node has free capacity and it stays under its limit. The request is a reservation, not a ceiling.
Q2What if I set a limit but no request?
Kubernetes uses the limit as the request too, unless a namespace default says otherwise. This can surprise you, because the pod then reserves as much as it is allowed to use.
Q3What if I set a request but no limit?
The container is guaranteed its request and may use any spare capacity on the node. There is no ceiling, which is risky for memory, because a leak can affect the whole node.
Q4Why is my pod Pending when the node looks idle?
The scheduler counts requests, not real usage. A node can be idle and still fully reserved. Look at how much of the allocatable space is already requested.
Q5What does exit code 137 mean?
The process was ended by a kill signal, most often because the container exceeded its memory limit. Check the reason in the pod status to confirm it says OOMKilled.
Q6Should I always set CPU limits?
Opinions differ. Always set CPU requests. Set limits if you need to protect neighbors or predict performance, and monitor throttling if you do. Many teams choose generous limits or none for trusted workloads.
Q7What is the difference between a millicore and a megabyte?
A millicore is a thousandth of a CPU core and measures processing time. A megabyte is a unit of memory. They measure different resources and are never interchangeable.
Q8How do I get the Guaranteed class?
Set both CPU and memory requests and limits on every container in the pod, and make each limit equal to its request. Kubernetes assigns the class on its own.
Q9Do requests and limits apply to init containers?
Yes. Init containers can have their own values, and the pod’s effective request is the larger of the biggest init container and the sum of the main containers.
Q10Can I change requests on a running pod?
Traditionally you had to recreate the pod. Recent versions can resize CPU and memory in place, depending on your cluster version and settings.
Q11Why does my autoscaler keep adding pods?
If it scales on CPU percentage, a request that is too small makes every pod look busy. Raise the request to match real typical usage.
Q12Is memory ever given back after a container uses it?
A program may release memory to the operating system, but many hold on to it for reuse. Plan limits around the highest amount the program holds, not the amount it needs at one instant.

27Summary and Key Takeaways

What to remember about requests and limits

  • A request is a reservation; a limit is a ceiling. The scheduler uses requests to place pods. The node enforces limits while pods run.
  • CPU bends, memory breaks. Going over a CPU limit slows the container through throttling. Going over a memory limit kills it with an OOMKilled result.
  • Units matter. Use millicores for CPU and Mi or Gi for memory. A lowercase m on memory means less than a byte.
  • Your numbers decide the service class. Guaranteed, Burstable, and BestEffort determine who is protected and who is evicted first under pressure.
  • Requests steer autoscaling and cost. Percentage-based scaling and node additions both depend on them, so wrong requests waste money or hurt users.
  • Guardrails scale the practice. LimitRange fills in defaults and boundaries, and ResourceQuota sets each namespace’s budget.
  • Measure, set, watch, adjust. Set values from real data, monitor throttling and memory, and revisit the numbers as traffic changes.