Cost Optimization in Kubernetes: Bin Packing and Spot Nodes

Cost Optimization in Kubernetes: Bin Packing and Spot Nodes

Why Kubernetes bills grow faster than traffic, how to pack workloads tightly onto fewer machines, how to run safely on discounted spot capacity, and how to keep the savings, explained from zero.

Picture a moving day. You have a hundred boxes of different sizes and a fleet of trucks you rent by the hour. If you toss boxes into trucks at random, most trucks leave half empty, and you pay for all that empty air. If you pack carefully, you need fewer trucks, and your bill drops. Now imagine that some trucks are offered at a big discount, with one catch: the rental company may take the truck back with only two minutes of warning. Would you use them? Only if your plan survives a truck disappearing in the middle of the road.

Running applications on Kubernetes is exactly this moving day. Your applications are the boxes. Your servers, called nodes, are the trucks. Bin packing is the art of fitting boxes tightly into as few trucks as possible. Spot nodes are the discounted trucks that can be taken back at short notice. Together, these two ideas are the biggest levers for lowering a Kubernetes bill. This post explains both from the very beginning, assuming you know nothing yet. By the end, you will understand where the money goes, why clusters are usually far emptier than anyone expects, how the scheduler and autoscalers pack work, how to use spot capacity without waking up at night, and how to talk about all of it clearly in a design review.

1The Problem: A Bill That Grows Faster Than Traffic

Almost every team that adopts Kubernetes goes through the same surprise. The platform works well, and then the cloud invoice arrives and nobody can explain it. Let us start by understanding why.

What you actually pay for

A cloud provider does not charge you for the applications you run. It charges you for the machines you reserve, for every hour they exist, whether or not they are busy. Kubernetes sits on top of those machines. It decides which application runs on which machine, but it does not change the fact that the machines are rented by the hour. So the real question of cost optimization is simple: how many machines do we rent, how big are they, how much do each cost, and how much useful work happens on them?

Everyday analogy

Think of an office building. The landlord charges rent for the floor space, not for the number of people who sit at the desks. If you rent ten floors but only four are in use, you still pay for ten. Cost optimization is the work of renting only the floors you need, filling them well, and negotiating a better rent.

Why the bill drifts upward

Cause

Generous guesses

Teams ask for more CPU and memory than they need, because asking for too little causes outages and asking for too much causes nothing visible.

Cause

Forgotten workloads

Test environments, old experiments, and unused services keep running because nobody is sure they can be deleted.

Cause

Always-on nodes

Machines stay up all night and over weekends, even when almost no one uses the applications.

Cause

Poor packing

Applications are spread across many machines in a way that leaves small unusable gaps everywhere.

Cause

Full-price purchasing

Every machine is bought at the standard on-demand rate, even for work that could tolerate interruptions.

Cause

No one owns the number

The invoice belongs to the whole company, so no single team feels the cost of its own choices.

The two levers in this post

Cost work has many parts, and later chapters cover several. But two levers stand out. The first is density: getting more useful work out of each machine through bin packing. The second is price: paying less per machine through spot capacity and commitments. Density reduces the number of machines. Price reduces the cost of each one. They multiply: if you need half as many machines, and each costs a third less, the bill falls to a third of what it was.

2
main levers: fewer machines and cheaper machines
1
rule that matters most: pay only for what you use
0
savings that are free of trade-offs
i
A note on numbers

This post gives ranges and rules of thumb rather than exact prices. Cloud prices, discount sizes, and product names change over time and differ between providers and regions. Use the ideas here, and check current prices and documentation for your own cloud.

2Where the Money Goes

Before cutting costs, you must know what you are cutting. A Kubernetes bill has several parts, and they do not all shrink with the same tools.

The main cost buckets

BucketWhat it isTypical shareMain lever
ComputeThe machines that run your podsUsually the largest partRight-sizing, bin packing, spot, commitments
StorageDisks attached to pods, snapshots, and backupsOften surprising and slowly growingDelete unused volumes, pick the right disk type, set retention
NetworkTraffic between zones, out to the internet, and through load balancers and gatewaysCan be large for chatty servicesKeep chatty services in one zone, reduce cross-zone traffic
Control plane and add-onsThe managed cluster fee, logging, monitoring, and security toolsSmall per cluster, large across many clustersFewer clusters, careful log retention
Idle and wasteReserved but unused capacityOften a third or more of computeEverything in this post

Compute is the big one

In most clusters, compute dominates. Compute cost depends on three numbers: how many nodes, how large each is, and the hourly price of each. The first number is driven by how well work is packed. The second is a design choice. The third depends on how you buy the machines, which is the subject of the spot and commitment chapters.

The hidden costs that surprise people

Costs people usually see

  • Node hours on the invoice.
  • Load balancer fees.
  • Managed database fees.

Costs people usually miss

  • Cross-zone data transfer between chatty services.
  • Orphaned disks left behind by deleted workloads.
  • Verbose logs stored for years.
  • Idle development clusters running all weekend.
Everyday analogy

A household budget has rent, groceries, and utilities. Rent is the biggest bill, so you look there first. But a few small subscriptions that you forgot to cancel can add up to a surprising total. Cost work in Kubernetes has the same shape: one big bill, plus a long tail of small leaks.

Production example: finding the first savings

Teams that begin cost reviews often report that the first and easiest wins are not clever. They are shutting down forgotten environments, deleting unused disks, and noticing that development clusters ran at full size over weekends. Only after clearing this surface waste do the more technical levers, such as bin packing and spot, become the main story.

3Requests, Limits, and the Scheduler

To understand bin packing, you must understand how Kubernetes decides where to put an application. Three ideas are needed: requests, limits, and the scheduler.

Pods and nodes

A pod is the smallest unit Kubernetes runs. It holds one or more containers, which are the running copies of your applications. A node is a machine, virtual or physical, that runs pods. A cluster is a group of nodes managed together. The scheduler is the part of Kubernetes that looks at each new pod and chooses which node it should run on.

Requests: what you reserve

Every container can declare a request for CPU and memory. A request is a promise from the cluster: “this container is guaranteed at least this much.” The scheduler uses requests, and only requests, to decide whether a pod fits on a node. If a node has four CPU cores and pods already on it have requested three, then a pod requesting two will not fit, even if the three existing pods are actually using only a tenth of a core.

Limits: the ceiling

A limit is the maximum a container may use. If a container tries to use more CPU than its limit, it is slowed down. If it tries to use more memory than its limit, it is killed and restarted. Limits protect neighbors from a greedy container. They do not affect where the pod is placed.

Everyday analogy

Think of booking seats on a train. The request is the number of seats you reserve. The railway holds those seats for you whether or not you sit in all of them, and it will not sell them to someone else. The limit is the most seats you are allowed to spread into if the train is empty. The scheduler is the ticket office that checks only reservations when deciding whether a new traveler fits.

Why the scheduler looks at requests, not usage

This choice is deliberate. Real usage changes second by second, so if the scheduler packed pods by current usage, a quiet moment could lead to a crowded node that then collapses when everyone gets busy. Requests are stable promises, so they give predictable placement. The price of this safety is that requests, not real usage, decide how many nodes you need. If requests are inflated, you pay for capacity that is reserved but empty.

flowchart LR
  P["New pod with CPU and memory requests"] --> S["Scheduler"]
  S --> F{"Filter: which nodes have enough unreserved room?"}
  F -->|"none"| PEND["Pod stays pending, autoscaler may add a node"]
  F -->|"some"| SC["Score the remaining nodes"]
  SC --> BEST["Choose the best-scoring node"]
  BEST --> RUN["Pod runs on that node"]
    
Figure 1: The scheduler filters nodes by requested room, scores the rest, and picks one

Allocatable capacity is smaller than the machine

A node’s advertised size is not all available for your pods. The operating system, the node agent called the kubelet, the container runtime, and system pods reserve a share. What remains is called allocatable capacity. On small nodes, the reserved share is a larger fraction, which is one reason very small nodes waste more. Chapter 12 returns to this point.

i
The one sentence to remember

You pay for nodes, nodes are filled according to requests, so the quality of your requests decides the size of your bill.

4The Slack Gap: Requested Versus Used

The most common source of waste is a simple gap between what applications ask for and what they use. We call this gap slack.

How the gap appears

When a team deploys a new service, nobody knows how much CPU and memory it will need. Asking for too little risks slow responses or crashes in front of customers. Asking for too much risks nothing that anyone sees. So teams pick generous numbers, often copied from another service, and then never revisit them. Months later, the service may be using a fifth of what it reserves.

flowchart TD
  NODE["Node: 8 CPU cores allocatable"] --> R["Requested by pods: 6 cores"]
  NODE --> U["Unreserved: 2 cores, can still schedule"]
  R --> USED["Actually used: 1.5 cores"]
  R --> SLACK["Slack: 4.5 cores reserved but idle"]
    
Figure 2: A node can look full to the scheduler while most of its reserved capacity sits idle
Everyday analogy

