Cluster Autoscaler in Kubernetes
A plain-English, production-minded guide to how Kubernetes adds and removes servers on its own, why it sometimes refuses to, and how to run it without nasty surprises.
Picture a pizza shop on a quiet Tuesday afternoon. Two cooks, two ovens, plenty of space. Then a big cricket match ends, and within ten minutes two hundred orders pour in. If the owner has to phone a staffing agency, wait for new cooks to arrive, and only then switch on more ovens, half the customers will walk away hungry. On the other side, if the owner keeps twenty cooks standing around every quiet afternoon, the shop slowly goes broke.
A Kubernetes cluster lives with this exact problem every single day. Traffic jumps, a batch job starts, a marketing email goes out, and suddenly the servers have no room left. A few hours later the rush is over, half the servers sit idle, and the cloud bill keeps ticking anyway. The Cluster Autoscaler is the part of the Kubernetes ecosystem that handles this calmly and automatically. It adds servers when pods have nowhere to run, and it removes servers when they are no longer needed. This guide explains how it thinks, how it works inside, and how to run it in production.
1Why Clusters Must Grow and Shrink
Before learning any tool, it helps to feel the problem it solves.
Every application needs computers to run on. In Kubernetes, those computers are called nodes. If you buy too few nodes, your application slows down or stops working when many users arrive. If you buy too many, you pay for computers that do nothing. Cloud providers bill you for every running virtual machine, usually by the second, so an idle machine is simply money leaving your account.
Traffic is rarely flat. A food-delivery app is busy at lunch and dinner. A ticket-booking site explodes when a popular concert goes on sale. A payroll system works hard on the last day of the month. A fixed-size cluster has to be sized for the busiest moment, which means it is wasteful for the other twenty-three hours of the day. The smarter plan is a cluster that breathes: it grows when work arrives and shrinks when work leaves.
Three different things can scale
Kubernetes has more than one autoscaler, and beginners often mix them up. Each one answers a different question.
- How many copies of my app should run? The Horizontal Pod Autoscaler (HPA) answers this by adding or removing pods.
- How big should each copy be? The Vertical Pod Autoscaler (VPA) answers this by adjusting the CPU and memory each pod asks for.
- How many servers do I need to hold all those copies? The Cluster Autoscaler answers this by adding or removing nodes.
flowchart TB
A["Traffic grows"] --> B["HPA creates more pods"]
B --> C["Scheduler tries to place the pods"]
C --> D{"Is there room on existing nodes?"}
D -->|"Yes"| E["Pods start running"]
D -->|"No"| F["Pods wait in Pending state"]
F --> G["Cluster Autoscaler adds nodes"]
G --> E
Think of a hotel. The guests are pods and the rooms are nodes. A travel agency (the HPA) decides how many guests to send. When guests arrive and every room is taken, the hotel manager (the Cluster Autoscaler) opens another floor. When whole floors sit empty for a long time, the manager closes them to save electricity. The manager never decides how many guests come. The manager only makes sure there are enough rooms.
The Cluster Autoscaler does not watch your website traffic or your CPU graphs. It watches one thing above all: pods that cannot find a place to run.
2Core Concepts From Zero
A handful of words carry almost the whole story. Learn them once and everything else becomes easy.
Node
One computer, real or virtual, that runs your containers. In the cloud it is usually a virtual machine such as an EC2 instance.
Pod
The smallest unit Kubernetes runs. It wraps one or more containers that always live together on the same node.
Resource Request
The amount of CPU and memory a pod promises it needs. Kubernetes reserves this amount on a node for the pod.
Resource Limit
The maximum a pod is allowed to use. Going over the memory limit gets the container killed.
Scheduler
The Kubernetes component that picks which node each new pod will run on, based on requests and rules.
Pending Pod
A pod that has been created but not placed on any node yet. If the scheduler says no node fits, it is called unschedulable.
Allocatable
The part of a node that pods can actually use. Some CPU and memory is always kept back for the operating system and Kubernetes itself.
Node Group
A set of identical nodes managed together, such as an AWS Auto Scaling group. The autoscaler changes the size of these groups.
Requests are the currency of autoscaling
This is the single most important idea in the whole article. The scheduler does not look at how busy a node really is. It looks at the requests written in pod definitions and adds them up. If a node has 4 CPUs available and four pods each request 1 CPU, the node is full in the eyes of the scheduler, even if those pods are actually sitting idle and using almost nothing.
The Cluster Autoscaler uses the very same arithmetic. It makes its decisions from requests, not from live usage. That is why a cluster can look half empty on a monitoring dashboard and still be unable to take one more pod. It is also why pods without any requests confuse the autoscaler badly, a problem we return to later.
A cinema sells seats, not bodies. If you reserve three seats for your family but only two people come, the third seat stays empty, yet nobody else can sit there. Requests are seat reservations. The scheduler counts reservations, not who actually sat down.
Rules that narrow the choices
Pods can carry extra rules. A node selector says “only run me on nodes with this label”. A taint on a node says “keep out unless you carry my matching toleration”. Affinity and topology spread rules say things like “keep my replicas in different zones”. These rules matter a lot, because the autoscaler can only add a node that would truly accept the waiting pod. A node of the wrong kind, however large, does not help.
High CPU usage on a node does not trigger scale-up, and low usage does not by itself trigger scale-down. Requests and pending pods drive everything.
3What Cluster Autoscaler Is, and Is Not
A short history, a clear definition, and a list of common misunderstandings.
The Cluster Autoscaler, often shortened to CA, is an open-source program that lives in the Kubernetes autoscaler repository and is maintained by the Kubernetes SIG Autoscaling community. It has been part of the ecosystem since the mid-2010s, which makes it one of the most battle-tested pieces of the platform. It runs as an ordinary pod inside your cluster, talks to the Kubernetes API on one side and to your cloud provider on the other, and changes the number of nodes in your node groups.
Its job can be said in two sentences. If some pods cannot be scheduled because no node has enough free room, and a new node from one of your groups would fix that, add the node. If some nodes have been mostly unused for a while and their pods could happily live elsewhere, remove those nodes.
How it differs from its neighbours
| Tool | What it changes | What triggers it | Speed |
|---|---|---|---|
| Horizontal Pod Autoscaler | Number of pod copies | Metrics such as CPU or custom values | Seconds to a minute |
| Vertical Pod Autoscaler | CPU and memory requests of pods | Observed usage over time | Slow, often needs pod restarts |
| Cluster Autoscaler | Number of nodes in existing node groups | Pending pods and underused nodes | Minutes, limited by VM boot time |
| Karpenter | Nodes launched directly, any fitting type | Pending pods and consolidation checks | Often faster, avoids group indirection |
Three popular misunderstandings
- “It scales my pods.” No. It never changes how many pods a Deployment has. That is the HPA’s job.
- “It reacts to CPU spikes.” No. It reacts to pods that cannot be scheduled, which is a scheduling event, not a usage event.
- “It makes my nodes bigger.” No. It only adds or removes whole nodes using templates you already defined in node groups.
Managed offerings hide some of this work. Google Kubernetes Engine has autoscaling built into its managed control plane. On Amazon EKS and Azure AKS you can either run the open-source autoscaler yourself or use the provider’s newer alternatives.
4Architecture and Components
One small program, several clear responsibilities.
The autoscaler is a single binary running one main loop. It does not need a database. It reads the world from the Kubernetes API, makes decisions in memory, and writes its orders to the cloud provider. Here is the big picture.
flowchart LR
U["Users and CI systems"] --> API["Kubernetes API server"]
API --> SCH["kube-scheduler"]
SCH -->|"cannot place pod"| PEND["Pending pods"]
CA["Cluster Autoscaler"] -->|"reads pods and nodes"| API
PEND --> CA
CA -->|"asks to change size"| CP["Cloud provider API"]
CP --> NG1["Node group A"]
CP --> NG2["Node group B"]
NG1 --> N1["New nodes"]
N1 -->|"register and become Ready"| API
CA -->|"writes health and status"| CM["Status ConfigMap in kube-system"]
The moving parts inside
Main Loop
Wakes up every ten seconds by default, refreshes its view of the cluster, and decides whether to scale up, scale down, or do nothing.
Cluster State Registry
Remembers which nodes are healthy, which groups are growing, and which groups recently failed so they can be avoided for a while.
Cloud Provider Interface
A plug-in layer with one implementation per cloud. It knows how to read group sizes and change them.
Scheduling Simulator
Reuses the real scheduler’s logic to ask a pretend question: if this node existed, would this pod fit on it?
Estimator
Works out how many new nodes are needed by packing the waiting pods into imaginary nodes, like fitting boxes into trucks.
Expander
The tie-breaker. When several node groups could help, it picks one using a chosen strategy.
Scale-Down Planner
Tracks which nodes look unneeded, for how long, and whether their pods can move safely.
Actuator
Carries out the plan: taints nodes, evicts pods, and finally tells the cloud to delete the machine.
Where it runs
On a self-managed cluster or on Amazon EKS, the autoscaler normally runs as a Deployment in the kube-system namespace on one of your worker nodes. You may run more than one replica, but only one is active at a time because they elect a leader. On Google Kubernetes Engine, the autoscaler is part of the managed control plane, so you configure it but never see its pod.
Imagine a building manager with a clipboard. Every ten seconds the manager walks the halls, counts who is waiting outside without a room, checks which rooms are nearly empty, and phones the cleaning agency to open or close floors. The scheduling simulator is the manager’s floor plan, used to test “would these guests fit if I opened that wing?” before spending any money.
5How Scale-Up Works Internally
From a pod stuck in Pending to a fresh node, step by step.
Scale-up begins when the scheduler gives up on a pod. The scheduler writes a condition on the pod saying it is unschedulable, and that is the signal the autoscaler waits for. On its next scan it collects all such pods. Pods that are brand new may be ignored for a moment so the scheduler gets a fair chance first.
flowchart TD
A["Scan every 10 seconds"] --> B{"Any unschedulable pods?"}
B -->|"No"| C["Check scale-down instead"]
B -->|"Yes"| D["Build a template node for each group"]
D --> E["Simulate: would the pending pods fit?"]
E --> F{"Does any group work?"}
F -->|"No"| G["Record a NotTriggerScaleUp event"]
F -->|"Yes"| H["Expander picks one group"]
H --> I["Increase the group size"]
I --> J["Wait for nodes to register"]
J --> K["Scheduler places the pending pods"]
Collect the stuck pods
The autoscaler lists pods that the scheduler marked unschedulable and filters out ones that could never be fixed by adding nodes.
Imagine a new node from each group
For every node group it builds a template: the CPU, memory, labels and taints a new node of that group would have.
Test the fit
Using the scheduler’s own rules, it checks which waiting pods would fit on each template node.
Count the nodes needed
The estimator packs pods into imaginary nodes to find how many are required, never going past the group’s maximum size.
Choose a winner
If several groups qualify, the expander strategy decides which one to grow.
Order the nodes
The cloud provider layer raises the desired size of the chosen group. The cloud then boots the machines.
Template nodes, in simple words
The autoscaler cannot ask a node that does not exist yet how big it will be, so it guesses from a template. If the group already has a running node, that node is copied as the model. If the group is empty, the autoscaler asks the cloud provider or reads tags on the group. System pods that run on every node, called DaemonSets, are subtracted from the template, because they will take their share of each new machine before your pods get any.
When the cloud says no
Sometimes a group is asked to grow but nodes never appear: the cloud has no capacity of that machine type, a quota is exhausted, or the machine boots but never joins the cluster. The autoscaler waits up to a limit, fifteen minutes by default, then gives up on that attempt and places the group in a temporary backoff. During backoff it tries other groups instead. Repeated failures lengthen the backoff, so a broken group is not hammered over and over.
You need a taxi big enough for six friends. You phone several taxi companies and ask whether their vans fit six people with luggage. Only some can. You pick one, and if the van never turns up, you wait a while, then call a different company and stop calling the first for a bit. That is template matching, expansion and backoff in one story.
If a single pod asks for more CPU or memory than the largest node type in any group, no amount of waiting will help. The pod will stay Pending forever and the autoscaler will log that no group can fit it.
6How Scale-Down Works Internally
Removing a server is much more delicate than adding one, so the autoscaler is deliberately cautious.
Adding a node is almost risk-free. Removing a node can hurt running users if done carelessly. For that reason scale-down has many safety checks and long waiting times. A node is removed only when every check says it is safe.
flowchart TD
A["Compute utilization of every node"] --> B{"Below the threshold, 50 percent by default?"}
B -->|"No"| Z["Keep the node"]
B -->|"Yes"| C["Mark the node as unneeded"]
C --> D{"Unneeded for 10 minutes?"}
D -->|"No"| C
D -->|"Yes"| E{"Can all its pods fit elsewhere?"}
E -->|"No"| Z
E -->|"Yes"| F["Taint the node and evict its pods"]
F --> G["Delete the machine from the node group"]
How utilization is measured
For each node the autoscaler adds up the requests of all pods on it and divides by the node’s allocatable amount, once for CPU and once for memory. The larger of the two ratios is the node’s utilization. Take a node with 4 CPUs allocatable where pods request 1.2 CPUs in total: that is 30 percent. If memory is at 45 percent, the node’s utilization is 45 percent. Both are under the default threshold of 50 percent, so the node becomes a candidate.
Safety checks before removal
Being under the threshold is only the first gate. The autoscaler then simulates moving every pod to other nodes. The node is spared if any of these is true:
- A pod is not owned by a controller such as a Deployment, so nothing would recreate it after eviction.
- A pod uses local storage that would be lost, unless it is marked as safe to evict.
- A pod carries the annotation
cluster-autoscaler.kubernetes.io/safe-to-evictset to false. - Evicting a pod would break a PodDisruptionBudget, a promise that a minimum number of replicas stay up.
- A pod has strict placement rules and no other node in the cluster would accept it.
- The node would drop the group below its minimum size, or the node is annotated to disable scale-down.
The graceful goodbye
When a node passes all checks, the autoscaler first adds a taint so that no new pods land on it. Then it asks Kubernetes to evict each pod politely through the Eviction API, which respects disruption budgets and the pod’s shutdown grace period. Only when the node is empty does it tell the cloud provider to delete the machine and shrink the group.
A supermarket has ten checkout lanes. Closing one lane is sensible only if its queue has been short for a good while and its customers can easily join other lanes. The manager would never close a lane while someone is halfway through paying. The waiting time, the “can they move?” check and the polite hand-over are all part of the same caution.
After a scale-up, the autoscaler also waits ten minutes by default before it considers any scale-down. This stops the cluster from adding a node and deleting it again in a nervous loop.
7Data Flow and Lifecycle of a Node
Follow one burst of traffic from the first new pod to the last deleted machine.
Now let us put the pieces together. Here is the life of a node, told as a conversation between the components.
sequenceDiagram
participant D as Deployment
participant S as Scheduler
participant C as Cluster Autoscaler
participant P as Cloud Provider
participant K as New Node
D->>S: Create 8 new pods
S->>S: Only 2 pods fit on existing nodes
S-->>C: 6 pods marked Unschedulable
C->>C: Simulate and choose a node group
C->>P: Raise desired size by 2
P->>K: Boot two virtual machines
K->>S: Nodes join and become Ready
S->>K: Place the pending pods
Note over C,K: Later, when load drops, scale-down reverses this
A worked example with real numbers
Suppose a cluster has three nodes, each with 4 CPUs available to pods, so 12 CPUs in total. Existing pods request 10 CPUs, leaving 2 CPUs free. Each pod of a web service requests 1 CPU. A flash sale begins, and the HPA raises the web service by 8 pods.
- Two of the new pods fit into the free 2 CPUs. Six pods stay Pending.
- Each new node can hold four such pods, so six pods need two nodes (6 divided by 4 is 1.5, rounded up to 2).
- The autoscaler asks the node group to grow by two.
HPA creates the pods
Eight new pods appear. The scheduler places two and marks six unschedulable within a few seconds.
Autoscaler notices
On its next scan it sees six Pending pods, simulates, and orders two nodes.
Machines boot
The cloud starts the virtual machines, the operating system boots, and the kubelet registers with the cluster.
Pods start
The scheduler places the waiting pods on the new nodes. Container images are pulled and the pods become Ready.
Load falls, scale-down starts
The HPA removes pods. The two extra nodes drop under half utilization, stay unneeded for ten minutes, get drained, and are deleted.
The exact times depend on your cloud, instance type and image size, so treat them as rough guides. The important lesson is that a new node is never instant. From the first Pending pod to a running pod often takes several minutes, and that delay shapes many design choices later in this guide.
The autoscaler keeps a status record in a ConfigMap named cluster-autoscaler-status inside kube-system. It lists the health of the cluster and the scale-up and scale-down state of each node group, which makes it the first place to look when behaviour seems odd.
8Expanders and Tuning Knobs
Two sets of dials control almost all of the autoscaler’s personality.
Expanders: choosing which group to grow
When more than one node group could host the waiting pods, the expander breaks the tie. You pick the strategy with a command-line flag. You can even chain several, for example priority first and least-waste as a tie-breaker.
| Expander | How it chooses | Good for |
|---|---|---|
| random | Picks any qualifying group (the default) | Simple clusters with similar groups |
| most-pods | Picks the group that lets the most pending pods be scheduled | Mixed workloads with node selectors |
| least-waste | Picks the group leaving the least idle CPU and memory after scaling | Cost-conscious clusters with different sizes |
| priority | Follows a ranked list you write in a ConfigMap | Preferring cheap spot groups before on-demand ones |
| price | Picks the cheapest option using provider pricing | Only available with some providers |
| grpc | Asks an external service of your own to decide | Custom business rules |
The settings that matter most
The defaults below come from the open-source project. Managed services may change them, so always check your provider’s documentation.
| Setting | Default | What it controls |
|---|---|---|
scan-interval | 10 seconds | How often the main loop runs |
scale-down-utilization-threshold | 0.5 | Utilization below which a node is a removal candidate |
scale-down-unneeded-time | 10 minutes | How long a node must stay unneeded before removal |
scale-down-delay-after-add | 10 minutes | Pause on scale-down after any scale-up |
scale-down-delay-after-failure | 3 minutes | Pause after a failed scale-down |
max-node-provision-time | 15 minutes | How long to wait for a new node before giving up |
max-graceful-termination-sec | 600 seconds | Longest wait for pods to finish during a drain |
balance-similar-node-groups | false | Keep similar groups, such as one per zone, evenly sized |
expander | random | Strategy for choosing a group to grow |
Each node group also has its own minimum and maximum size. These two numbers are your safety rails: the minimum guarantees a baseline of capacity, and the maximum caps the bill. You can also set cluster-wide limits on total cores, total memory and total node count.
Think of a thermostat. The scan interval is how often it checks the room. The thresholds and waiting times are how patient it is before it switches the heater on or off. Set it too twitchy and the heater clicks on and off all day. Set it too lazy and the room freezes before anything happens.
Change one setting at a time and watch the cluster for a few days. Autoscaler behaviour is a conversation between your workloads, your cloud and these dials, so small changes can have surprising effects.
9Working With HPA, VPA and Spare Capacity
The autoscalers are teammates. Used together they are strong, used carelessly they fight.
HPA and Cluster Autoscaler
This is the classic pair. The HPA reacts to load by creating pods. When those pods cannot be placed, the Cluster Autoscaler provides nodes. Both are needed: HPA alone runs out of room, and Cluster Autoscaler alone has nothing to react to. The HPA relies on a metrics source such as the Kubernetes Metrics Server to read usage. Event-driven tools like KEDA can feed the HPA from queues and other signals, which suits background workers.
VPA and Cluster Autoscaler
The VPA changes the requests of pods, usually by restarting them. Better-sized requests give the autoscaler a more truthful picture, which is a real benefit. The warning is to avoid letting VPA and HPA act on the same CPU or memory signal for the same workload, because they will pull in opposite directions.
The waiting problem and the headroom trick
Remember that a new node takes minutes to arrive. During a sudden spike, users may wait that long. A popular fix is overprovisioning, also called headroom. You run a small Deployment of dummy “pause” pods that do nothing but reserve CPU and memory. These pods get a very low priority, lower than every real workload.
flowchart LR
PP["Low priority pause pods hold spare room"] --> NEW["A real pod arrives"]
NEW -->|"preempts a pause pod"| RUN["Real pod starts in seconds"]
NEW --> EV["Evicted pause pod becomes Pending"]
EV --> CA["Cluster Autoscaler adds a node in the background"]
CA --> PP
When a real pod arrives and the cluster is otherwise full, Kubernetes evicts a pause pod instantly to make space. The real pod starts in seconds. The evicted pause pod becomes Pending, which triggers the autoscaler to add a node, quietly rebuilding the spare room. You pay for a little unused capacity and receive fast reaction in return.
The autoscaler ignores pods whose priority is below a cutoff, which is minus ten by default. Give your pause pods a priority such as minus one, not minus one thousand, or they will never cause nodes to be added.
A restaurant keeps a few tables with a “reserved” card on them. When a real party walks in, the card is removed and they sit down at once, while a waiter quietly sets up a new reserved table in the back room. The customers never notice the work.
10Cloud Provider Behavior
The idea is the same everywhere. The plumbing underneath differs.
| Cloud | Node group is called | Notes |
|---|---|---|
| AWS (EKS) | Auto Scaling group, often via managed node groups | Groups are found by tags or by name; you run the autoscaler yourself |
| Google Cloud (GKE) | Managed instance group inside a node pool | Built in; offers profiles such as balanced and optimize-utilization |
| Microsoft Azure (AKS) | Virtual machine scale set inside a node pool | Enabled per node pool with min and max counts |
Amazon Web Services details
On AWS, the autoscaler can discover groups automatically by looking for two tags on each Auto Scaling group: one marking it as enabled and one carrying your cluster name. Alternatively you list groups by name with their minimum and maximum sizes. Several lessons come up again and again.
- Do not fight it. Do not attach the Auto Scaling group’s own scaling policies, and do not change desired capacity by hand, while the Cluster Autoscaler is in charge. Two controllers managing one number leads to chaos.
- Match the version. Use an autoscaler release whose minor version matches your Kubernetes minor version, because it embeds scheduler logic from that release.
- Similar instance types. If one group mixes instance types, choose types with the same CPU and memory. The autoscaler builds its template from one type, so a wildly different type makes its predictions wrong.
- Zones and storage. Disks such as EBS volumes live in a single zone. If pods use them, create one node group per zone and enable balancing between similar groups, so a pod needing a zone can always get a node there.
- Scaling from zero. A group may rest at zero nodes. To do this, the autoscaler needs to know what a node would look like, so you add tags for labels and taints on the group.
Google Cloud and Azure in short
On GKE you simply switch autoscaling on for a node pool and set its limits. The service hides the controller and offers profiles that trade speed against cost: one favours availability, the other removes underused nodes more eagerly. GKE Autopilot goes one step further and removes node management from you altogether. On AKS you enable the autoscaler per node pool with minimum and maximum counts, and a profile object holds the fine-tuning settings.
Quotas matter. If your account is limited to a certain number of machines of a type, the autoscaler can ask for more and the cloud will simply refuse. Always check quotas before a launch event, not during it.
11Advantages, Disadvantages and Trade-offs
No tool is perfect. Knowing the costs helps you choose wisely.
Advantages
- Cuts waste by removing idle nodes automatically.
- Prevents Pending pods caused by lack of room.
- Mature, widely used and well documented.
- Works across many clouds with the same concepts.
- Respects pod disruption budgets and scheduling rules.
- Supports scale-from-zero for batch and GPU pools.
Disadvantages
- Reactive, so users may wait minutes during sudden spikes.
- Depends on node groups you must design and maintain.
- Bad requests produce bad decisions.
- Scale-down is slow and often blocked by small details.
- Cannot choose a brand new instance type on its own.
- Version matching and tuning need ongoing care.
Cluster Autoscaler or Karpenter?
Karpenter is a newer approach, reached its 1.0 release in 2024, and is now developed within the Kubernetes SIG Autoscaling community with a mature AWS implementation. Instead of growing fixed node groups, it looks at the waiting pods and launches machines of whatever suitable type fits them best, then regularly consolidates workloads onto fewer nodes. Neither is simply better.
| Question | Cluster Autoscaler | Karpenter |
|---|---|---|
| How are nodes organised? | Pre-defined node groups | Rules describing allowed machine shapes |
| Instance choice | Fixed per group | Picked per batch of pods |
| Clouds supported | Many providers | Strongest on AWS, growing elsewhere |
| Operational style | Familiar, predictable, simple to reason about | Flexible, more automatic, newer concepts to learn |
| Best fit | Stable fleets, multi-cloud teams, teams wanting well-known behaviour | Diverse or spiky workloads that benefit from fast, right-sized nodes |
Do not migrate just because something is newer. If your current autoscaling is quiet, cheap and boring, that is a good sign. Consider a change when node-group sprawl or slow reaction is genuinely hurting you.
12Security
The autoscaler can create and destroy servers. That is real power, so treat it carefully.
If an attacker or a bug controlled the autoscaler, they could launch hundreds of machines and run up a large bill, or delete nodes and cause an outage. Security here is mostly about giving it only the permissions it needs and keeping it healthy.
Cloud permissions
The autoscaler needs permission to read group information and to change group sizes or terminate instances inside those groups. Grant only those actions. Where the cloud supports it, restrict the write actions to groups carrying your cluster’s tag, so that a mistake in one cluster cannot touch another. Use short-lived workload identity, such as IAM roles for service accounts on AWS or workload identity on Google Cloud, rather than long-lived keys stored in a secret.
Kubernetes permissions
Inside the cluster, the autoscaler uses a service account with a role that allows it to read pods, nodes and related objects, evict pods, and update its own status ConfigMap and leader-election lease. Review the standard manifest and remove anything you do not use. Run it in kube-system with a high priority class so it is never the first pod to be pushed out under pressure.
Protecting the bill and the fleet
- Always set maximums. A node group maximum and a cluster-wide node limit are your cost firewall.
- Keep it off nodes it manages. Run the autoscaler on a small, stable node group it does not shrink, so it cannot delete the machine it lives on.
- Harden nodes the usual way. Patched images, restricted network rules and minimal permissions on the node role still apply to every machine it creates.
- Watch for abuse. A user who can create unlimited large pods can cause large scale-ups. Use namespace resource quotas and limit ranges to put a ceiling on what any team can request.
Give the building manager a company credit card with a spending limit and a list of approved suppliers, not an unlimited card. If the card is ever lost, the damage stays small.
Resource quotas are not only about fairness. In an autoscaled cluster they are also a safety net against runaway cost.
13Monitoring, Logging and Metrics
You cannot trust what you cannot see. The autoscaler gives you three windows into its mind.
Window one: events
Kubernetes events are short notes attached to pods and nodes. The autoscaler writes a note named TriggeredScaleUp on pods that caused a scale-up, and NotTriggerScaleUp on pods it could not help, with a reason such as “no node group fits” or “max size reached”. It also records scale-down events on nodes. Reading the events of a Pending pod is usually the fastest way to learn why it is stuck.
Window two: logs and the status ConfigMap
The autoscaler logs each loop’s key decisions. Raising log verbosity shows the simulation details and which blockers kept a node alive, but it is noisy, so use it for investigations rather than all the time. The status ConfigMap in kube-system shows cluster health, per-group sizes, and whether a group is in backoff.
Window three: metrics
The autoscaler exposes Prometheus metrics, by default on port 8085. These are the ones worth putting on a dashboard.
| Metric | What it tells you |
|---|---|
cluster_autoscaler_unschedulable_pods_count | How many pods are waiting right now |
cluster_autoscaler_nodes_count | Node totals by state, such as ready or unready |
cluster_autoscaler_scaled_up_nodes_total | How many nodes were added over time |
cluster_autoscaler_scaled_down_nodes_total | How many nodes were removed, with reasons |
cluster_autoscaler_failed_scale_ups_total | Scale-ups that never produced nodes |
cluster_autoscaler_function_duration_seconds | How long each part of the loop takes |
cluster_autoscaler_errors_total | Errors by type |
cluster_autoscaler_last_activity | When each step last ran, useful for spotting a stuck loop |
What to alert on
- Pending pods stay above zero for longer than your tolerance, for example fifteen minutes.
- Failed scale-ups keep climbing, which hints at quota or capacity trouble.
- The last-activity time stops advancing, meaning the loop may be hung or unhealthy.
- Node count nears the cluster maximum, so you are warned before the ceiling blocks growth.
- Average requested-to-allocatable ratio stays very low for days, hinting at inflated requests or a wasteful minimum size.
Events are the manager’s sticky notes on each door, logs are the daily diary, and metrics are the wall chart showing trends. When a guest complains, you read the sticky note. When you want to know whether this week was worse than last week, you read the chart.
14Design Patterns and Anti-Patterns
Habits that make autoscaling smooth, and habits that quietly break it.
Patterns that work
Workload-Shaped Node Groups
Keep separate groups for general apps, memory-heavy jobs, GPU work and system add-ons. Taints and selectors steer pods to the right group.
Spot First, On-Demand Fallback
Use the priority expander to try cheaper interruptible machines first and fall back to regular ones if they are unavailable.
One Group Per Zone
With zone-bound storage, create a group in each zone and enable balancing so capacity grows evenly and pods always find a home.
Headroom With Pause Pods
Reserve a thin layer of spare room with low-priority placeholder pods so spikes meet capacity that is already warm.
Scale-to-Zero Batch Pools
Let costly pools for nightly jobs or GPU training rest at zero nodes and wake only when work is queued.
Disruption Budgets Everywhere
Give every important workload a sensible PodDisruptionBudget so drains are safe, yet not so strict that they are blocked.
Anti-patterns to avoid
The habit
Deploying pods with no CPU or memory requests.
Why it hurts
The scheduler treats such pods as free, so it crams many onto one node. The autoscaler then sees nodes that look empty and tries to remove them, and never sees a need to add more. The result is crowded, unstable nodes.
Better approach
Require requests on every workload using a LimitRange or policy, and review them using real usage data.
The habit
Changing group sizes by hand, or attaching cloud scaling policies, while the autoscaler manages the same group.
Why it hurts
Two controllers fight over one number. Nodes appear and vanish unpredictably and the autoscaler’s view of the world becomes wrong.
Better approach
Change minimum and maximum sizes instead, and let the autoscaler own the actual count.
The habit
Setting a PodDisruptionBudget that allows zero disruptions, or running single-replica workloads that must never move.
Why it hurts
The node holding such a pod can never be drained. It stays forever, and your savings from scale-down shrink away.
Better approach
Run at least two replicas, allow at least one disruption, and spread them across nodes.
The habit
Inflating requests “to be safe”, for example asking for four CPUs when two are used.
Why it hurts
The cluster fills up on paper long before it fills up in reality. The autoscaler faithfully buys machines to match the inflated numbers, and you pay for the padding.
Better approach
Right-size requests from measured usage, perhaps with VPA recommendations, and keep a modest safety margin.
The habit
One enormous node group mixing machine types of very different sizes.
Why it hurts
The autoscaler builds its prediction from one type. If the cloud delivers a smaller one, pods still do not fit and the cycle repeats.
Better approach
Keep each group to instance types with matching CPU and memory.
15Best Practices and Common Mistakes
A checklist you can follow, then a troubleshooting path for the day something goes wrong.
Best practices
- Write honest requests. Everything else depends on them.
- Set sensible minimums and maximums. A minimum for a baseline, a maximum for cost safety.
- Match versions. Upgrade the autoscaler together with Kubernetes, one minor version at a time.
- Run it highly available. Two replicas with leader election and a critical priority class.
- Use graceful shutdown. Give pods realistic grace periods and a preStop hook so drains do not drop requests.
- Test scaling before you need it. Create a controlled load and watch pods, nodes and timings end to end.
- Review quotas and limits quarterly. Cloud quotas, subnet IP space and node limits all cap how far you can grow.
Check that your subnets have enough free IP addresses. On some clouds every pod takes an IP, and a cluster can stop growing because the network ran out of addresses, not because of the autoscaler.
Common mistakes
- Expecting instant reaction and skipping headroom for traffic spikes.
- Leaving bare pods, or pods using local disk, on nodes you hoped would shrink.
- Forgetting that DaemonSets and system pods can block scale-down on small clusters.
- Setting a minimum size too high and then wondering why the bill never drops.
- Ignoring the events on Pending pods, which usually explain the problem outright.
Troubleshooting: pods are Pending and no node appears
Read the pod events
Look for NotTriggerScaleUp and its reason. It often names the exact blocker, such as a selector no group satisfies.
Compare request and node size
Confirm the pod fits on the largest machine type in any group. If not, add a bigger group or shrink the request.
Check group limits
See whether the matching group is already at its maximum, or the cluster hit its total node or core limit.
Inspect the status ConfigMap
A group in backoff, or a cluster marked unhealthy, will pause scale-up until it recovers.
Look at the cloud side
Check quotas, machine availability in the zone, launch template errors, and whether new machines joined the cluster at all.
Verify permissions
Errors in the autoscaler logs about access denied usually mean its cloud role is missing an action.
Troubleshooting: idle nodes never go away
Work in the opposite direction. Check the node’s utilization against the threshold, look for blocking pods (bare pods, local storage, strict budgets, the do-not-evict annotation), confirm the group is above its minimum, and make sure no recent scale-up is still inside its cooling-off delay. The autoscaler’s logs usually say which pod blocked the node.
If more than a set share of nodes in the cluster are unhealthy, the autoscaler deliberately pauses itself, because acting on a broken picture could make things worse. Fix the unhealthy nodes first.
16Real-World and Industry Examples
How large teams think about node autoscaling, plus a few typical scenarios.
Google Kubernetes Engine
Google ships the autoscaler as a built-in feature of its managed Kubernetes service. Customers enable it per node pool and choose between profiles that favour availability or aggressive cost saving. It is a clear example of the same ideas in this article, hidden behind a settings page.
Airbnb
Airbnb has published engineering writing on how it scales its Kubernetes clusters dynamically to match demand, describing the work of adapting node scaling to its own workload mix. The lesson for smaller teams is that real fleets usually need extra tuning on top of the defaults.
Zalando
The European fashion retailer Zalando has openly shared its approach to running a very large number of Kubernetes clusters on AWS, where node autoscaling is a core building block. Their public material shows the value of treating cluster setup and autoscaling configuration as repeatable, automated infrastructure.
Typical scenario: a retail sale
An online store expects a big sale at noon. The team adds headroom pods an hour before, raises the node group maximum, checks quotas, and lets the HPA and autoscaler handle the climb. After the sale they lower the headroom and let scale-down trim the extra nodes overnight.
Typical scenario: nightly data jobs
A data team runs heavy processing from midnight to four in the morning. A dedicated, tainted node group sits at zero during the day. When the jobs are submitted, pods go Pending, nodes appear, the work runs, and the group returns to zero. The company pays for those machines only while the jobs run.
Typical scenario: machine learning on GPUs
GPU machines are expensive. A GPU node group with scale-from-zero and a short unneeded time avoids paying for idle accelerators. Taints keep ordinary pods away, so the costly nodes serve only the training and inference pods that need them.
17Getting Resource Requests Right
Every autoscaling problem eventually leads back to one humble field in the pod definition.
We have said many times that the autoscaler works from requests. This chapter explains how to choose good ones, because a team that gets this right will see the rest of the system behave almost by itself. A team that gets it wrong will fight strange symptoms for months.
Too low, too high, and just right
If a request is too low, the scheduler believes the pod is tiny. It places many such pods on one node. When they all become busy at once, the node runs out of real CPU or memory. CPU shortage makes everything slow. Memory shortage is worse, because the kernel starts killing containers. The autoscaler never saw a problem, since on paper the node was never full.
If a request is too high, the opposite happens. The cluster looks full while the machines are actually quiet. The autoscaler buys more nodes to satisfy the paper demand, and the bill grows with no benefit to users. This waste is invisible unless you compare requested amounts with measured usage.
A healthy request sits a little above the typical busy usage of the pod, with room for normal spikes. Many teams aim for the request to cover the usual high point of the day, not the rarest peak of the year.
Booking a table at a restaurant works the same way. Tell them you are coming with two friends when you really bring ten, and the table is too small and everyone is squeezed. Book a banquet hall for two people, and the restaurant turns away paying guests while your hall sits empty. An honest booking helps the whole restaurant plan its evening.
Quality of Service classes
Kubernetes sorts pods into three classes based on how requests and limits are set. These classes decide who is protected and who is sacrificed when a node runs short of memory.
| Class | How you get it | What it means |
|---|---|---|
| Guaranteed | Requests equal limits for CPU and memory in every container | Most protected, last to be killed under pressure |
| Burstable | At least one request set, but not equal to limits | Middle ground, common for ordinary services |
| BestEffort | No requests and no limits at all | First to be killed, and invisible to the autoscaler’s maths |
For autoscaling, the key point is that BestEffort pods look free. They do not make a node look fuller, so they never contribute to scale-up and they quietly crowd nodes that the autoscaler believes are empty.
Guardrails you can put in place
- LimitRange. A namespace rule that gives default requests and limits to pods that forget to set them, and can also reject absurd values.
- ResourceQuota. A namespace rule that caps the total CPU, memory and pod count a team may request, protecting both fairness and the cloud bill.
- Admission policies. Cluster-wide checks that block deployments without requests at all.
- VPA in recommendation mode. The Vertical Pod Autoscaler can watch real usage and publish suggested requests without changing anything. Engineers then review and apply the suggestions on a schedule.
A worked example
A team runs 40 copies of a checkout service. Each pod requests 2 CPUs, yet measured usage shows a busy-hour average of 0.7 CPUs and a peak of 1.1. The team lowers the request to 1.25 CPUs, which still gives comfortable room above the peak. The total reserved CPU falls from 80 to 50. On nodes with 8 usable CPUs each, that is the difference between ten nodes and about six or seven. The autoscaler does the rest by removing the nodes that are no longer needed, and the saving arrives without a single change to the application code.
Review requests after every big release, not only once a year. Applications grow heavier over time, and last year’s numbers quietly stop being true.
Memory requests deserve extra care. CPU can be shared and slowed down, but memory cannot. A pod that exceeds its memory limit is stopped, so never trim memory requests as aggressively as CPU.
18Spot Instances and Interruptions
Cheap machines that the cloud may take back at short notice, and how to use them safely.
Cloud providers sell spare machines at a deep discount, often a large fraction below the regular price. On Amazon these are Spot Instances, and on other clouds they have similar names. The catch is that the provider can reclaim them when it needs the capacity back. On AWS the warning is usually about two minutes. For stateless, replicated workloads this trade is excellent, and the Cluster Autoscaler is a natural partner for it.
Which workloads suit spot machines
- Good fit: stateless web replicas with several copies, queue workers, batch processing that can retry, build and test jobs, and development environments.
- Poor fit: a single-replica database, long jobs that cannot save progress, and anything where a two-minute warning is not enough to finish cleanly.
The recommended shape
Most teams build two kinds of node groups: a spot group for flexible work and an on-demand group as the dependable base. The priority expander is then told to try the spot group first and fall back to on-demand when spot capacity is not available. Pods that must be safe carry a node selector or affinity that points only to on-demand nodes.
flowchart TD
A["Pending pods need nodes"] --> B["Priority expander reads its ranked list"]
B --> C{"Is the spot group able to grow?"}
C -->|"Yes"| D["Add spot nodes"]
C -->|"No or in backoff"| E["Add on-demand nodes"]
D --> F["Pods run at lower cost"]
E --> G["Pods run at normal cost"]
F --> H{"Provider reclaims a spot node?"}
H -->|"Yes"| I["Node drains and pods return to Pending"]
I --> A
Making spot capacity dependable
Spot reliability depends mostly on variety. If a group uses only one machine type, a shortage of that type hurts you completely. If it can use many types of similar size, the provider usually finds some capacity somewhere. On AWS, a mixed instances policy with several equal-sized types and a capacity-aware allocation strategy lowers the chance of interruption. Remember the earlier rule: the types in one group should have matching CPU and memory so the autoscaler’s template stays truthful.
It also helps to run a small helper that listens for the provider’s interruption notice and starts a graceful drain at once, rather than waiting until the machine disappears. On AWS this job is done by the Node Termination Handler, and similar tools exist elsewhere.
Protect the service itself
- Keep at least two replicas of every service and spread them across nodes and zones, so one interruption cannot take everything down.
- Use PodDisruptionBudgets so that a burst of reclaimed nodes cannot remove too many replicas at once.
- Make shutdown fast and tidy, so the pod finishes its work inside the warning window.
- Keep a floor of on-demand capacity big enough to carry the minimum acceptable traffic.
Think of renting a car versus borrowing a neighbour’s spare. The neighbour’s car is almost free, but they may ask for it back at short notice. You would happily use it for grocery runs, yet you would not use it to drive to an important job interview. Keep a rented car on standby for the trips that cannot fail.
Large engineering teams commonly report that their cheapest and most flexible capacity comes from interruptible machines, as long as the software is built to expect them. The design effort is in the application, not in the autoscaler.
19Zones, Topology and Storage
Spreading across data centres makes a service sturdier, and also makes autoscaling a little more interesting.
A cloud region is split into several availability zones, which are separate buildings or groups of buildings with their own power and networking. Running copies of your application in more than one zone means a failure in one building does not stop the service. Kubernetes lets you ask for this with topology spread constraints. They say things such as “keep the number of replicas in each zone roughly equal”.
How spreading affects scale-up
Imagine a service that must keep an even number of pods in each of three zones, and the zone called A has no room left. Adding capacity in zone B does not satisfy the rule. The pod still waits, because the autoscaler must create a node in the right zone. This is why one node group per zone is so popular. The autoscaler can see exactly which group serves which zone and can grow the correct one.
When you do run one group per zone, turn on balancing between similar groups. The autoscaler then treats groups with matching machine shapes and labels as a family and grows them evenly, instead of filling one zone while the others stay small. A few labels naturally differ between zones, such as the zone name itself, so there is a setting that tells the autoscaler which labels to ignore when it decides that two groups are similar.
Spread Constraint
Keeps replicas evenly divided across zones or nodes. A strict version refuses to schedule when the spread would break, which can create Pending pods.
Anti-Affinity
Tells Kubernetes not to place two copies of the same app on one node. Useful, but each copy then needs its own node.
Node Affinity
Restricts pods to nodes with certain labels, such as a machine family or a zone.
Taints and Tolerations
Reserve special nodes for special pods, such as GPU machines for machine learning.
Storage is tied to a zone
Block disks on most clouds live inside one zone. A pod that uses such a disk can only run in that zone. If the pod is evicted and no node has room in that zone, it will stay Pending even though other zones have plenty of space. The autoscaler must add a node in the right zone, and this works well only when you have separate groups per zone.
A second helpful setting is the volume binding mode called wait for first consumer. With it, Kubernetes delays creating the disk until the pod has been placed, and then creates the disk in the same zone as the chosen node. This avoids the awkward situation where a disk is created in a zone first and the pod cannot find a node there.
Suppose a school wants equal numbers of children in each of three classrooms. When the first classroom is full, it is no help to add chairs in the second. The school needs a new chair in the first. Likewise, a child who keeps books in a locker in classroom one must sit in classroom one. Where something is stored decides where it can sit.
Strict rules have a cost
The more rules you add, the fewer places a pod can go, and the harder it is for the autoscaler to find a node that helps. Treat strict anti-affinity and strict spread rules as precious tools. For many services, a softer preference expresses the same wish without risking a stuck pod. A common compromise is to make zone spreading strict for the services that truly need it, and preferred for everything else.
If a zone loses a lot of capacity, strict spread rules can leave pods Pending in the healthy zones as well, because they would break the balance. Plan for this by allowing a reasonable skew, not a perfect one.
20Upgrades and Day-Two Operations
Launching autoscaling is the easy day. Keeping it healthy for years is the real work.
Engineers sometimes call the first deployment day one, and everything after it day two. Day two is where small habits decide whether the system is boring and reliable or a constant source of surprises.
Upgrading Kubernetes with the autoscaler in place
A cluster upgrade usually follows a clear order. The control plane moves first, then the worker nodes, then the add-ons. The Cluster Autoscaler is an add-on that must be moved to the matching version, because it embeds the scheduler’s logic for that Kubernetes release. A mismatch can cause strange mistakes in its simulations, such as believing a pod fits when the real scheduler disagrees.
- Read the release notes of both Kubernetes and the autoscaler for removed or changed settings.
- Test the new combination in a staging cluster that has similar node groups and similar workloads.
- Upgrade one minor version at a time, never skipping.
- Pause or watch scale-down during the rollout, so nodes are not removed while the cluster is mid-change.
Rotating node images
Nodes should be replaced regularly with fresh, patched machine images. When a node group gets a new image, old nodes are drained and replaced. During this process the autoscaler sees pods being evicted and Pending for a short time, and it may add temporary nodes. This is normal. Good disruption budgets and enough spare capacity make the rotation smooth. Some teams set a maximum node lifetime so that no machine lives forever and surprises never pile up.
A simple runbook
Before a big event
Check quotas, raise group maximums if needed, add headroom pods, and confirm alerts are on. Do this days in advance, not minutes.
During the event
Watch Pending pod counts, node counts and failed scale-ups. Know who can raise limits quickly.
After the event
Remove extra headroom, restore normal limits, and let scale-down trim nodes. Check that the bill falls as expected.
Every month
Review idle capacity, blocked scale-downs, and requests against real usage. Small trims add up.
Every quarter
Rehearse a failure: a zone loss, a quota hit, a stuck node group. Practice makes the real thing dull.
Changing settings safely
Treat the autoscaler’s configuration like application code. Keep it in version control, review changes, roll them out in staging first, and record why each value was chosen. When behaviour changes after an edit, you will be glad to know exactly what was edited and when. Tune one thing at a time and give each change a few days of normal traffic before judging it.
Startup delays and node readiness
A node is not useful the moment it appears. It needs to start the kubelet, join the cluster, run its networking and storage helpers, and often wait for system add-ons. Some teams apply a startup taint that keeps application pods away until every helper reports ready. The autoscaler has an option to ignore such known startup taints when it builds its template node, so it does not wrongly decide that the new node can never accept pods. Without this, you can see the autoscaler add more nodes than needed because it keeps thinking the earlier ones are unusable.
A new restaurant waiter on the first minute of the shift is not yet ready. They still need an apron, a notepad and the menu. A good manager does not send customers to them until they are ready, and also does not assume the waiter is missing just because they are not serving yet.
21Cost Thinking and Capacity Planning
Autoscaling saves money only if the pieces around it are chosen with cost in mind.
The Cluster Autoscaler keeps your node count close to what your pods need. It does not, by itself, decide whether those pods are efficient or whether the machines are the best value. Cost comes from several layers, and each layer has its own lever.
The layers of cost
| Layer | Typical lever | Who owns it |
|---|---|---|
| Application efficiency | Faster code, less memory, right-sized requests | Application teams |
| Pod count | HPA targets and minimum replicas | Application and platform teams |
| Node count | Cluster Autoscaler settings and blockers | Platform team |
| Machine choice | Instance family, size, spot versus on-demand | Platform team |
| Commitment | Discount plans for a steady baseline | Finance and platform teams |
Large nodes or small nodes?
This is one of the most debated design choices. Both extremes have a price.
Bigger nodes
- System add-ons on each node take a smaller share.
- Large pods fit more easily.
- Fewer machines to patch and watch.
- Less wasted space from fragmentation of leftover capacity, in some mixes.
Smaller nodes
- Finer steps when scaling up and down, so less over-buying.
- A single failure affects fewer pods.
- Easier to drain, because fewer pods sit on each one.
- More overhead from per-node helper pods and more IP addresses used.
Many teams settle on a middle size and keep one or two special groups for unusually large or small needs. A useful guide is to pick a node size that can hold at least several of your biggest typical pods, so that rounding errors do not leave much space unusable.
Fragmentation, the hidden waste
Imagine nodes with 8 CPUs where most pods ask for 3 CPUs. Two pods fit per node and use 6 CPUs, leaving 2 CPUs that nothing can use. A quarter of every node is wasted, and the autoscaler cannot fix it, because it simply obeys the requests. Changing the request to 2.5 CPUs would allow three pods per node and use 7.5 CPUs. Small changes in pod size can alter the packing a lot, so it pays to look at the shape of your pods against the shape of your nodes.
Stable base, flexible top
Cloud providers offer discounts for committing to a steady amount of usage over a year or longer. The autoscaler makes this planning easier. Look at the lowest level your cluster reaches in a normal week. That floor is a good amount to cover with commitments. Everything above it moves up and down with traffic, and is best served by on-demand or spot capacity that you pay for only while it exists.
Pod density limits
Kubernetes allows a default of about one hundred and ten pods per node, and some clouds set a lower limit based on how many network addresses a machine can carry. A node may be nearly empty in CPU terms yet full by pod count. When pods are small and many, check this limit, since the autoscaler will add nodes for pod count too, and the machine size you picked may be wasteful in that case.
Track one simple number over time: total requested CPU divided by total allocatable CPU across the cluster. If it hovers near fifty to seventy percent in a busy period, you have healthy headroom. If it is near ninety, spikes will wait for new nodes. If it is near twenty, you are likely paying for air.
22Large Clusters and Tricky Edge Cases
What changes when clusters grow big, and the quirks that surprise even experienced engineers.
Scale and speed
Kubernetes itself is designed for clusters of up to a few thousand nodes. In big clusters the autoscaler’s main loop has more to examine each time, and the simulation work grows with the number of pods and node groups. Its function-duration metric shows if loops are taking too long. A loop that takes longer than the scan interval means decisions arrive late. Remedies include fewer and larger node groups, avoiding needlessly complex scheduling rules, and giving the autoscaler enough CPU and memory of its own. Many organisations also prefer several medium clusters over one huge one, which limits the blast radius of any problem.
Parallel work
Removing empty nodes can happen in bulk, up to a configurable number at once, which keeps large clean-ups from being slow. Removing nodes that still hold pods is more careful and usually proceeds more gently. Recent releases have also improved how quickly the autoscaler can handle many node groups growing at the same time. Check the release notes of the version you run, since this area improves steadily.
Edge cases worth knowing
Pods That Fit Nowhere
A pod larger than any template node never triggers a useful scale-up. Fix the request or add a larger group.
Preemption
High-priority pods can push out lower ones. The pushed-out pods then wait, and may cause scale-up in turn.
Daemon Pods
Helper pods on every node reduce usable space. If they grow, every template node shrinks and fewer pods fit.
Unready Nodes
Nodes stuck in a not-ready state count against the autoscaler’s health checks and are cleaned up only after a waiting period.
Ghost Capacity
Machines that exist in the cloud but never joined the cluster are tracked and removed after a timeout, to avoid paying for them.
Expendable Pods
Pods below the priority cutoff never cause scale-up. This is perfect for placeholders and a trap for anything important.
When things look healthy but are not
One of the trickiest situations is when the autoscaler reports no problem while users complain. This can happen when pods are scheduled fine but are starved of real resources because their requests are too low, or when a service reaches a limit that autoscaling does not see, such as a database connection limit, a network quota or a rate limit on a third-party service. The autoscaler solves the compute problem only. Always trace a slow user request along its full path before blaming capacity.
The human side
Autoscaling is not a replacement for load testing or capacity thinking. It is a safety net and a cost saver. The best-run teams combine it with regular tests, clear ownership of settings, and short written notes explaining the reasons behind each unusual value in their configuration. When a person new to the team reads those notes a year later, the system still makes sense.
An automatic door opens for people who walk toward it, but it cannot tell you whether the corridor behind it is wide enough for the whole crowd. Autoscaling opens the door of capacity. You still need to plan the corridor.
The habit
Treating autoscaling as a reason to skip load testing and capacity planning.
Why it hurts
Hidden limits such as quotas, address space, databases and downstream services are discovered during a real incident rather than a rehearsal.
Better approach
Run regular load tests that follow the whole path, from the first request to the last dependency, and fix the first bottleneck you find before the next one appears.
23Frequently Asked Questions
24Summary and Key Takeaways
If you remember only a handful of things, remember these.
Key Takeaways
- It adds and removes nodes, nothing else. The HPA scales pods, the VPA resizes pods, and the Cluster Autoscaler scales the servers underneath.
- Pending pods drive scale-up. It reacts to pods the scheduler could not place, not to CPU graphs.
- Requests are everything. Honest, right-sized CPU and memory requests make good decisions possible.
- Scale-down is careful by design. Underused nodes wait about ten minutes, and blockers such as bare pods or strict budgets keep nodes alive.
- New nodes take minutes. Use headroom pods when your users cannot wait.
- Guard the rails. Set minimums, maximums and quotas, give it minimal permissions, and never fight it with manual changes.
- Watch events, status and metrics. They explain nearly every strange behaviour.