Imagine a restaurant that lets guests reserve tables. Every group asks for a table for eight, just in case friends show up. Most groups arrive as three people. The restaurant turns away walk-in customers because every table is reserved, while half the chairs are empty. Slack is empty chairs at reserved tables.

Typical sizes of the gap

Surveys of real clusters by cost tools and cloud vendors have repeatedly found that average CPU usage is a small fraction of what is requested, often well under half, and memory usage is higher but still well below requests. Exact numbers vary widely from company to company, so measure your own. The key lesson is that the gap is usually large enough that fixing it can shrink the node count noticeably, before any other technique is applied.

Why the gap persists

1

Fear of outages

An outage from too little memory is visible and painful. A bill from too much is quiet and diffuse.

2

Peak thinking

Teams size for the busiest minute of the year, and then pay for that peak every minute of every day.

3

Copy and paste

New services inherit the numbers of old ones, including the old ones’ waste.

4

Missing feedback

Developers rarely see the cost of their requests, so there is no pressure to tune them.

!
Do not cut blindly

Reducing requests without measuring can cause throttling and crashes. The goal is to match requests to real needs plus a safety margin, not to squeeze to the bone. The next chapter shows how to do it with evidence.

5Right-Sizing: Fixing the Requests First

Bin packing works on the numbers you give it. If the numbers are inflated, even perfect packing wastes money. So the first job is to make requests honest.

The right-sizing loop

1

Measure real usage

Collect CPU and memory usage over a long enough window, ideally including the busiest days, such as month-end or sale periods.

2

Choose a target percentile

Look at a high percentile of usage, such as the ninety-fifth or ninety-ninth, rather than the average, so normal peaks are covered.

3

Add a safety margin

Add a cushion for growth and surprises, commonly ten to thirty percent depending on the service.

4

Change gradually

Lower requests in steps and watch latency, error rates, and restarts after each step.

5

Repeat regularly

Usage changes as products change. Revisit the numbers every few months, or automate the loop.

CPU and memory behave differently

CPU is a compressible resource. If a container wants more than it has, it simply runs slower. Memory is incompressible. If a container needs more memory than its limit, the system kills it. This means you can be bolder when trimming CPU requests, and you must be more careful with memory. Many teams set memory requests equal to memory limits, for predictability, and treat CPU limits with caution, since very tight CPU limits can slow down applications even when the node has spare capacity.

Everyday analogy

CPU is like water pressure in a shared pipe: if many people use it at once, everyone gets a weaker stream, but nobody is cut off. Memory is like seats in a lifeboat: when they run out, someone must get off. You can share water more freely than seats.

Tools that help

Tool

Vertical Pod Autoscaler

Watches real usage and recommends, or applies, new requests. It can run in a recommendation-only mode, which is a safe way to start.

Tool

Cost dashboards

Tools such as OpenCost and Kubecost show requested versus used resources per workload, and highlight the biggest gaps.

Tool

Metrics systems

Prometheus-style monitoring gives the raw usage history that all right-sizing depends on.

Tool

Cloud vendor advisors

Managed platforms offer recommendations based on their own usage data. Confirm what is available for your provider.

Automatic versus manual changes

Recommendation-only

  • Safe: humans review each change.
  • Builds trust in the numbers.
  • Works with any release process.

Fully automatic

  • May restart pods to apply new values, depending on version and mode.
  • Can clash with other autoscalers if both watch the same metric.
  • Needs careful rollout for critical services.
!
Do not mix vertical and horizontal on the same signal

If a vertical autoscaler and a horizontal autoscaler both react to CPU for the same workload, they can fight each other. Common practice is to use the horizontal one for CPU or traffic, and the vertical one in recommendation mode, or to use them on different signals. Check the current documentation before combining them.

Production example: a right-sizing campaign

Platform teams at larger companies commonly run a recurring campaign. A report ranks services by wasted requested resources, owners receive a list of suggested new requests, and each change is applied during normal releases. Teams often describe this as the least glamorous and most reliable saving, because every other technique in this post works better afterwards.

6What Bin Packing Really Means

Now we reach the first main idea. Bin packing is an old problem from computer science, and it describes exactly what a cluster must do all day long.

The classic problem

You have items of different sizes, and bins of a fixed size. You want to fit all the items using as few bins as possible. In Kubernetes, the items are pods, sized by their requests. The bins are nodes, sized by their allocatable capacity. Fewer bins means fewer nodes, which means a smaller bill.

Everyday analogy

Think of packing a suitcase. If you throw in clothes at random, the lid will not close and you need a second suitcase. If you place the big items first and fill the gaps with socks, everything fits in one. A good packer thinks about shapes and gaps, not just the total volume.

Two dimensions at once

Real packing in Kubernetes is harder than packing a single number, because every pod has at least two sizes: CPU and memory. A node can be full on one and empty on the other. A memory-hungry pod and a CPU-hungry pod can share a node well, because they need different things. Two memory-hungry pods may fill the memory while leaving CPU unused. This mismatch is called stranded capacity, and we meet it again in the next chapters.

No perfect answer, only good ones

Finding the absolute best packing is a famously hard problem. Real systems use fast, simple rules that give good results most of the time. A common rule is to place larger items first and put each into the fullest bin that still has room. The Kubernetes scheduler and the node autoscalers use rules of this kind, plus extra constraints such as zones, affinities, and spreading requirements.

flowchart LR
  subgraph A["Poor packing: 4 nodes"]
    A1["Node 1: 40 percent full"]
    A2["Node 2: 35 percent full"]
    A3["Node 3: 30 percent full"]
    A4["Node 4: 45 percent full"]
  end
  subgraph B["Good packing: 2 nodes"]
    B1["Node 1: 90 percent full"]
    B2["Node 2: 60 percent full"]
  end
  A -->|"consolidate"| B
    
Figure 3: The same pods can need four nodes or two, depending on how they are packed

Why tight packing is not always the goal

Packing nodes to one hundred percent leaves no room for traffic spikes, node failures, or rolling updates. A cluster with no spare room will leave new pods pending whenever anything unusual happens. So the aim is tight but not brittle: high utilization with deliberate headroom. Many teams aim for requested utilization well above half and below the top, and tune from there according to how quickly new nodes can be added and how risky an outage is.

Benefits of tighter packing

  • Fewer nodes and a smaller bill.
  • Less idle power and lower carbon footprint.
  • Fewer machines to patch and monitor.

Risks of packing too tightly

  • Little room for spikes and rolling updates.
  • A single node failure displaces more pods.
  • Noisy neighbors can hurt each other more.

7How the Scheduler Chooses: Spread Versus Pack

The scheduler is the daily packer of the cluster. Its default behavior is tuned for safety, not for thrift, and understanding that default is the key to packing better.

Filter, then score

As Figure 1 showed, the scheduler works in two steps. First it filters: it removes nodes that cannot host the pod, because of insufficient requested room, taints, required labels, or other rules. Then it scores the remaining nodes with several plug-in rules and picks the highest total. Each rule looks at a different concern, such as whether the image is already downloaded on the node, whether the pod’s affinity wishes are met, or how balanced the node’s resources are.

The default favors spreading

By default, the scheduler tends to prefer nodes with more free room. This spreads pods across many nodes. Spreading is good for resilience, because no single node holds everything, and good for performance, because each pod has less competition. But it is bad for cost, because it keeps many nodes partly filled instead of filling a few completely, which blocks the autoscaler from removing the emptier nodes.

Everyday analogy

A hotel receptionist can assign guests in two ways. One way is to give each new guest a room on an empty floor, so every floor has a few guests and the lights stay on everywhere. The other is to fill one floor completely before opening the next, so that unused floors can be shut down. The first is comfortable. The second is cheap.

Choosing a packing strategy

StrategyWhat the scheduler prefersEffect on costEffect on resilience
Least allocated (spreading)Nodes with the most free requested roomHigher: many half-empty nodesStronger: pods spread widely
Most allocated (packing)Nodes that are already the fullestLower: nodes fill up, empty ones can be removedWeaker: a node loss displaces more pods
Requested-to-capacity ratioConfigurable shape between the twoTunableTunable

Where the setting lives

The scheduler can be configured through scheduler profiles, which let operators choose which scoring rules are active and how strongly each counts. On self-managed clusters you can change the default profile, or define a second scheduler profile and select it by name in specific pods. On many managed services, the default scheduler cannot be freely changed, and the cloud provider may offer a packing-friendly option or a different node provisioning tool instead. Always check what your provider allows, since this varies by service and by version.

!
Spreading rules still apply

Many production workloads carry topology spread constraints or anti-affinity rules to keep replicas in different zones or on different nodes. These rules are valuable and should stay for critical services. Remember they work against tight packing, so apply them where resilience truly matters, and not to every workload by habit.

Packing at placement time versus at removal time

There are two moments to pack well. At placement time, the scheduler chooses a good node for a new pod. At removal time, a consolidation tool moves pods off lightly used nodes and deletes those nodes. The first creates a good layout from the start. The second repairs the layout as pods come and go. Strong cost control uses both. The next chapters look at the second.

Production example: a second scheduler profile for batch work

Some organizations run a packing-oriented scheduler profile for batch and background jobs, which tolerate being crowded, while keeping the default spreading profile for customer-facing services. The two live in the same cluster, and each pod chooses its profile by name. This gives dense, cheap batch nodes without lowering the safety of the front end.

8Fragmentation and Stranded Resources

Even with a good scheduler, nodes develop small unusable gaps. Understanding these gaps explains why clusters seem full while still having plenty of unused capacity.

What fragmentation is

Fragmentation means that free capacity exists in total, but it is spread across nodes in pieces too small for any new pod. Imagine five nodes, each with one free CPU core. In total you have five free cores, yet a pod that needs four cores cannot be placed anywhere. The pod stays pending, and the autoscaler adds a new node, even though five cores of room were sitting idle.

Everyday analogy

A parking lot has twenty empty spaces, but they are scattered one by one between parked cars. A bus arrives needing three spaces in a row. There are plenty of empty spaces, but the bus cannot park. The lot is both empty and full, depending on what you are trying to fit.

Stranded resources

A related problem is stranding. Suppose a node has plenty of free CPU but no free memory, because the pods on it ask for a lot of memory. The spare CPU cannot be used by pods that also need memory, so it is stranded. Stranding happens when the mix of pod shapes does not match the shape of the nodes.

flowchart TD
  N1["Node A: CPU free, memory full"] --> S1["Spare CPU is stranded"]
  N2["Node B: memory free, CPU full"] --> S2["Spare memory is stranded"]
  S1 --> FIX["Fix: mix pod shapes, choose node shapes that match your workloads"]
  S2 --> FIX
    
Figure 4: Stranded capacity appears when pod shapes and node shapes do not match

Ways to reduce fragmentation

1

Use fewer pod sizes

A handful of standard sizes pack more cleanly than hundreds of unique ones. Odd request values create odd gaps.

2

Choose matching node shapes

If your workloads need about four times as much memory as CPU in gigabytes per core, pick node types with a similar ratio.

3

Separate very different workloads

Put memory-heavy and CPU-heavy workloads into separate node groups sized for each, when the mix is extreme.

4

Avoid huge pods

A pod that needs most of a node is hard to place and leaves large leftovers. Prefer more, smaller pods where the application allows.

5

Consolidate regularly

Use tools that move pods and remove emptied nodes, covered in the next chapters.

i
Measure it

Compare the total free requested capacity in the cluster with the number of pods stuck pending. If free capacity is high while pods are pending, fragmentation or stranding is the likely cause, and the answer is better shapes, not more nodes.

9Node Autoscaling: Adding and Removing Machines

Packing pods well is only half the story. Someone must also add nodes when pods do not fit and remove nodes when they are no longer needed. That is the job of node autoscalers.

The basic loop

A node autoscaler watches for pods that are pending because no node has room. It then adds a node that can fit them. It also watches for nodes that are empty or lightly used, and removes them when their pods can be safely moved elsewhere. The cluster therefore grows and shrinks with the work, and the bill follows.

sequenceDiagram
  participant D as Deployment
  participant S as Scheduler
  participant A as Node autoscaler
  participant C as Cloud API
  D->>S: Create new pods
  S-->>D: No node has room, pods pending
  A->>S: Sees pending pods
  A->>C: Request a new node
  C-->>A: Node joins the cluster
  S->>S: Place pending pods on the new node
  Note over A,S: Later, load falls
  A->>S: Finds a lightly used node
  A->>C: Drain pods and delete the node
    
Figure 5: Node autoscalers add nodes for pending pods and remove nodes that are no longer needed

Two families of autoscaler

QuestionGroup-based autoscalerDirect provisioning autoscaler
Well-known exampleCluster AutoscalerKarpenter, and similar tools offered by cloud vendors
How it adds capacityIncreases the size of a pre-defined node groupChooses and launches a node that fits the pending pods
Node varietyEach group has a fixed node type or a small setCan pick from many instance types per decision
Packing behaviorDepends on group design and schedulerConsiders pod requests together to choose nodes, and can consolidate
Setup effortDefine and tune many groupsDefine rules about allowed shapes and limits
Everyday analogy

A group-based autoscaler is like a taxi company with three vehicle types, who sends another car of whichever type is assigned to a route. A direct provisioning autoscaler is like a dispatcher who looks at the passengers waiting, sees that there are two people and a piano, and sends exactly the right vehicle, from the full fleet of options.

Why scale-down is the hard half

Adding a node is easy. Removing one is careful work. Before deleting a node, the autoscaler must check that every pod on it can be moved: that other nodes have room, that no pod has a rule that forbids moving, and that moving it will not break a service. Many clusters keep nodes alive for a long time because of a single stubborn pod. Common blockers are listed below.

Blocker

Pods with local storage

Pods that use storage tied to the node cannot be moved freely.

Blocker

No controller

Pods created directly, without a Deployment or similar owner, will not be recreated, so tools avoid removing them.

Blocker

Strict disruption budgets

A budget that allows zero interruptions prevents any pod from being evicted.

Blocker

Do-not-evict markers

Annotations that explicitly forbid removal, sometimes added by well-meaning teams and then forgotten.

Blocker

Tight anti-affinity

Rules that require each replica on its own node limit where pods can go.

Blocker

Daemon-style pods

Pods that must run on every node do not block removal by themselves, but badly configured ones can.

i
Check why nodes stay

When nodes refuse to scale down, look at the autoscaler’s events and logs. It usually names the exact pod or rule that blocked removal, which is the fastest path to the fix.

10Consolidation: Repacking a Running Cluster

Pods come and go all day. Over time, a once tidy cluster becomes patchy, with many half-empty nodes. Consolidation is the practice of tidying it again while it keeps running.

Why clusters get messy

Imagine a cluster at the end of a busy day. Traffic peaked at noon, so the autoscaler added many nodes. By evening, most pods were removed, but each node still holds a few survivors. Nothing is fully empty, so no node can be deleted, yet the total load would fit on far fewer machines. This is the natural drift toward waste.

Everyday analogy

After a party, guests leave at different times. The living room, kitchen, and balcony each still have two or three people. The host could ask everyone to gather in one room and turn off the lights in the others. Consolidation is that polite request, applied to pods.

How consolidation works

1

Find candidates

The tool looks for nodes that are empty or underused, whose pods could fit elsewhere.

2

Simulate the move

It checks that all pods can be rescheduled on the remaining nodes, respecting every rule.

3

Respect disruption budgets

It evicts pods gently, never taking too many replicas of one service at a time.

4

Drain and delete

Pods are evicted and recreated elsewhere, and the emptied node is removed.

5

Sometimes replace

A smarter tool may replace two lightly used nodes with one cheaper node of a different size, when that lowers cost.

Tools that consolidate

Cluster Autoscaler scale-down

  • Removes nodes below a utilization threshold after a delay.
  • Well understood and widely used.
  • Works inside the fixed node groups you defined.

Karpenter consolidation

  • Can delete empty nodes, merge underused nodes, and replace nodes with cheaper types.
  • Governed by a policy and optional time windows.
  • Powerful, so it needs good disruption budgets.

The descheduler

A separate tool called the descheduler evicts pods that are badly placed so that the scheduler can place them again. It can, for instance, evict pods from lightly loaded nodes so that those nodes empty out, or fix placements that no longer follow the rules. It does not add or remove nodes. It only moves pods, and then a node autoscaler removes the emptied nodes. Used together, they form a repacking pipeline.

flowchart LR
  A["Patchy cluster: many half-empty nodes"] --> B["Descheduler or consolidation evicts pods from light nodes"]
  B --> C["Scheduler repacks pods onto fuller nodes"]
  C --> D["Emptied nodes are deleted by the node autoscaler"]
  D --> E["Tighter cluster and lower bill"]
    
Figure 6: The repacking pipeline: evict, reschedule, delete
!
Consolidation is controlled disruption

Every move restarts pods. Applications that start slowly, hold long connections, or keep state in memory feel this the most. Set disruption budgets, graceful shutdown periods, and, if available, time windows so that consolidation does not run during your busiest hours.

Production example: consolidation with guard rails

Teams using direct provisioning autoscalers commonly report large drops in node count after enabling consolidation, but they also describe early surprises, such as a burst of restarts at an unlucky hour. The usual remedy is to protect critical services with disruption budgets, mark a few workloads as not to be disrupted, and limit how many nodes may be consolidated at once. After that, consolidation becomes a quiet daily saving.

11Choosing Node Sizes: Big, Small, and Mixed

The size of your nodes changes how well pods pack. This is one of the quietest, most powerful design choices in a cluster.

The trade-off in plain words

Large nodes pack well, because big gaps are rare and fixed overhead is shared by more pods. But a large node holds many pods, so losing one hurts more. Small nodes limit the damage of a failure and add capacity in small steps, but they waste more on overhead and leave awkward gaps. Neither extreme is right for most clusters.

Everyday analogy

Think of choosing between a few large moving trucks or many small vans. The big truck carries furniture easily and costs less per box, but if it breaks down, a lot of your move is stuck. Small vans can be added one at a time and a breakdown matters less, but each van needs a driver and cannot carry a wardrobe.

FactorLarger nodesSmaller nodes
Overhead shareLower: system reservations are spread across more podsHigher: reservations eat a bigger fraction
FragmentationLower for typical pod sizesHigher: small leftovers cannot hold a pod
Blast radius of a node failureLarger: many pods displaced togetherSmaller
Scaling granularityCoarse: capacity added in big stepsFine
Pod count limitsMay hit per-node pod limits or network address limits before CPU is usedRarely hit them
Fit for large podsGoodMay not fit at all

Hidden limits to check

Some cloud networking models limit how many pods a node can hold, based on how many network addresses the machine type supports. A large node may therefore run out of pod slots before it runs out of CPU, wasting capacity again. Check the pod-per-node limit for the instance types you plan to use, and the options your provider gives to raise it. These limits differ by provider and by version.

Mixed fleets win

The best answer is often not one size but a range. Direct provisioning autoscalers can choose from many instance types according to what is pending, which fits big pods onto big nodes and small leftovers onto small ones. Even with group-based autoscalers, a small number of groups with different sizes can help. Mixed fleets also help for a second reason that later chapters explain: they make spot capacity far more reliable.

flowchart TD
  Q1{"Are your pods large, with a few needing many cores?"} -->|"yes"| BIG["Include large node types"]
  Q1 -->|"no"| Q2{"Do you hit pod-per-node limits before CPU?"}
  Q2 -->|"yes"| MID["Prefer medium nodes or raise the limit"]
  Q2 -->|"no"| Q3{"Is a single node failure very costly?"}
  Q3 -->|"yes"| SMALL["Prefer more, smaller nodes"]
  Q3 -->|"no"| MIX["Use a mixed fleet and let the autoscaler choose"]
    
Figure 7: A simple guide for choosing node sizes

Matching the CPU-to-memory ratio

Cloud machine families come in shapes: some have much memory per core, some have little. If your workloads use about four gigabytes per core, a family with that ratio avoids stranding. If a few workloads are unusual, such as large caches or compute-heavy jobs, put them on separate node pools labeled for them. Check your actual ratio from your own usage data before deciding.

Newer processor families

Many clouds offer processors based on different designs, often priced lower for similar performance on suitable workloads. If your applications run on more than one processor architecture, a mixed fleet can lower costs further. This requires images built for each architecture and testing, so treat it as a later optimization once the basics are done.

Production example: widening the instance list

A common lesson from teams adopting direct provisioning is that allowing many instance families and sizes, rather than naming one, gives the autoscaler room to pick the cheapest node that fits, and to find spot capacity more easily. Restricting the list to a single type is the most frequent reason savings fall short of what vendors promise.

12Spot Nodes: Cheap Machines With a Catch

We now turn to the second lever: price. Spot capacity is the largest single discount most teams can access, and also the one that demands the most care.

What spot capacity is

Cloud providers keep large amounts of spare machines so they can meet demand. When some of those machines are idle, the provider sells them at a steep discount. This is called spot capacity on some clouds, and goes by similar names such as preemptible or low-priority machines on others. The discount is commonly large, often well over half off the standard price, though the exact figure changes by time, region, and machine type. The catch is that the provider can take the machine back when it needs the capacity, usually with a short warning.

Everyday analogy

It is like a hotel selling empty rooms at a deep discount on the day of stay. You get the room cheaply, but if a full-price guest arrives and no other rooms are free, the hotel may ask you to leave after a short notice. If your plans can handle moving to another room, you save a lot. If you must stay put, the discount is not for you.

The three ways to buy compute

OptionPriceCommitmentCan be taken back?Best for
On-demandStandardNoneNo, apart from failuresCritical and unpredictable workloads
Committed use (reserved or savings plans)Discounted, commonly by a quarter to a half or more depending on termOne to three years of spend or capacityNoSteady baseline load
SpotDeepest discountNoneYes, with short noticeFault-tolerant, flexible workloads

Why spot fits Kubernetes so well

Kubernetes was built on the assumption that machines fail. It restarts pods elsewhere, replaces unhealthy nodes, and keeps the desired number of copies running. A spot interruption is like a machine failure that comes with advance notice, which is actually easier to handle. So if your applications already tolerate a node disappearing, spot is a natural fit. The remaining work is making that tolerance real instead of assumed.

Often 50+%
typical discount range versus on-demand, varying by time and type
Short
warning before reclaim, commonly seconds to a couple of minutes
Varies
interruption frequency by instance type, region, and zone
i
Confirm the rules for your cloud

Each provider has its own notice period, interruption signals, and pricing model, and these have changed over the years. Before building anything, read your provider’s current documentation on its spot or preemptible offering.

13Surviving an Interruption

The central skill of running on spot is handling the moment a node is taken away. Let us walk through what happens, and what makes the difference between a calm event and an outage.

The life of an interruption

sequenceDiagram
  participant Cloud as Cloud provider
  participant H as Termination handler
  participant K as Kubernetes
  participant P as Pods on the node
  participant A as Autoscaler
  Cloud->>H: Interruption notice with short countdown
  H->>K: Mark node unschedulable (cordon)
  H->>K: Drain node, evict pods gently
  K->>P: Send termination signal, start grace period
  P-->>K: Finish requests and shut down cleanly
  K->>K: Recreate pods on other nodes
  A->>Cloud: Request replacement capacity if needed
  Cloud->>Cloud: Take the machine back
    
Figure 8: What happens between the warning and the machine disappearing

The pieces that make it safe

Piece

Interruption handling

Something must notice the warning and start draining the node before it vanishes. Managed offerings and autoscalers often include this, and standalone handlers exist when they do not.

Piece

Several replicas

A service with only one copy goes down when that copy’s node is reclaimed. Run at least two, spread across nodes.

Piece

Pod disruption budgets

A budget tells Kubernetes how many replicas may be unavailable at once, so that a drain does not remove too many together.

Piece

Graceful shutdown

The application must finish in-flight work and close connections within the grace period after it receives the termination signal.

Piece

Fast startup

Pods that start quickly recover quickly. Slow startup stretches every interruption into a longer dip.

Piece

Readiness checks

Accurate readiness checks ensure traffic only reaches pods that can serve it, including freshly started replacements.

The two-minute reality

Warnings are short. Some clouds give around two minutes, others give less, and some give very little for certain machine types. That means a drain must be quick, and your pod’s shutdown grace period should fit inside the warning window. A pod that takes five minutes to shut down will simply be killed halfway. Test your actual shutdown time before trusting it.

Everyday analogy

It is a fire drill. Everyone must leave the building in the time the alarm allows. A drill is only useful if people know the exits, close their work quickly, and meet at a known place. A plan that needs ten minutes will not help in a building with a two-minute alarm.

Pod disruption budgets in practice

A pod disruption budget states the smallest number, or share, of replicas that must stay available during voluntary disruptions such as drains. It protects against well-behaved removals, which include consolidation and graceful spot handling. It cannot stop a hard failure or an abrupt reclaim. Set budgets that match reality: a budget that allows no disruption at all blocks consolidation and draining, while one that is too loose may allow too many replicas to leave together.

Signs your service is spot-ready

  • Three or more replicas spread across nodes and zones.
  • Pod disruption budget in place.
  • Shutdown completes well within the warning window.
  • No local state that cannot be rebuilt.

Signs it is not ready

  • Single replica.
  • Long, stateful startup or shutdown.
  • Data stored only on the node’s local disk.
  • Long-running jobs with no checkpoints.

Production example: interruption handling as a standard add-on

Teams running spot at scale usually deploy interruption handling as a standard part of their cluster build, and rehearse it by deliberately terminating spot nodes in a test environment. They treat the first successful rehearsal as the moment spot becomes approved for a service, and not before.

14Spreading Spot Risk: Many Pools, Many Zones

Spot interruptions are not random in one way: machines of the same type in the same zone are often reclaimed together. The way to stay safe is to avoid putting all your eggs in one pool.

What a capacity pool is

A capacity pool is the set of spare machines of one type in one zone. Prices and interruption chances are set per pool. When demand rises in a pool, spot machines there may be reclaimed together, and new ones may be unavailable for a while. If your whole fleet is one instance type in one zone, a single pool event can remove everything at once.

Everyday analogy

Imagine buying vegetables from only one farm, in one village, on one road. If a flood closes that road, you have nothing. If you buy from many farms in many villages, a flood hurts only a part of your supply. Diversifying pools is the same idea applied to machines.

How to diversify

1

Use many instance types

Allow a wide list of sizes and families that suit your workloads, such as several that have similar CPU and memory.

2

Use several zones

Spread nodes across at least two or three zones in the region, so a zone-wide shortage affects only part of the fleet.

3

Choose a sensible allocation policy

Many platforms offer strategies that pick pools with lower interruption chance, rather than only the lowest price. The cheapest pool is often the one most likely to be reclaimed.

4

Keep an on-demand fallback

Allow the autoscaler to fall back to on-demand machines when spot is unavailable, so pods do not stay pending.

5

Spread replicas across pools

Use topology spread rules so replicas of one service sit on different nodes and zones, not all in a single pool.

flowchart TD
  W["Workload needs capacity"] --> SP{"Is spot capacity available in any allowed pool?"}
  SP -->|"yes"| PICK["Pick a pool with low interruption chance and good price"]
  SP -->|"no"| OD["Fall back to on-demand capacity"]
  PICK --> RUN["Run pods on spot nodes"]
  OD --> RUN2["Run pods on on-demand nodes"]
  RUN -.->|"interruption"| REPL["Replace in another pool"]
    
Figure 9: Diversified spot with an on-demand fallback keeps pods running when a pool dries up

The cost of a narrow list

When teams choose only one or two instance types for spot, they see frequent interruptions and long periods with no capacity, and conclude that spot is unreliable. Often the real cause is the narrow list. Widening it usually improves both price and stability. It does require your workloads to run acceptably on any of the allowed types, so avoid requiring a very specific machine unless truly necessary.

!
Availability is not guaranteed

Even a well-diversified fleet can hit times when spot capacity is scarce, such as during large demand events in a region. Always plan for the case where a meaningful share of spot capacity cannot be obtained, and make sure the fallback to on-demand is automatic and has enough budget headroom.

Production example: a mixed fleet for a streaming-style service

Large consumer platforms that stream video or process events commonly run most of their stateless processing on spot across many instance types and zones, with a small on-demand core. The core keeps the service alive during rare shortages, while the spot majority provides the savings.

15Which Workloads Belong on Spot

Not every workload should run on spot. The skill is sorting your applications into the ones that welcome interruption and the ones that cannot take it.

A simple sorting guide

Workload typeSpot suitabilityWhy
Stateless web and API services with several replicasGood, with a mixed floor of on-demandReplicas replace each other quickly
Queue workers and event processorsVery goodWork returns to the queue and another worker picks it up
Batch and data processing jobsVery good, with retries or checkpointsCheap to restart, big savings on long runs
Continuous integration buildsVery goodFailed builds can simply rerun
Machine learning training with checkpointsGoodResume from the last saved point
Development and test environmentsVery goodInterruption is an inconvenience, not an outage
Databases and stateful storesUsually poorLosing a node triggers recovery, and some designs risk data loss or long outages
Single-replica critical servicesNoAny interruption is downtime
Cluster control services and ingressUsually noThe cluster itself depends on them
Everyday analogy

Think of a courier company using a mix of company trucks and hired freelancers. Routine parcels go with freelancers, because a missed pickup just means another driver takes it. The vault shipment with valuables always goes in a company truck. Sorting by what a failure costs is the whole decision.

The mixed-floor pattern

For customer-facing services, a popular pattern keeps a floor of on-demand capacity that can serve the minimum safe traffic by itself, and runs the rest of the replicas on spot. If every spot node vanishes, the service continues at a reduced but safe level while replacements arrive. This pattern gives most of the discount with a modest risk.

flowchart LR
  subgraph FLOOR["On-demand floor: always on"]
    F1["Replica 1"]
    F2["Replica 2"]
  end
  subgraph SPOTS["Spot layer: cheap, can disappear"]
    S1["Replica 3"]
    S2["Replica 4"]
    S3["Replica 5"]
    S4["Replica 6"]
  end
  LB["Traffic"] --> FLOOR
  LB --> SPOTS
    
Figure 10: A small on-demand floor protects the service while spot provides the bulk of the capacity

How to steer workloads

Kubernetes lets you steer pods using node labels, taints and tolerations, and affinity rules. Spot nodes are usually labeled and sometimes tainted so that only workloads that explicitly accept them land there. A pod can say it prefers spot but accepts on-demand, or that it must avoid spot entirely. Preferences, rather than hard requirements, keep pods schedulable when spot is scarce.

Prefer rather than require

  • Pods still run if spot is unavailable.
  • The autoscaler has more freedom to pack.
  • Fewer stuck pending pods.

Require spot only for tolerant jobs

  • Good for batch jobs that can wait.
  • Bad for services where waiting is an outage.
  • Needs a clear owner who accepts delays.
i
Start with the safest candidates

Move development environments, continuous integration, and batch jobs to spot first. They give early savings, teach your team how interruptions behave, and carry little customer risk. Move customer-facing services only after you have rehearsed interruptions.

16The Layers of Autoscaling

Cost savings come from many scaling mechanisms working together, each at a different layer. Knowing which layer does what stops them from fighting each other.

Four layers, four questions

LayerQuestion it answersWhat it changesTypical tool
Pod sizeHow big should each pod be?CPU and memory requestsVertical Pod Autoscaler
Pod countHow many pods do we need right now?Number of replicasHorizontal Pod Autoscaler, event-driven autoscalers such as KEDA
Node countHow many machines hold those pods?Number and type of nodesCluster Autoscaler, Karpenter
Purchase typeHow do we pay for those machines?Spot, on-demand, committedAllocation policies, commitments
Everyday analogy

Think of running a delivery business. Pod size is how big each delivery van is. Pod count is how many vans are on the road. Node count is how many garages you rent to park them. Purchase type is whether you lease, rent daily, or buy at a discount. A smart manager adjusts all four, in the right order.

flowchart TD
  T["Traffic rises"] --> H["Horizontal autoscaler adds pods"]
  H --> PEND["New pods may be pending"]
  PEND --> N["Node autoscaler adds nodes"]
  N --> BUY["Allocation policy chooses spot or on-demand"]
  V["Vertical recommendations tune pod sizes over time"] -.-> H
  T2["Traffic falls"] --> H2["Horizontal autoscaler removes pods"]
  H2 --> C["Consolidation removes empty nodes"]
    
Figure 11: Each layer reacts to the one above it, from traffic to pods to nodes to purchase type

The horizontal autoscaler

The Horizontal Pod Autoscaler changes the number of replicas according to a measured signal, most often average CPU use, and in newer setups custom metrics such as requests per second. Because it measures usage as a share of the request, an inflated request makes it scale too late, since utilization appears low. Another reason right-sizing comes first.

Event-driven scaling

Event-driven autoscalers, with KEDA being a widely used example, scale workloads according to external signals such as queue length, and can scale to zero when there is no work. This is useful for background workers, which often idle for long stretches. Scaling to zero removes their cost entirely during those stretches, and lets nodes be released.

The pitfalls of stacking

Pitfall

Slow node start

If new nodes take minutes to join, pods wait pending during spikes. Keep some headroom or use faster provisioning.

Pitfall

Flapping

Scaling up and down too eagerly wastes work and causes restarts. Use stabilization windows so a brief dip does not trigger removal.

Pitfall

Competing signals

Two autoscalers reacting to the same metric can fight. Give each layer its own job.

Pitfall

Ceiling without a floor

Maximum limits protect budgets, but minimums protect users. Set both on purpose.

Headroom: paying a little for speed

When a spike arrives, adding a node takes time, commonly from tens of seconds to several minutes. Some teams run a small amount of placeholder pods at low priority that reserve space. When real pods arrive, the placeholders are evicted instantly, the real pods start at once, and the autoscaler quietly adds a node to restore the placeholders. This costs a little but protects user experience during bursts.

i
Savings should not become slowness

If tight cost settings make users wait during every traffic rise, the saving is false economy. Measure both the bill and the user experience, and tune headroom to the point where both are acceptable.

17The Baseline: Commitments and Discounts

Spot is for flexible work. For the steady core of your load, there is a different discount: promise the cloud provider a certain amount of usage in return for a lower price.

How commitments work

Cloud providers offer discounts, commonly called reserved instances, savings plans, or committed use discounts, in exchange for committing to a level of spending or capacity for one to three years. The discount can be substantial, often between a quarter and well over a third or more depending on term and flexibility. The risk is that you pay for the commitment even if you use less than promised.

Everyday analogy

It is like a gym membership versus paying per visit. If you go every week, the yearly membership is far cheaper. If you stop going, you still pay. The wise move is to commit only to the number of visits you are sure to make.

Layering the three purchase types

flowchart TD
  L["Total compute demand over time"] --> B["Steady baseline: covered by commitments"]
  L --> M["Predictable variable load: on-demand"]
  L --> S["Flexible, interruption-tolerant load: spot"]
  B --> C1["Lowest guaranteed price"]
  M --> C2["Full price but no commitment"]
  S --> C3["Cheapest per hour but can be reclaimed"]
    
Figure 12: A layered buying strategy: commit for the floor, spot for the flexible, on-demand for the rest

The right order of operations

1

Right-size and pack first

Reduce waste before buying commitments, otherwise you commit to capacity you would have removed.

2

Introduce spot for flexible work

This lowers and reshapes the on-demand demand curve.

3

Measure the stable floor

Look at the lowest level of on-demand use that persists over weeks, not at peaks.

4

Commit to a share of that floor

Start with a conservative share, such as most of the stable minimum, and add later as confidence grows.

5

Review regularly

Check coverage and utilization of commitments, and renew or adjust as plans change.

!
Commit last, not first

A common and costly mistake is buying large commitments before right-sizing. When requests are later reduced, the committed capacity is no longer needed, and the savings from optimization are partly swallowed by commitment payments that cannot be undone.

Production example: a three-layer strategy

Finance-minded engineering teams often describe their strategy as three layers: committed spend that covers the always-on core, spot for the elastic and fault-tolerant layers, and on-demand as the flexible buffer between them. Reviews every quarter check each layer’s share of total spend and move work between layers when habits change.

18Storage and Network: The Costs Beyond Compute

After compute, the next surprises are usually disks and data transfer. They are less visible, and often easier to reduce than people expect.

Storage costs

Pods that need durable data request persistent volumes, which are disks from the cloud. These disks are billed whether or not anything reads them. Several patterns inflate the bill: disks left behind after a workload was deleted, volumes sized far above real need, expensive high-performance disk types used for data that is rarely touched, and snapshots kept forever.

Storage

Orphaned volumes

Check which volumes are not attached to any pod. Many clusters carry a surprising number, and reclaim policies decide whether they are removed automatically.

Storage

Oversized claims

Volumes are often created with a round, generous size. Many cloud disks can be grown later, so start modest and expand.

Storage

Wrong disk class

Use faster, costlier disks only where performance demands them. Logs, archives, and rarely read data fit cheaper classes.

Storage

Snapshot retention

Set retention windows. A backup from three years ago is rarely useful and always billed.

Network costs

Cloud providers usually charge for data that crosses zone boundaries, and more for data that leaves to the internet. Inside a cluster that spans several zones, services that talk to each other a lot can generate large cross-zone transfer without anyone noticing. A chatty service pair in different zones, exchanging a steady flow of data, can cost more in transfer than the machines themselves.

Everyday analogy

Imagine a courier company that charges a small fee whenever a parcel crosses a district boundary. If two offices that exchange hundreds of parcels a day sit in different districts, the fees add up. Moving them into the same district removes the fees without changing any work.

Ways to reduce transfer costs

1

Measure first

Use network cost reports or flow data to find which services generate the most cross-zone and outbound traffic.

2

Prefer same-zone routing

Kubernetes offers topology-aware routing options that prefer endpoints in the same zone as the caller. Check which option your version supports and its conditions.

3

Co-locate chatty pairs

Use affinity preferences to place closely coupled services near each other, balancing against resilience.

4

Cache and compress

Reduce the volume of data crossing boundaries by caching results close to callers and compressing payloads.

5

Watch gateways and NAT

Outbound traffic through address translation gateways is often billed per gigabyte. Private endpoints to cloud services can avoid some of it.

Benefit of same-zone preference

  • Lower transfer fees.
  • Lower latency between services.

Trade-off

  • Uneven load if one zone has fewer pods.
  • Reduced spread if a zone fails, depending on settings.

Production example: the log bill

A frequent surprise in production is that observability costs rival compute. Verbose debug logs shipped to a paid logging service at high volume can cost more than the nodes that produce them. Teams cut this by lowering log levels, sampling, dropping noisy fields, and setting short retention for low-value logs.

19Time-Based Savings: Turning Things Off

The cheapest machine is one that is not running. Many environments are used only during working hours, yet run all day and night.

The simple arithmetic

A week has one hundred sixty-eight hours. A development environment used from nine to six on weekdays is needed for only about forty-five of them, which is under thirty percent. Running it around the clock means paying for more than seventy percent of the time it sits idle. Switching it off outside working hours cuts its compute cost by well over half.

168
hours in a week
~45
hours of typical weekday office use
70%+
share of time an always-on dev environment sits idle
Everyday analogy

It is the same reason you switch off the lights when you leave the room. One bulb costs little. A building with every light on all night is another matter. Environments that nobody is using are rooms with the lights on.

Ways to turn things off

Method

Scheduled scale-down

A scheduled job or tool sets the replica count of development workloads to zero in the evening and restores it in the morning. Node autoscaling then removes the empty machines.

Method

Scale to zero on idle

Event-driven tools scale workloads to zero when there is no traffic or work, and start them on demand.

Method

Ephemeral environments

Create a full test environment for each change and delete it when the change is merged, rather than keeping long-lived copies.

Method

Expiry labels

Require a time-to-live label on temporary namespaces, and have a cleaner delete expired ones.

The cold start question

Switching things off has a price: when someone arrives in the morning, the environment must start. If starting takes ten minutes, people complain. Good designs start environments a little before working hours, keep images cached, and keep startup times short. For on-demand scale-to-zero, the first request may be slow, which is acceptable for internal tools but not for customer-facing paths.

!
Beware shared dependencies

Scaling down one service that others quietly depend on can break far-away tests or jobs. Keep a list of dependencies, and announce the schedule clearly so that people on other time zones or night shifts are not surprised.

Production example: nightly and weekend shutdowns

Many engineering organizations report that simply shutting non-production clusters or namespaces outside working hours was among their first, and most popular, savings. It requires almost no technical change in the applications, only agreement on the schedule and a reliable way to bring things back.

20Visibility and Accountability: Making Cost Everyone’s Job

Technical levers only work if the organization notices cost. People optimize what they can see. This chapter covers how to make spend visible and owned.

Allocation: who spent what

Cloud invoices list machines, not teams. In a shared cluster, a single machine may host pods from ten teams. To divide the bill, tools measure each pod’s share of the node, based on its requests and sometimes its usage, and add up the shares per namespace, label, or team. Open-source and commercial tools, such as OpenCost and Kubecost, do this by combining cluster metrics with cloud price data.

Everyday analogy

Think of an apartment building with one shared electricity bill. Without meters, nobody saves energy, because the cost is split no matter what. Install a meter in each flat, and behavior changes. Cost allocation tools are the meters for a shared cluster.

Showback and chargeback

ApproachWhat happensEffect on behavior
ShowbackTeams see their cost in reports, but are not chargedGentle awareness, easy to start
ChargebackThe cost is moved to each team’s budgetStrong incentive, needs fair and trusted numbers

Guardrails inside the cluster

Guardrail

Resource quotas

Cap the total CPU, memory, and object counts a namespace may request, so one team cannot take the whole cluster.

Guardrail

Limit ranges

Set default requests and limits for pods that do not state them, and maximums for those that do.

Guardrail

Policy checks

Admission rules can require cost labels, reject missing requests, and block unsafe settings such as oversized requests without approval.

Guardrail

Budget alerts

Send a message when a team’s spend rises faster than its forecast, so surprises are caught in days rather than at month end.

Labels make everything possible

Consistent labels for team, service, environment, and cost center are the foundation of allocation. Without them, a large share of spend stays “unallocated” and nobody feels responsible. Make labels required by policy at deployment, rather than asking people to remember.

The FinOps habit

The discipline of managing cloud cost as a shared engineering and finance activity is often called FinOps. Its core cycle is to inform teams with clear data, to optimize with the techniques in this post, and to operate with regular reviews and ownership. The practice matters as much as any single tool, because savings that are not reviewed tend to slip away.

i
Show cost next to the code

When developers see an estimate of cost in their pull request or dashboard, they start asking cost questions on their own. A number placed where decisions are made is worth more than a monthly report nobody opens.

Production example: a monthly cost review

Many platform teams run a short monthly review. They look at the top spenders, the largest requested-versus-used gaps, orphaned resources, and the share of compute on spot and under commitments. Owners take away a short list of actions, and the next review begins by checking whether they were done.

21A Worked Example: Cutting a Cluster Bill Step by Step

Let us walk through a story, using plain numbers and no code. The company and figures are invented to teach the order of steps. Real results depend on your workloads, prices, and starting waste.

The starting point

A mid-sized online shop runs its services on one cluster with sixty identical on-demand nodes, running day and night. A review shows that nodes are about thirty percent utilized by real usage, while the scheduler sees them as around seventy percent full by requests. Development and test environments sit in the same cluster. The monthly compute bill is the baseline we call one hundred units.

The five steps

1

Clean the surface

The team deletes forgotten test namespaces, unattached disks, and idle load balancers. The bill falls a few units, without touching any production service.

2

Right-size requests

Using a high percentile of real usage plus a margin, the team lowers requests for the largest services over several weeks. Requested utilization drops, so fewer nodes are needed for the same pods. The node count falls by roughly a quarter.

3

Enable consolidation and a mixed fleet

The team adopts a direct provisioning autoscaler with a wide list of instance types and consolidation turned on, behind disruption budgets. The cluster now follows daily traffic, and nodes shrink at night.

4

Move flexible work to spot

Queue workers, batch jobs, continuous integration, and test environments move to spot, while customer-facing services keep an on-demand floor and use spot for extra replicas.

5

Commit to the new floor

After a few months of stable data, the team commits to a share of the remaining always-on on-demand usage, with a conservative amount.

An illustrative result

StageIllustrative compute cost indexWhat changed
Start100Sixty on-demand nodes, always on
After cleanup95Waste removed
After right-sizing72Fewer nodes needed
After consolidation and mixed fleet58Night shrink, better packing
After spot for flexible work40Large discount on a large share
After commitments on the floor34Lower rate for always-on baseline
!
These numbers are only a teaching sketch

The percentages above are invented to show how savings stack. Some organizations find more, some less. The lesson is the order: reduce waste first, then pack, then pay less per machine, then commit.

flowchart LR
  S0["Start: index 100"] --> S1["Cleanup: 95"]
  S1 --> S2["Right-size: 72"]
  S2 --> S3["Consolidate and mix: 58"]
  S3 --> S4["Spot for flexible work: 40"]
  S4 --> S5["Commit the floor: 34"]
    
Figure 13: An illustrative path from a wasteful cluster to a lean one, one lever at a time

What the team measured along the way

At each step, the team watched user-facing latency, error rates, pod restarts, and pending pods. One step, right-sizing memory, caused a handful of restarts that were fixed by raising two requests. Without these measurements, the team would not have known whether a saving was healthy or a hidden risk.

22An Incident Walkthrough: When Spot Capacity Disappears

The following story is a fictional composite of events that teams commonly describe. It shows how a good design absorbs a spot shortage, and how a weak one does not.

The setting

A ticketing company runs its booking front end on spot nodes, to save money. On a busy afternoon, with a popular event on sale, a large number of spot nodes in one zone receive interruption notices within minutes of each other.

Version one: the weak design

1

One instance type, one zone

The whole spot fleet was a single machine type in a single zone, chosen because it was cheapest.

2

No on-demand floor

Every replica of the booking service lived on spot nodes.

3

The pool dries up

The provider reclaims most nodes at once. Replacement spot capacity of the same type is unavailable, so new pods stay pending.

4

Outage

With most replicas gone and few new ones starting, customers see errors at the busiest moment of the year.

Version two: the redesigned system

After the incident, the team made five changes. They widened the allowed instance list to many families and sizes, spread nodes over three zones, kept a small on-demand floor able to serve minimum traffic, set pod disruption budgets, and rehearsed interruptions monthly by terminating spot nodes in a staging cluster.

sequenceDiagram
  participant Cloud as Cloud provider
  participant K as Cluster
  participant F as On-demand floor
  participant S as Spot layer
  participant U as Users
  Cloud->>S: Interruption notices in one pool
  S->>K: Nodes cordoned and drained
  K->>F: Floor keeps serving traffic
  K->>Cloud: Autoscaler requests other pools and zones
  Cloud-->>K: Capacity found in other pools
  K->>S: New spot nodes join, pods rescheduled
  U->>F: Requests continue, slightly slower
    
Figure 14: The redesigned system keeps serving users from the floor while spot capacity is replaced elsewhere

What the team learned

Lessons that worked

  • Diversify pools: many types, many zones.
  • Keep a floor that can carry minimum safe traffic.
  • Rehearse the failure before it happens.
  • Alert on pending pods and on spot fallback rates.

Mistakes to avoid

  • Choosing the single cheapest pool.
  • Running every replica on spot.
  • Assuming replacement capacity will always exist.
  • Never testing the interruption path.
i
Price the risk honestly

The saving from running the last few replicas on spot is small compared with the cost of one serious outage. The wise line is to stop spot at the point where one more replica on spot saves a little and risks a lot.

23The Trade-Offs: Cost, Reliability, and Speed

Every technique in this post trades something for money. Good engineering does not hide these trades. It names them and chooses them on purpose.

The triangle

Think of three goals pulling against each other: low cost, high reliability, and fast response to change. You can improve any two easily, but the third suffers. A cluster packed tightly and fed on spot is cheap, and may be less reliable and slower to absorb spikes. A cluster with generous headroom and on-demand machines is reliable and quick, and expensive. The art is choosing the right mix for each workload.

flowchart TD
  C["Low cost"] --- R["High reliability"]
  R --- F["Fast response to spikes"]
  F --- C
  C -.- NOTE1["Pushing cost down: tighter packing, more spot"]
  R -.- NOTE2["Pushing reliability up: spread, headroom, on-demand"]
  F -.- NOTE3["Pushing speed up: spare capacity, warm nodes"]
    
Figure 15: Cost, reliability, and speed pull against each other, and every setting is a choice among them

Technique by technique

TechniqueWhat you gainWhat you riskHow to soften the risk
Lower requestsFewer nodesThrottling, memory killsUse a high percentile and a margin, change gradually
Packing scheduler profileFuller nodes, easier scale-downLarger blast radius per nodeKeep spread rules for critical services
ConsolidationFewer nodes automaticallyFrequent pod restartsDisruption budgets and quiet-hour windows
Spot nodesDeep price discountSudden node lossDiversify, on-demand floor, graceful shutdown
Scale to zeroNo cost when idleSlow first requestUse only for tolerant paths
CommitmentsLower ratePaying for unused commitmentCommit to a conservative share of the stable floor
Same-zone routingLower transfer costUneven load, less spreadKeep enough replicas in every zone
Everyday analogy

Think of a hospital emergency room. A hospital that ran every bed at one hundred percent would be very efficient on paper, and would turn away a car crash victim. Some empty beds are not waste; they are insurance. The cost question is how much insurance is worth buying for each service.

Different services deserve different settings

Not every workload needs the same balance. A payment service deserves generous headroom and on-demand machines. A nightly report generator deserves the cheapest spot nodes and tight packing. Create a small number of service tiers, each with a clear recipe for requests, spread, purchase type, and disruption rules. Teams then choose a tier instead of arguing about every setting.

Tier

Critical

On-demand nodes, generous headroom, strict spread, strong disruption budgets.

Tier

Standard

On-demand floor plus spot extras, balanced packing, moderate headroom.

Tier

Flexible

Spot first, tight packing, retries and checkpoints, delay tolerated.

Tier

Non-production

Spot, scheduled shutdown, expiry labels, smallest sizes.

24Best Practices

Here are the habits that teams with lean, stable clusters tend to share. Think of them as a checklist to return to.

Requests

Make requests honest

Base them on a high percentile of measured usage plus a margin, and review them on a schedule.

Packing

Fewer, standard pod sizes

A small menu of sizes packs better than hundreds of unique values.

Nodes

Wide instance lists

Let the autoscaler pick among many families and sizes that suit your workloads.

Spot

Diversify and keep a floor

Many pools, several zones, an on-demand fallback, and a floor for customer-facing services.

Resilience

Rehearse interruptions

Terminate spot nodes in a safe environment regularly, and measure what users feel.

Disruption

Set budgets and grace periods

Disruption budgets and realistic shutdown times make every drain safe.

Order

Commit last

Right-size, pack, and add spot before buying long commitments.

Visibility

Label everything

Require team and environment labels so every dollar has an owner.

Time

Switch off the idle

Schedule non-production to shut down outside working hours.

Review

Meet monthly

Review top spenders, gaps, and orphaned resources, and track actions to completion.

The cheapest capacity is the capacity you never needed to rent, and the second cheapest is the capacity you rented with a plan for losing it.

A starter sequence for a new team

1

Week one: see

Install a cost allocation tool, turn on labels, and find the top ten spenders and the biggest requested-versus-used gaps.

2

Weeks two and three: clean

Remove unused resources and schedule non-production shutdowns.

3

Weeks four to eight: right-size

Lower requests gradually for the largest services, watching health signals.

4

Weeks nine to twelve: pack and spot

Enable consolidation, widen the instance list, and move flexible workloads to spot after rehearsal.

5

Quarter two: commit and govern

Commit to a share of the stable floor and add quotas, policy, and a review rhythm.

25Anti-Patterns and Decision Records

An anti-pattern is a habit that looks sensible but leads to trouble. Each one below is written as a short decision record so you can see why people choose it and what to do instead.

ANTI-PATTERN 01 · CUTTING REQUESTS WITHOUT MEASURINGAvoid
Why teams do it

A bill alarm leads to a mandate to lower all requests by a fixed percentage.

What goes wrong

Services that really needed their memory are killed, and incidents follow. Trust in cost work collapses.

Better choice

Use measured high-percentile usage plus a margin, and change in small steps with monitoring.

ANTI-PATTERN 02 · ALL ON SPOT, ONE POOLAvoid
Why teams do it

The cheapest single instance type looks like the best deal.

What goes wrong

One pool event removes the entire fleet, and replacement may be unavailable.

Better choice

Diversify across types and zones, keep an on-demand floor, and allow automatic fallback.

ANTI-PATTERN 03 · COMMITTING BEFORE OPTIMIZINGAvoid
Why teams do it

Commitment discounts are visible, fast, and impressive in a report.

What goes wrong

Later optimization reduces usage below the commitment, so the team pays for capacity it no longer needs.

Better choice

Right-size and add spot first, then commit to a conservative share of the measured stable floor.

ANTI-PATTERN 04 · ZERO-DISRUPTION BUDGETS EVERYWHEREAvoid
Why teams do it

A budget that allows no interruption feels like the safest setting for every service.

What goes wrong

Nodes can never be drained, so consolidation, upgrades, and spot handling are blocked, and the cluster stays large.

Better choice

Allow a sensible number of replicas to be unavailable, and keep strict settings for truly critical services only.

ANTI-PATTERN 05 · SPREADING EVERY WORKLOAD STRICTLYAvoid
Why teams do it

Strict anti-affinity and spread rules are copied from a critical service into every template.

What goes wrong

Pods cannot share nodes, packing collapses, and the cluster fills with half-empty machines.

Better choice

Use soft preferences by default, and strict rules only where a failure would truly hurt.

ANTI-PATTERN 06 · OPTIMIZING WITHOUT OWNERSAvoid
Why teams do it

A central team runs a one-time cleanup and considers the problem solved.

What goes wrong

Without labels, reports, and review, waste slowly returns, and nobody notices until the next bill shock.

Better choice

Assign ownership by label, show costs to the owners, and keep a regular review.

26Managed Cloud Options and Where They Differ

The ideas in this post are the same on every cloud, but the product names, defaults, and limits differ. This chapter gives a map, so you know what to look up for your own platform.

The same ideas under different names

IdeaAmazon (EKS)Google (GKE)Microsoft (AKS)
Discounted interruptible machinesSpot instancesSpot or preemptible virtual machinesSpot virtual machines
Node autoscalingCluster Autoscaler or Karpenter, plus managed node groupsCluster autoscaler and node auto-provisioning, plus an autopilot mode that bills by podCluster autoscaler and node auto-provisioning options
Commitment discountsSavings Plans and reserved instancesCommitted use discountsReservations and savings plans
Interruption noticeShort warning, commonly about two minutesShort warning, commonly around half a minuteShort warning, commonly around half a minute
!
Check the current details

Names, notice periods, product options, and billing rules change over time. The table above is a rough guide from general knowledge and may be out of date. Always confirm in your provider’s current documentation before designing around a specific number.

Two billing models

Most clusters bill by the node, so you pay for the machine whether or not pods fill it. This is why packing matters. Some managed modes bill by the pod request instead, so you pay for what your pods ask for, and the provider handles packing. In those modes, right-sizing requests becomes the single most important lever, because requests are directly your bill. Packing is still important, but it is the provider’s problem and not yours.

Everyday analogy

It is the difference between renting a whole van and paying per parcel. When you rent the van, you must fill it well. When you pay per parcel, the courier fills vans, but you pay for each parcel’s declared size, so declaring a size too large costs you directly.

Production example: choosing between modes

Teams with large, predictable workloads and strong platform skills often prefer node-based billing with their own autoscaling and spot strategy, to control cost tightly. Smaller teams, or those with variable workloads and limited platform capacity, often prefer pod-based modes for simplicity, accepting a slightly higher unit price in return for less operational work.

27A Decision Guide: What to Do First

With so many levers, where should you begin? This guide helps you pick the next step based on what you see in your own cluster.

flowchart TD
  START["Cluster bill is too high"] --> Q1{"Are there unused or forgotten resources?"}
  Q1 -->|"yes"| A1["Delete them and schedule non-production shutdowns"]
  Q1 -->|"no"| Q2{"Is requested capacity far above real usage?"}
  Q2 -->|"yes"| A2["Right-size requests with measured data"]
  Q2 -->|"no"| Q3{"Are many nodes half full?"}
  Q3 -->|"yes"| A3["Improve packing: consolidation, wider instance list, fewer pod sizes"]
  Q3 -->|"no"| Q4{"Is most work tolerant of interruption?"}
  Q4 -->|"yes"| A4["Move flexible work to diversified spot with a floor"]
  Q4 -->|"no"| Q5{"Is a stable always-on floor measured?"}
  Q5 -->|"yes"| A5["Commit to a conservative share of the floor"]
  Q5 -->|"no"| A6["Improve visibility with labels and allocation reports"]
    
Figure 16: A simple order of attack for lowering a Kubernetes bill

Comparing the main levers

LeverTypical effortTypical savingRiskDo it
Delete waste, schedule shutdownsLowSmall to mediumVery lowFirst
Right-size requestsMediumMedium to largeLow to mediumSecond
Better packing and consolidationMediumMediumLow to mediumThird
Spot for flexible workMedium to highLargeMediumFourth
Commitments on the floorLowMediumLow if conservativeFifth
Network and storage tuningMediumVariesLowAlongside the others
i
Savings you keep are the savings that last

The best result is not the biggest cut in the first month. It is a system where waste does not creep back: honest requests, automatic consolidation, labeled ownership, and a regular review.

28Frequently Asked Questions

These are the questions people ask most often when they begin to work on Kubernetes cost.

Q1What is bin packing in Kubernetes?
It is the practice of fitting pods onto as few nodes as possible, based on their requested CPU and memory, so you rent fewer machines. The scheduler does it at placement time, and consolidation tools repair it as pods come and go.
Q2Why does the scheduler use requests instead of real usage?
Requests are stable promises, so placement is predictable. Real usage changes constantly, and packing by it could overcrowd nodes when load rises. The consequence is that inflated requests directly inflate your node count.
Q3Should I set CPU limits?
Opinions differ. Very tight CPU limits can slow applications even when the node has spare capacity. Many teams set CPU requests carefully and use generous or no CPU limits, while keeping memory limits. Decide with measurements for your own workloads.
Q4What is the difference between Cluster Autoscaler and Karpenter?
Cluster Autoscaler resizes pre-defined node groups. Karpenter and similar tools look at pending pods and launch nodes of suitable types directly, and can consolidate nodes. The second family usually offers more flexibility and packing, while the first is mature and widely supported.
Q5How big is the spot discount?
It is commonly large, often more than half off the standard price, but it varies by cloud, region, instance type, and time. Treat any single number with care and check current prices.
Q6How much warning do I get before a spot node is taken?
Usually a short warning, from around half a minute to about two minutes depending on the provider. Design shutdown to complete well within that time, and confirm the figure in your provider’s documentation.
Q7Can databases run on spot?
Usually not for the primary copy, because an interruption can cause long recovery or data risk. Some replicas or read-only copies can tolerate it, but only with careful design. Most teams keep stateful stores on on-demand capacity.
Q8What is a pod disruption budget?
It is a rule that says how many replicas of a service may be unavailable during voluntary disruptions such as drains and consolidation. It protects availability during planned moves, but it cannot stop an abrupt failure.
Q9Why will my nodes not scale down?
Usually a pod or rule blocks removal: local storage, pods without owners, strict disruption budgets, do-not-evict markers, or tight anti-affinity. The autoscaler’s events usually name the blocker.
Q10Should I buy commitments or use spot?
Both, for different parts of the load. Commit to the stable always-on floor, use spot for flexible and interruption-tolerant work, and use on-demand for the rest. Right-size before you commit.
Q11Will tighter packing hurt performance?
It can, if nodes are crowded and neighbors compete for resources, or if there is no headroom for spikes. Aim for high utilization with deliberate headroom, and watch latency and throttling as you tighten.
Q12What is the first thing I should do?
Make cost visible. Add labels, install an allocation tool, and look for forgotten resources and the largest gaps between requested and used capacity. Those findings tell you which lever to pull next.

29Summary and Key Takeaways

We began on a moving day with a hundred boxes and a fleet of rented trucks. Kubernetes cost work is the same game: fewer trucks, cheaper trucks, and a plan for the ones taken back.

What to remember

  • You pay for nodes, and requests decide how many nodes you need. Inflated requests create slack, which is reserved capacity that sits idle.
  • Make requests honest first. Use a high percentile of measured usage plus a margin, and change gradually while watching health signals.
  • Bin packing means fewer, fuller nodes. The default scheduler favors spreading, so combine standard pod sizes, wide instance lists, and consolidation to get density, and keep headroom on purpose.
  • Spot is the biggest discount and needs a plan for loss. Diversify across instance types and zones, keep an on-demand floor, set disruption budgets, and rehearse interruptions.
  • Buy in layers. Commit to the stable floor last, use spot for flexible work, and use on-demand as the buffer.
  • Remember storage, network, and idle time. Orphaned disks, cross-zone traffic, and always-on environments quietly drain budgets.
  • Make cost visible and owned. Labels, allocation reports, quotas, and a regular review keep the savings from slipping away.