Autoscaling Internals and Metrics Pipelines in Kubernetes
A plain-English tour of how Kubernetes decides to add or remove pods and machines, and how the numbers behind those decisions travel from your containers to the control loops that act on them.
Picture a busy restaurant. On a quiet Tuesday afternoon, two waiters are enough. On Saturday night, the same restaurant needs ten. A good manager watches the dining room, notices the queue at the door growing, and calls in more staff before the customers get angry. When the rush ends, the manager sends people home so wages are not wasted. The hard parts are noticing early, calling in the right number of people, and not sending staff home five minutes before the next wave.
Kubernetes autoscaling is that manager. It watches your application, decides when more copies are needed, decides when each copy needs more power, and decides when the whole building needs more rooms. But Kubernetes does not have eyes. It only has numbers, and those numbers must travel through a chain of programs before anyone can act on them. If the chain is slow, broken or misleading, the manager makes bad calls.
This guide opens the hood. You will learn the four main autoscalers, the exact formula the pod autoscaler uses, how metrics flow from a container all the way to a scaling decision, why scaling always feels slower than you expect, and how to build a setup that is fast, stable and affordable. Every new term is explained in simple words, and we use everyday pictures to keep the ideas clear.
1Why Autoscaling Exists
Before looking at the machinery, we need to feel the problem it solves. Demand for software is never flat, and fixed capacity is always wrong.
The two ways fixed capacity fails
If you run a fixed number of servers, you must pick that number in advance. Pick too few, and a traffic spike overwhelms them. Pages load slowly, requests time out and customers leave. Pick too many, and you pay for idle machines around the clock. Most real traffic has peaks and valleys, such as a morning rush, a lunchtime lull, a sale event or a viral post. A single fixed number cannot be right for all of them.
Teams used to solve this by guessing the peak and paying for it all day. That works, but it is expensive. Cloud bills are driven by how much capacity you reserve, not by how much you use. Autoscaling aims to make the reserved amount follow the used amount as closely as possible.
Think of a city’s bus service. Running every bus all day wastes fuel at midnight. Running only a few buses leaves people stranded at the morning rush. A smart dispatcher watches how many passengers are waiting and adds or removes buses along the route. Kubernetes autoscalers are dispatchers for your containers.
Three different things can scale
Beginners often think “autoscaling” means one thing. In Kubernetes it means three different kinds of change, each handled by a different tool.
How many copies
Add or remove identical pods. This is called horizontal scaling, like opening more checkout lanes in a supermarket. The Horizontal Pod Autoscaler does this.
How big each copy is
Give each pod more or less CPU and memory. This is called vertical scaling, like giving one cashier a faster scanner. The Vertical Pod Autoscaler does this.
How many machines
Add or remove the nodes that pods run on. Without this, new pods have nowhere to land. The Cluster Autoscaler and Karpenter do this.
When events happen
Scale based on outside events, such as the length of a message queue, and even down to zero pods. KEDA does this.
Those three defaults already tell a story. The system reacts every few seconds, but it is deliberately slow and cautious about shrinking. We will see why later in the guide.
Why this topic is harder than it looks
On a diagram, autoscaling looks simple: if busy, add more. In practice, every step has a delay. Measuring takes time. Computing takes time. Starting a new pod takes time. Finding or creating a new machine takes much more time. Meanwhile the traffic keeps changing. An autoscaler that reacts too fast thrashes up and down like a nervous driver pumping the brakes. One that reacts too slowly lets users suffer. The craft of autoscaling is the craft of tuning these delays.
Autoscaling does not make a slow application fast. It only adds capacity. If one request is slow because of a bad database query, adding ten more pods will not fix it, and may make the database slower.
2The Map of Autoscaling in Kubernetes
Let us see all the players on one page, and how they cooperate like layers of a cake.
The layers
Autoscaling in Kubernetes works in layers. The layers depend on each other, and the bottom layers react to the top ones.
Metrics layer
Programs that measure how busy things are, such as CPU usage, memory, request rates and queue sizes. Everything above depends on these numbers.
Workload layer
The Horizontal Pod Autoscaler, the Vertical Pod Autoscaler and KEDA change the number or size of pods based on the metrics.
Scheduler layer
The Kubernetes scheduler tries to place every new pod on a node that has enough free room. If it cannot, the pod waits in a Pending state.
Node layer
The Cluster Autoscaler or Karpenter notices pods that cannot be placed and asks the cloud for more machines. Later, they remove machines that are no longer needed.
flowchart TB
APP["Running pods"] -->|"usage numbers"| MET["Metrics pipeline"]
MET --> HPA["HPA - how many pods"]
MET --> VPA["VPA - how big each pod"]
MET --> KEDA["KEDA - event driven scaling"]
HPA --> DEP["Deployment replica count changes"]
KEDA --> DEP
VPA --> RES["Pod resource requests change"]
DEP --> SCH["Scheduler places new pods"]
RES --> SCH
SCH -->|"no room - Pending pods"| CA["Cluster Autoscaler or Karpenter"]
CA -->|"add or remove machines"| NODES["Cluster nodes"]
NODES --> SCH
A chain reaction in plain words
Imagine traffic doubles. Pods become busy, so the metrics layer reports high CPU. The HPA asks for more pods. The scheduler tries to place them, but the existing machines are already full, so the new pods stay Pending. The Cluster Autoscaler sees Pending pods, asks the cloud for a new machine, and waits for it to join the cluster. The scheduler then places the pods on the new machine. Finally, the pods start, pass their health checks and begin serving traffic.
Count the steps and you can already see why this takes minutes, not seconds. Each handoff adds delay. A big goal of this guide is to help you shorten or hide these delays.
The pod autoscalers and the node autoscalers do not talk to each other directly. They communicate only through Pending pods and node capacity. This indirect link is the source of many surprises, such as pods waiting while the node autoscaler is blocked by a cloud quota.
3Control Loops: The Heartbeat of Kubernetes
Every autoscaler in Kubernetes is built on one simple idea called the control loop. Understanding it makes all the later chapters feel natural.
Desired state versus actual state
Kubernetes works by comparing two things. The desired state is what you asked for, such as “run five copies of this application”. The actual state is what is really running right now. A small program called a controller watches both. When they differ, it takes an action to close the gap. Then it looks again. This repeating cycle of observe, compare and act is called a control loop or a reconciliation loop.
A thermostat is a perfect control loop. You set the desired temperature to 22 degrees. The thermostat measures the real temperature, compares the two, and turns the heater on or off. It never gets tired and never asks for a meeting. The HPA is a thermostat for your application’s load.
Where the autoscaler fits
The Deployment controller already keeps the number of pods equal to the replica count you wrote. An autoscaler adds a second, higher loop on top. It watches load, decides what the replica count should be, and writes that number into the Deployment. Then the Deployment controller does the work of creating or deleting pods. In this way, autoscalers never start containers themselves. They only change a number, and other controllers do the rest.
This works because Kubernetes defines a standard scale subresource, a tiny interface that says “here is the replica count, and you may read or change it”. Any object that supports it can be autoscaled, including Deployments, StatefulSets and ReplicaSets. Even custom resources can opt in.
Why loops are periodic, not instant
Control loops run on a timer. The HPA checks roughly every 15 seconds. This is a deliberate choice. Reacting to every tiny change would waste effort and cause constant flapping. A short sleep between checks smooths the behavior, much like how a thermostat has a small dead zone before it switches. It also protects the API server from being flooded with requests.
flowchart LR
A["Read current metrics"] --> B["Compare with the target"]
B --> C{"Outside the tolerance band?"}
C -->|"no"| D["Do nothing"]
C -->|"yes"| E["Compute desired replicas"]
E --> F["Apply limits and stabilization"]
F --> G["Write new replica count"]
G --> H["Deployment controller adds or removes pods"]
D --> I["Wait for next cycle"]
H --> I
I --> A
Because the loop is level-based, not event-based, a missed cycle is harmless. The next cycle looks at the current picture and corrects anything that is still wrong. This makes the system robust to crashes and restarts.
4Where the Numbers Come From
An autoscaler is only as smart as its data. Let us follow a single CPU number from the moment a container does some work to the moment an autoscaler sees it.
First, what is being measured?
CPU in Kubernetes is measured in cores or in thousandths of a core called millicores. A value of 500m means half of one CPU core. Memory is measured in bytes, usually written as mebibytes or gibibytes. The number Kubernetes uses for memory is the working set, which is the memory the container is actively using and that cannot easily be taken back by the system.
Two settings on every container matter a great deal. The request is how much the container is promised, and it is what the scheduler uses to find room. The limit is the most it is allowed to use. When we later talk about “CPU utilization”, the percentage is measured against the request, not against the limit and not against the machine. This single fact explains a huge number of autoscaling mysteries.
The measuring chain for CPU and memory
The Linux kernel counts
The operating system tracks, for every container, the total CPU time used and the memory held. These are cumulative counters that only grow.
cAdvisor reads the counters
A component built into the kubelet, the agent on each node, is called cAdvisor. It reads these kernel counters for every container on the node.
The kubelet exposes them
The kubelet serves the numbers through a small resource metrics endpoint that other programs can read over the network.
Metrics Server collects them
A cluster add-on called Metrics Server visits every kubelet on a regular schedule, collects the numbers and keeps the latest values in memory.
The API server serves them
Metrics Server registers itself with the Kubernetes API, so that anyone, including the HPA, can ask for the current usage like any other Kubernetes data.
flowchart LR
K["Linux kernel counters"] --> C["cAdvisor inside kubelet"]
C --> KL["Kubelet resource metrics endpoint"]
KL -->|"periodic scrape"| MS["Metrics Server - in memory"]
MS --> AG["Kubernetes API aggregation layer"]
AG --> HPA["HPA controller"]
AG --> TOP["kubectl top command"]
From counters to rates
Remember that the kernel gives cumulative totals, such as “this container has used 3,000 seconds of CPU since it started”. An autoscaler wants a rate, such as “how much CPU is it using right now”. To get a rate, the system takes two readings a short time apart and divides the difference by the time between them. If the container used 6 more CPU seconds over a 12 second window, it was using half a core. That is why a freshly started container has no usable CPU number for a little while: there is only one reading, so no rate can be computed yet.
If a container has no CPU request, the HPA cannot compute a utilization percentage for it, and it will report an error instead of scaling. Always set requests on containers you want to autoscale on CPU or memory.
5Inside Metrics Server
Metrics Server is a small program with a narrow job. Knowing exactly what it does, and what it refuses to do, saves a lot of confusion.
What it does
Metrics Server periodically contacts the kubelet on every node, reads the current CPU and memory numbers for every pod and node, and keeps only the most recent values in its own memory. It does not write to a disk. It does not keep history. It exists for one reason: to feed autoscalers and simple tools like the kubectl top command with fresh, cheap numbers.
In recent versions it collects data roughly every 15 seconds by default, which is why it is described as built for fast autoscaling. The exact interval is a setting, and managed Kubernetes services may configure it differently, so check your own cluster.
How it plugs into Kubernetes: the aggregation layer
Metrics Server is not part of the core API server. It is a separate program, yet you can ask the main API server for metrics as if it were built in. This works through the API aggregation layer. An object called an APIService tells the API server: “any request for the metrics group should be forwarded to this other service”. The API server acts as a front door and passes the request along, after checking who is asking and whether they have permission.
The API group used for resource metrics is named metrics.k8s.io. When the HPA wants CPU numbers, it sends a request to this group, and Metrics Server answers.
Think of a hotel front desk that forwards guest requests to different departments. You always talk to the same front desk, but the housekeeping request goes to one team and the restaurant request to another. The API server is the front desk, and Metrics Server is the housekeeping team for metrics.
What it deliberately does not do
It is good for
- Feeding the HPA and VPA with current CPU and memory.
- Quick checks with
kubectl top. - Being tiny, simple and cheap to run.
- Giving very fresh numbers with low delay.
It is not meant for
- Keeping history, graphs or alerts.
- Reporting custom application metrics.
- Serving as a complete monitoring system.
- Surviving restarts with its data intact.
Common problems
- It is not installed. Some clusters do not ship with it. Without it, CPU and memory based autoscaling cannot work at all.
- It cannot reach the kubelets. Network rules or certificate settings may block it, and the HPA then reports that it cannot fetch metrics.
- A single replica becomes a single point of failure. If it crashes, autoscaling for resource metrics stops until it returns. Production clusters often run more than one copy.
- Brand-new pods show no data. For a short while after start, there is no complete reading, so the HPA treats those pods carefully, as we will see.
If kubectl top pods works, the resource metrics pipeline is healthy from the kubelet up to the API. If it fails, fix that first before blaming the HPA.
6Custom and External Metrics Pipelines
CPU is a poor signal for many applications. To scale on requests per second, queue length or response time, you need a richer pipeline.
Three metric APIs
Kubernetes defines three standard places where an autoscaler can ask for numbers. They are all served through the aggregation layer, but by different programs.
| API group | What it describes | Typical example | Usual provider |
|---|---|---|---|
| metrics.k8s.io | Basic CPU and memory of pods and nodes | Pod CPU usage | Metrics Server |
| custom.metrics.k8s.io | Application metrics tied to Kubernetes objects | Requests per second per pod | Prometheus Adapter and similar |
| external.metrics.k8s.io | Metrics from systems outside the cluster | Messages waiting in a cloud queue | KEDA, cloud adapters |
The Prometheus path
Most teams that need custom metrics already run Prometheus, a popular monitoring system. Prometheus works by scraping, which means it visits a list of targets on a schedule, such as every 15 or 30 seconds, and records the numbers they publish. Applications publish their own numbers, such as total requests handled, through a small web page of metrics. Other helper programs publish numbers about Kubernetes objects. One example is kube-state-metrics, which reports things like how many replicas a Deployment wants and has.
Prometheus stores the scraped data in a time series database. But the HPA does not know how to speak to Prometheus. It only speaks the Kubernetes metrics APIs. So a translator is placed in between. The Prometheus Adapter reads rules that you write, runs queries against Prometheus, and presents the answers through the custom or external metrics API. The HPA asks the Kubernetes API, the API forwards the question to the adapter, the adapter asks Prometheus, and the answer travels back.
flowchart LR
APP["Application pods expose metrics"] -->|"scrape every 15-30 s"| PROM["Prometheus"]
KSM["kube-state-metrics"] -->|"scrape"| PROM
PROM -->|"query"| ADP["Prometheus Adapter - translation rules"]
ADP --> AG["API aggregation layer"]
AG -->|"custom.metrics.k8s.io"| HPA["HPA controller"]
HPA --> DEP["Deployment replicas"]
The adapter is like an interpreter at a meeting. The HPA speaks only “Kubernetes metrics language”. Prometheus speaks only its own query language. The interpreter listens to one, translates, asks the other, and translates the answer back. A good interpreter is invisible. A bad one causes misunderstandings and delays.
The event-driven path with KEDA
KEDA, short for Kubernetes Event-driven Autoscaling, takes a different route. It ships with dozens of small connectors, called scalers, that know how to read numbers directly from systems such as message queues, streaming platforms, databases and cloud services. KEDA turns these into external metrics for an HPA, and also handles the special case of moving from zero pods to one and back, which the plain HPA does not do by default. We will study KEDA in a later chapter.
Why every extra hop matters
Count the stages in the Prometheus path. The application updates a counter. Prometheus scrapes it at the next interval. The adapter queries a rate over a window of time, often a couple of minutes. The HPA reads the answer on its next cycle. Each of these has its own timer. A spike that starts right after a scrape may not be visible for one full interval. A rate calculated over a long window smooths the spike and hides how sudden it was. So custom metric pipelines are usually slower than the Metrics Server path, and this is a price paid for better signals.
Adapter rules that match too many series, or queries that are too heavy, can slow down answers or overload Prometheus. Keep queries simple, and expose only the few metrics your autoscalers truly need.
7The HPA Algorithm, Step by Step
The pod autoscaler follows a clear recipe. Once you know it, you can predict exactly what it will do and why.
The core formula
Every cycle, the HPA calculates how many replicas it believes are needed using one simple proportional rule: take the current number of replicas, multiply by the ratio of the current metric value to the target value, and round up to the next whole number. In words, if the metric is twice the target, double the pods. If it is half the target, halve the pods.
A worked example
Suppose a web application runs 5 pods. Each pod requests 500m of CPU. You set a target of 60 percent average CPU utilization. During a busy period, each pod is actually using about 750m. That equals 150 percent of its request. The ratio of current to target is 150 divided by 60, which is 2.5. Multiply the 5 current pods by 2.5 and you get 12.5. Rounding up gives 13 pods. The HPA asks for 13 replicas.
After the new pods start, the same total work is spread across 13 pods instead of 5. Each now uses about 288m, which is roughly 58 percent of its request, very close to the 60 percent target. The system has settled.
Imagine five cashiers who are each 150 percent busy, meaning each has more customers than they can comfortably handle. You want each cashier to be only 60 percent busy, so they have breathing room. You work out that you need about 2.5 times as many cashiers, so you open thirteen lanes. The HPA does this arithmetic without ever getting tired.
The tolerance band
To avoid constant tiny adjustments, the HPA skips any change if the ratio is close to one. By default, if the current value is within 10 percent of the target, nothing happens. In our example, a ratio between 0.9 and 1.1 means “close enough”. This dead zone is like the small gap in a thermostat that stops it clicking on and off every few seconds.
What counts as a pod?
The HPA is careful about which pods it includes in the math.
- Pods being deleted are ignored.
- Pods that are not ready yet, or only just started, are treated cautiously. For CPU, a new pod may use unusual amounts while starting, so the HPA waits through a short initialization period before trusting its readings.
- Pods with missing metrics are handled with conservative guesses. When the HPA wants to scale up, it assumes a pod with missing data was using nothing, which makes scale-up smaller. When it wants to scale down, it assumes such a pod was fully loaded, which makes scale-down smaller. In both cases, the effect is to hold back when it is not sure.
Several metrics at once
An HPA can look at more than one metric, for example CPU and requests per second. In each cycle it computes a desired replica count for every metric, then chooses the largest answer. This means the HPA will scale up if any signal is high, and will scale down only when all signals agree that fewer pods are enough. This is a safe choice because it favors availability.
Types of metrics the HPA understands
Resource
CPU or memory of the pods, usually as a percentage of their requests, or as an absolute average value.
Container resource
Looks at one named container in the pod, so a busy sidecar does not distort the scaling of the main application.
Pods
A custom metric measured on each pod, such as requests per second, then averaged across pods.
Object
A metric about another Kubernetes object, such as the request rate of an ingress that fronts the application.
External
A metric that does not belong to any Kubernetes object, such as a message queue length in a cloud service.
Sidecars are a hidden trap for CPU based scaling. A service mesh proxy beside your application also uses CPU and also has requests. Using a container-specific metric keeps the focus on the container that actually does the work.
8Making the HPA Calm: Stabilization and Behavior
The raw formula reacts instantly to every change. Real systems need brakes, so the HPA has a set of controls that decide how fast it may move.
The problem of flapping
Imagine traffic that bounces up and down every minute. A naive autoscaler would add ten pods, then remove ten pods, then add ten again. Each change costs time and resources: new pods must start, warm up and register with load balancers, and removing pods drops their open connections. This pattern is called flapping or thrashing, and it hurts both stability and cost.
Stabilization windows
The HPA remembers its recent decisions. A stabilization window is a period of time during which the HPA looks back at all the recommendations it made, and picks the safest one. For scaling down, it picks the highest recommendation seen during the window. This means the HPA will not shrink the deployment until the lower number has been consistently recommended for the whole window.
- The default scale-down window is 5 minutes. This is why Kubernetes feels slow to shrink: it wants five calm minutes before removing capacity.
- The default scale-up window is zero. The HPA is allowed to grow immediately, because under-capacity hurts users right now.
A restaurant manager does not send waiters home the moment the dining room empties for two minutes. They wait to see whether it is a true lull or just a gap between tables. The stabilization window is that patience.
Rate limiting policies
Beyond the window, the HPA’s behavior settings let you limit how many pods may be added or removed in a given period. A policy can be written as a fixed number of pods, such as “no more than 4 pods per minute”, or as a percentage, such as “no more than 10 percent per minute”. You can define several policies for each direction, along with a rule called the select policy which tells the HPA whether to follow the policy that allows the biggest change, the smallest change, or to disable scaling in that direction entirely.
| Setting | What it controls | Why you might change it |
|---|---|---|
| Scale-down stabilization window | How long to wait before shrinking | Lengthen for spiky traffic, shorten to save cost on steady workloads |
| Scale-up stabilization window | How long to wait before growing | Rarely needed, but a small value can ignore very brief spikes |
| Pods policy | Maximum pods added or removed per period | Protect a database from a sudden flood of new connections |
| Percent policy | Maximum percentage change per period | Keep growth proportional to current size |
| Select policy | Which of several policies wins | Choose the most cautious or the most aggressive behavior |
By default, scale-up is quite bold. The defaults allow the number of pods to double, or to grow by four pods, whichever is larger, every 15 seconds. Scale-down by default is allowed to remove everything it wants but only after the long stabilization window.
flowchart TB
R["Raw recommendation from the formula"] --> W["Stabilization window - pick the safest recent value"]
W --> P["Behavior policies - limit pods or percent per period"]
P --> M["Clamp between minReplicas and maxReplicas"]
M --> O["Final desired replica count"]
The minimum and maximum
Two plain numbers act as the outer guard rails. minReplicas is the floor, the fewest pods you will ever run. maxReplicas is the ceiling, the most the autoscaler may ever create. The ceiling is your emergency brake against runaway cost, a bug that makes metrics explode, or an attack that tries to drive your bill up. Never leave it unreasonably high.
If your deployment files, or a GitOps tool, keep writing a fixed replica count, they will fight with the HPA, and the count will jump back every time you deploy. Remove the fixed replica field from the manifest once an HPA owns it.
9Choosing the Right Metric
The most important autoscaling decision is not a setting. It is the choice of which number to follow. A great algorithm following a bad signal still makes bad decisions.
What makes a good scaling signal?
A good signal has three qualities. It should rise when more capacity is needed and fall when it is not. It should be proportional, so that doubling the load roughly doubles the number. And it should respond to scaling: when you add pods, the number should go down per pod. If adding pods does not change the metric, the autoscaler will keep adding pods forever.
| Signal | Good for | Weakness |
|---|---|---|
| CPU utilization | Compute-heavy services that burn CPU as they work | Poor for services that wait on databases or networks, since they can be overloaded with low CPU |
| Memory usage | Rarely a good primary signal | Memory often does not fall after load drops, so scale-down never triggers |
| Requests per second per pod | Web and API services with steady cost per request | Needs a custom metrics pipeline and a known capacity per pod |
| Concurrent requests or in-flight work | Services with a hard concurrency limit | Needs application support for reporting it |
| Queue length or lag | Workers that process messages or jobs | Needs an external metric, and care to avoid over-scaling for tiny backlogs |
| Response time or latency | Direct measure of user experience | Noisy, and can respond slowly, so it may chase its own tail |
Why CPU is not always enough
Think of a service that mostly waits for a database. Each request spends 95 percent of its time waiting and only 5 percent computing. The pods can be overloaded with waiting requests while their CPU stays low. The HPA sees low CPU and does nothing, while users stare at slow pages. For such services, scaling on concurrent requests or requests per second is much better.
A simple sizing rule from queueing theory
A famous result called Little’s Law says that the average number of requests being handled at once equals the arrival rate multiplied by the average time each request takes. This gives a handy way to estimate capacity. If your service receives 500 requests each second and each takes 0.2 seconds, then on average 100 requests are in flight at any moment. If one pod can comfortably handle 10 at once, you need around 10 pods, plus headroom for bursts and for pods that are starting up.
This reasoning is why scaling on in-flight requests per pod works well. You set the target to the number a pod can handle, and the HPA does the division.
A coffee shop gets 30 customers a minute, and each customer takes 2 minutes to be served, from order to coffee. At any moment there are about 60 people being served or waiting. If each barista can handle 6 at a time, ten baristas are needed. Nobody needs to look at how tired the baristas are. The queue itself tells you.
Rules of thumb
- Choose a signal that measures demand or saturation, not one that only reflects cause or symptoms inside the pod.
- Set the target below the true limit, leaving a safety margin such as 60 to 70 percent, so there is room to absorb bursts while new pods start.
- Prefer averages over single readings, but not so heavily averaged that a real spike disappears.
- Test the signal by load testing. Add load and check that the metric rises, then add pods and check that it falls.
For batch workers reading from a queue, scaling on the backlog divided by the work each pod can finish within an acceptable time is a very effective pattern. It aims at a business goal, such as “clear the queue in 5 minutes”, instead of an indirect machine measurement.
10The Vertical Pod Autoscaler
The HPA adds more pods. The VPA makes each pod the right size. Together they answer different questions.
The problem it solves
Most teams guess the CPU and memory requests for their pods, and the guesses are often wrong. Requests that are too high waste money, because the scheduler reserves room that is never used. Requests that are too low lead to crowded nodes, throttling and out-of-memory kills. The Vertical Pod Autoscaler watches real usage over time and recommends, or applies, better numbers.
Three components, three jobs
Recommender
Collects usage history, builds a statistical picture of what each container needs, and publishes suggested requests. It gives more weight to recent data and accounts for events such as out-of-memory kills.
Updater
Checks running pods against the recommendation. If a pod is far off, it evicts the pod so that a fresh one can start with better numbers, while respecting disruption budgets.
Admission controller
When a new pod is being created, it changes the pod’s requests on the fly to match the current recommendation. It is the part that actually writes the new values.
Update modes
- Off. Only produces recommendations that humans can read. This is the safest way to start, and a useful tool for right-sizing.
- Initial. Sets requests only when a pod is first created. Running pods are never disturbed.
- Recreate. Evicts pods when their requests drift too far from the recommendation, so they restart with new values.
- Auto. Applies recommendations using the best method available. In many versions this is the same as Recreate. Newer Kubernetes versions support resizing some pods in place without restarting, and VPA is gradually adopting that capability, so check what your version offers.
flowchart TB
M["Metrics history"] --> REC["VPA Recommender"]
REC --> OBJ["Recommendation: lower bound, target, upper bound"]
OBJ --> UPD["VPA Updater"]
OBJ --> ADM["VPA Admission Controller"]
UPD -->|"evict a drifted pod"| POD["Old pod"]
POD --> NEW["Replacement pod is created"]
NEW --> ADM
ADM -->|"rewrites CPU and memory requests"| NEW
What the recommendation looks like
The VPA does not give a single number. It publishes a lower bound, a target and an upper bound, along with an uncapped target that ignores any limits you configured. The target is what it believes the container should request. The bounds express its confidence. Early in a pod’s life, with little data, the bounds are wide. As history accumulates, they narrow.
Applying VPA changes by restarting pods means brief disruption. Single-replica workloads can go down during an update. Use the recommendation-only mode for important single-instance services, and rely on disruption budgets and multiple replicas elsewhere.
Good fits and poor fits
- Good fit: workloads that cannot scale out easily, such as a single database primary, a cache with a fixed shard count or a machine learning job whose memory needs vary.
- Good fit: teams that want data-driven right-sizing across hundreds of services, using the Off mode to produce reports.
- Poor fit: services with sudden, short bursts, since the VPA works from history and cannot predict the unexpected.
11HPA and VPA Together
It is tempting to turn on both autoscalers everywhere. That can work, but only if you understand how they interact.
The conflict
Remember that the HPA calculates CPU utilization as usage divided by the request. Now suppose the VPA changes the request. The percentage changes even though the real workload did not. If the VPA increases the request, utilization falls, and the HPA removes pods. Then each remaining pod is busier, usage rises, the VPA raises requests again, and the cycle can continue. The two controllers are chasing each other because they share the same metric.
Imagine two managers adjusting the same factory. One decides how many workers to hire. The other decides how big each worker’s toolkit is. If both react to “how busy is each worker” at the same time, one buys bigger toolkits so workers look less busy, and the other then fires workers because they look less busy. They need clear, separate responsibilities.
Safe combinations
HPA on custom metrics, VPA on resources
Let the HPA scale on requests per second or queue length, while the VPA right-sizes CPU and memory. They look at different signals, so they do not fight.
VPA in recommendation mode with HPA
Use the VPA purely as an advisor. Humans review its numbers and update requests deliberately. The HPA remains the only automatic controller.
VPA for memory, HPA for CPU
Possible, because each uses a different resource. Test it, since memory changes can still alter pod behavior and scheduling.
Both on the same CPU or memory metric
This is the classic fight. Choose one owner for each resource.
A practical workflow
Observe with VPA in recommendation mode
Let it watch your workload for at least a week, including peak days, so its estimates reflect real life.
Set sensible requests
Adopt the recommendations as the baseline requests, with a margin for safety.
Let the HPA handle load changes
With requests now reflecting reality, CPU percentages are meaningful, and the HPA decisions are accurate.
Review regularly
Applications change. Repeat the observation every few months, or after major releases.
Fixing request sizes with the VPA often improves the HPA more than any HPA setting does. A request that is far too large makes utilization look tiny, and the HPA never scales up when it should.
12The Cluster Autoscaler
Pods need machines to live on. When pods multiply, someone must add machines, and later remove the ones nobody needs. That is the Cluster Autoscaler’s job.
The trigger: Pending pods
The Cluster Autoscaler does not watch CPU or memory of nodes. It watches for one specific condition: pods that the scheduler marked as unschedulable because no node has enough room. Those Pending pods are a clear sign that the cluster is out of capacity. This makes its logic wonderfully simple and also explains why it reacts only after pods are already waiting.
How scale-up works
Scan
Every few seconds, the autoscaler looks for unschedulable pods.
Simulate
For each node group, which is a set of identical machines such as an instance group in the cloud, it imagines adding a new node of that kind and asks the scheduler logic whether the Pending pods would fit.
Choose
If several node groups would work, an expander rule picks one. Options include the group that wastes the least space, the one that fits the most pods, the cheapest, a user-defined priority order, or a random one.
Request
It asks the cloud provider to raise the node group’s size by the needed number.
Wait and place
The cloud creates the machine, it boots, joins the cluster and becomes ready. The scheduler then places the waiting pods on it.
How scale-down works
The autoscaler also looks for nodes that are underused. By default, a node whose requested resources are below about half of its capacity becomes a candidate. If it stays a candidate for around ten minutes, and if all its pods could be moved elsewhere, the autoscaler drains the node, which means safely evicting its pods, and then asks the cloud to delete it.
Many things prevent a node from being removed. Pods that are not managed by a controller, pods with local storage, pods blocked by a disruption budget, pods marked as not safe to evict, and pods with rules that tie them to a particular node all make the node sticky. This is a major reason clusters stay larger than expected.
flowchart TB
S["Scan every few seconds"] --> Q{"Unschedulable pods?"}
Q -->|"yes"| SIM["Simulate adding a node in each node group"]
SIM --> EXP["Expander picks the best group"]
EXP --> CLOUD["Ask cloud to add nodes"]
CLOUD --> READY["Node boots and joins"]
READY --> PLACE["Scheduler places Pending pods"]
Q -->|"no"| U{"Underused nodes for long enough?"}
U -->|"yes"| CHK{"Can pods move safely elsewhere?"}
CHK -->|"yes"| DRAIN["Drain and delete node"]
CHK -->|"no"| KEEP["Keep node"]
U -->|"no"| KEEP
Things that surprise people
- It reacts to requests, not usage. A node full of idle pods with large requests looks full and will not be removed. A node with tiny requests but heavy actual load looks empty.
- It needs fixed node shapes per group. Because it works with node groups, a pod that needs a very different machine shape may need a new group created in advance.
- Cloud limits matter. Quotas, lack of capacity in a zone and exhausted IP address ranges can all block scale-up even though the autoscaler is working perfectly.
- Machines are slow. Even a quick cloud machine can take a minute or several minutes to be ready, plus the time to pull container images.
A pod disruption budget that allows zero disruptions can pin a node forever. Review budgets when you wonder why the cluster never shrinks.
13Karpenter and Just-in-Time Nodes
Node-group-based scaling is a bit like ordering from a fixed menu. Karpenter takes a different approach: it looks at what you need and builds exactly that.
The big idea
Karpenter is a node provisioning project that began at Amazon Web Services and now lives under the Kubernetes autoscaling community. Instead of resizing predefined groups, it watches Pending pods directly, looks at their combined requirements, such as CPU, memory, zone, architecture and special hardware, and then asks the cloud for machines of the best fitting type. It can choose from a wide range of instance types, including cheaper spot capacity, without you maintaining dozens of groups.
The Cluster Autoscaler is like a taxi company with a few fixed car sizes: small, medium and large. You say how many cars you need. Karpenter is like a service that looks at your group, say two families and a wheelchair user, and sends the exact mix of vehicles that fits.
How it works
- Batching. Karpenter gathers Pending pods for a brief moment so that it can plan machines for many pods together.
- Bin packing. It computes the cheapest set of machines that can hold the pods, respecting rules about zones, hardware and spreading.
- Direct launch. It calls the cloud’s compute service directly to create the machines, which tends to be faster than going through scaling groups.
- Consolidation. It continuously looks for ways to reduce cost by deleting empty or underused nodes, and by replacing several nodes with fewer or cheaper ones, moving pods safely in between.
- Expiry and drift. It can retire nodes after a set age or when their configuration drifts from the desired template, which helps with security patching.
| Aspect | Cluster Autoscaler | Karpenter |
|---|---|---|
| Unit of scaling | Predefined node groups | Individual nodes chosen per need |
| Machine variety | Limited by groups you create | Wide choice of instance types automatically |
| Speed of provisioning | Goes through group APIs, often slower | Direct launch, often faster |
| Cost optimization | Removes underused nodes | Removes, replaces and consolidates nodes |
| Cloud support | Very broad, many providers | Growing, strongest on a few major clouds |
| Operational model | Mature, widely understood | Newer, with a different mental model |
Choosing between them
If your cloud or distribution has a well-supported Karpenter provider and you run varied workloads, Karpenter can reduce both waiting time and cost. If you run on a cloud where it is not available, or you need the predictability of fixed groups, the Cluster Autoscaler remains a solid and proven choice. Managed offerings from cloud providers also bundle their own node auto-provisioning features, which follow similar ideas.
Whichever you choose, give the node autoscaler accurate pod requests. It plans machines based on requests, so wrong requests produce wrong machines.
14Event-Driven Scaling and Scale to Zero
Some workloads should sleep entirely when there is nothing to do. They wake only when work arrives. This is where event-driven autoscaling shines.
Why the plain HPA struggles at zero
The standard HPA works from the metrics of running pods. If there are zero pods, there is no pod to measure, and nothing to wake the system when work arrives. Also, the HPA’s normal minimum is one replica. Support for scaling to zero exists only as an optional feature in some versions, and it needs metrics that come from outside the pods. So tools were built to close this gap.
KEDA in detail
KEDA is a project that started as a joint effort between Microsoft and Red Hat, and graduated within the Cloud Native Computing Foundation. It runs as a small operator in your cluster and adds new resource types. The main one, the ScaledObject, links a workload to a trigger, such as the length of a queue, the lag of a streaming topic, a database query result, or a schedule.
Operator
Watches ScaledObjects. It handles the special jump between zero and one replica, because only it can watch the event source when no pods exist.
Scalers
Small connectors that read numbers from a specific system, such as a queue service, a database or a monitoring tool.
Metrics adapter
Presents the scaler values as external metrics through the Kubernetes metrics API, so that a regular HPA, created and managed by KEDA, handles scaling from one up to many.
ScaledJob
Instead of scaling a long-running service, it creates one Kubernetes Job per unit of work, which suits long tasks that should run to completion.
flowchart LR
SRC["Event source - queue, stream, database"] --> SC["KEDA scaler reads the value"]
SC --> OP["KEDA operator"]
OP -->|"0 to 1 and 1 to 0"| DEP["Deployment"]
SC --> AD["KEDA metrics adapter"]
AD -->|"external.metrics.k8s.io"| HPA["HPA managed by KEDA"]
HPA -->|"1 to N"| DEP
DEP --> PODS["Worker pods consume events"]
PODS -->|"drain the source"| SRC
Cooldown and cold start
Scaling to zero saves money, but it introduces the cold start problem. When work arrives and no pod is running, someone has to start a pod, pull the image and wait for the application to become ready. The first request or message waits for all that. For queue workers, a short delay is usually fine. For user-facing web requests, it can be unacceptable, so such services often keep a minimum of one or two pods, or use request-buffering layers that hold the request until a pod is ready.
KEDA also has a cooldown setting that decides how long to wait after the last activity before scaling down to zero, preventing sleep and wake cycles from happening too frequently.
Request-driven scale to zero
Projects such as Knative Serving take a different path for web traffic. They put a small proxy in front of the application that counts requests in flight and holds requests while pods start. Knative’s own autoscaler is based on concurrency, meaning how many requests each pod handles at once. This matches Little’s Law from earlier and suits serverless-style services on Kubernetes.
A good rule: if the work comes from a queue or stream, an event-driven tool is usually the cleanest answer. If the work comes from live HTTP users and latency matters, keep a small warm floor of pods.
15The Latency Budget: Why Scaling Feels Slow
Everyone expects autoscaling to react in seconds. In practice it takes minutes. Adding up each delay shows why, and where you can win time back.
Follow one spike through the system
Measurement delay
The kubelet and Metrics Server refresh on a schedule. A custom metric adds the Prometheus scrape interval and the query window. Together this can be from 15 seconds to well over a minute.
Decision delay
The HPA only checks about every 15 seconds. A spike that begins right after a check waits for the next one.
Scheduling delay
The new pods are created and the scheduler picks nodes. If nodes have room, this takes a couple of seconds. If not, the pods stay Pending.
Node delay, if needed
When there is no room, a new machine must be requested, created, booted and made ready. This often dominates the total, taking one to several minutes.
Image pull delay
The node must download the container image, unless it is cached. Large images can take tens of seconds or more.
Startup delay
The application starts up, loads its configuration, warms caches and connects to databases. Some applications need many seconds, and some such as large Java services need much longer.
Readiness delay
The pod passes its readiness checks, and the load balancer begins sending traffic to it.
Ways to win time back
Keep spare capacity
Target a utilization below 100 percent, such as 60 or 70, so existing pods can absorb the first part of a spike while new ones start.
Overprovisioning pods
Run low-priority placeholder pods that reserve spare room on nodes. When real pods arrive, the placeholders are evicted instantly and the real pods take their place. The cluster then adds replacement capacity in the background.
Smaller, faster images
Slim images pull faster. Caching images on nodes, or using a nearby registry mirror, shortens the pull step.
Fast startup
Trim application initialization, use startup probes correctly and avoid slow work during boot.
Scale on leading indicators
Queue depth or request rate rises before CPU does. Following the earlier signal gives the system a head start.
Scheduled or predictive scaling
If you know a sale starts at noon, raise the minimum before then. Some platforms also offer predictions based on past patterns.
Think of a fire station. It does not wait for the alarm to start buying a fire truck. It keeps trucks and crew on standby, because the alarm-to-arrival time is the whole point. Headroom in a cluster is standby capacity. It costs a little all the time and saves you during an emergency.
Starting pods count as load, too. Heavy startup work can raise CPU on new pods, which makes the HPA think the load is even higher and add even more pods. Configure initialization periods and startup probes so that warm-up noise is not mistaken for demand.
16Advantages, Disadvantages and Trade-offs
Autoscaling is powerful but never free. A clear view of the trade lets you decide how much to rely on it.
Advantages
- Capacity follows demand, which lowers cost during quiet periods.
- Traffic spikes are absorbed without a human awake at midnight.
- Right-sizing with the VPA removes wasteful guesses.
- Event-driven scaling lets idle workloads cost nothing.
- Standard APIs let many tools plug in without custom code.
Disadvantages
- It reacts after the fact, so very sudden spikes can still hurt.
- Many moving parts, each with its own delays and settings.
- Bad metrics or requests lead to confidently wrong decisions.
- Runaway scaling can cause large bills or overload backend systems.
- Debugging requires understanding several layers at once.
The main tensions
| Trade-off | Leaning one way | Leaning the other way |
|---|---|---|
| Cost versus resilience | High target utilization saves money but leaves little room for spikes | Low target and spare capacity cost more but protect users |
| Speed versus stability | Fast reaction helps users but can cause flapping | Long windows calm the system but respond slowly |
| Simplicity versus precision | CPU based scaling is easy and works for many | Custom metrics track real demand but need a bigger pipeline |
| Efficiency versus disruption | Aggressive consolidation lowers cost | Frequent pod moves cause restarts and brief interruptions |
| Freshness versus load | Frequent scraping gives newer data | It also places more load on kubelets, Prometheus and the API server |
There is no universal best setting. A latency-sensitive payment service and an overnight reporting job want opposite choices on almost every row of this table. The right answer comes from knowing the cost of being late for your particular workload.
17Reliability, Failure Scenarios and Security
Autoscalers are part of your production safety net, so they must be treated as production systems that can themselves fail or be abused.
What happens when the metrics pipeline breaks
Metrics Server is down
The HPA cannot read CPU or memory and reports an error. It generally leaves the replica count where it is. The cluster keeps running, but it stops adapting to load until the pipeline is back.
Prometheus or the adapter is down
Custom metric based HPAs lose their signal. If you also keep a CPU metric on the same HPA, the remaining metric can still drive scaling, which is one reason to include a backup signal.
Stale or wrong values
A broken exporter that reports zero causes scale-down at the worst moment. A metric stuck at a high value causes endless scale-up until the maximum is reached.
Node autoscaler blocked
Cloud quota, a lack of machines or an exhausted network address range leaves pods Pending while the HPA keeps asking for more.
Designing for failure
- Set a sensible floor. The minimum replica count should cover your normal load, so that a metrics outage does not leave you under-provisioned.
- Set a sensible ceiling. The maximum replica count limits the damage from a metric gone wrong, and should respect what your databases and downstream services can take.
- Run the pipeline with redundancy. Multiple copies of Metrics Server and the adapter, with proper resource requests of their own.
- Use disruption budgets. They protect your service while nodes are drained for scale-down.
- Spread pods across zones. Topology spread rules ensure one zone failure does not take out the whole service, and that scale-up lands in several zones.
- Protect downstream systems. If 50 new pods all open database connections at once, the database may collapse. Use connection pooling and limit scale-up speed with behavior policies.
Security considerations
Least privilege for adapters
Metrics adapters and KEDA scalers often hold credentials to read queues, databases or monitoring systems. Give them read-only access to only what they need.
Secure the aggregation layer
The links between the API server and extension servers use certificates. Keep them valid and tightly scoped, since a rogue metrics server could feed false numbers to autoscalers.
Cost and capacity attacks
An attacker who can generate fake load, or who can alter metrics, can drive scaling up to waste money or exhaust quotas. Rate limiting, ceilings and budget alerts are your defense.
Limit who can change scaling
Permission to edit autoscaler objects is permission to change cost and capacity. Treat it as a sensitive role, and review changes through version control.
Quotas inside Kubernetes, such as namespace resource quotas, can silently block new pods created by the HPA. The HPA reports the desired number, but the deployment never reaches it. Always check quotas when scaling seems stuck.
18Monitoring and Troubleshooting Autoscaling
When scaling misbehaves, you need to find out which layer is at fault. A methodical approach saves hours.
What to watch
Desired versus current replicas
A growing gap means something blocks the scale-up, such as Pending pods, quotas or capacity problems.
Status conditions
The HPA reports whether it is able to scale, whether metrics are active, and whether its limits are capping the result. These conditions explain most mysteries.
Metric freshness
Track the age of the latest data and the success rate of scrapes. Old data means decisions based on the past.
Pending pods and node counts
The number of pods waiting for a place, and how long they wait, is the single best sign of node-level trouble.
Spend per service
A sudden jump in replicas or nodes should trigger an alert, even before users notice anything.
Scaling events and logs
Kubernetes records an event each time the HPA changes the count, and the node autoscalers log their reasons for every decision.
A troubleshooting walk-through
HPA shows unknown metrics
Check that the metrics source works. Run the quick top command for resource metrics, and query the custom metrics API for the others. Confirm that pods have resource requests set.
HPA does not scale up although load is high
Read its conditions. Look for the tolerance band, the maximum replica cap, a behavior policy that limits growth, or a metric that is lower than you expected because requests are oversized.
HPA scales up but pods stay Pending
This is a node problem. Look at the scheduler events on the pods, then the node autoscaler logs. Check cloud quotas, zone capacity and node group limits.
Replica count flaps up and down
Lengthen the stabilization window, add behavior policies, check that readiness probes are accurate, and verify the metric is stable and not distorted by startup spikes.
Cluster never shrinks
Look at what blocks node removal: strict disruption budgets, pods without controllers, local storage, annotations that forbid eviction, or oversized requests that make nodes look full.
New pods get no traffic or fail health checks
This is an application readiness problem, not an autoscaler problem. Check startup time, probes and dependencies.
Make a dashboard that puts the whole chain on one screen: the load signal, the desired and current replicas, the count of Pending pods and the number of nodes, all on the same time axis. Delays between the lines become obvious at a glance.
19Design Patterns and Anti-Patterns
The same few good habits show up in teams that scale well. The same few traps show up in teams that struggle.
Patterns that work
Right-size first, scale second
Set honest requests using real usage data before tuning the HPA. Everything built on percentages becomes more accurate.
Scale on demand signals
Follow requests, concurrency or queue depth for services where CPU does not tell the story.
Fast up, slow down
Allow quick growth to protect users, and use a long stabilization window for shrinking to avoid flapping.
Warm buffer of capacity
Use headroom in utilization targets, and low-priority placeholder pods, so spikes meet ready capacity instead of waiting for machines.
Separate pools by workload type
Put steady services and bursty batch jobs on different node pools, each with its own scaling and cost rules.
Backup signals
Attach a simple CPU metric alongside a custom one, so that a failure in the custom pipeline does not leave the HPA blind.
Anti-patterns to avoid
What it looks like
Pods run with no CPU requests, or with requests copied from a template, and the HPA is set to scale on CPU utilization anyway.
Why it hurts
Utilization is measured against requests. Wrong requests produce meaningless percentages, so the HPA scales too late, too early or not at all.
Better approach
Measure real usage, set requests with a margin, and review them regularly. Use the VPA in recommendation mode to guide you.
What it looks like
Both the HPA and the VPA in automatic mode act on the same CPU metric for the same workload.
Why it hurts
They fight over the same number. The HPA changes pod counts, the VPA changes requests, and each change alters the other’s input, producing oscillation.
Better approach
Give each resource one owner. Use custom metrics for the HPA when the VPA manages CPU and memory, or run the VPA as an advisor only.
What it looks like
The maximum replica count is set to a very large number “just in case”, with no check on cloud quotas, databases or budget.
Why it hurts
A metric bug or an attack can scale the service to a huge size, overload dependencies and produce a shocking bill, while the node autoscaler may hit quota walls and leave pods Pending.
Better approach
Set the ceiling to what the whole system can safely support, alert when the service approaches it, and review it as capacity grows.
What it looks like
A deployment file in version control still includes a fixed replica count, and a deployment tool applies it on every release.
Why it hurts
Each release resets the replica count to the fixed number, causing sudden shrinking or growing in the middle of traffic, and confusing the HPA.
Better approach
Remove the fixed replica field when an HPA manages the workload, and configure your deployment tool to leave the count alone.
20Best Practices and Common Mistakes
Here is a checklist you can carry into your next design review.
Best practices
- Always set requests. Autoscaling on utilization depends on them, and the scheduler and node autoscalers rely on them too.
- Pick the signal with care. Use CPU only if the service is truly CPU-bound. Otherwise follow concurrency, request rate or queue depth.
- Leave headroom. Target around 60 to 70 percent so the system has time to react before it hits the wall.
- Use readiness and startup probes properly. They keep half-started pods out of both traffic and the scaling math.
- Tune behavior deliberately. Choose stabilization windows and rate limits based on how your traffic behaves, not defaults alone.
- Protect dependencies. Limit how fast pods can be added, use connection pools, and test that your database can handle the maximum.
- Add disruption budgets and zone spreading. They keep the service healthy while nodes are added and removed.
- Keep images small and startup fast. Faster start means smaller headroom is needed.
- Load test the whole chain. Simulate a spike and measure the time from load rise to extra capacity serving traffic. Do this before the real event does it for you.
- Watch cost and capacity. Alert on replica or node counts nearing their ceilings and on unusual spending.
- Version everything. Keep autoscaler settings in code, reviewed like any other change.
Common mistakes
- Believing that autoscaling will rescue a poorly performing application.
- Scaling on memory for programs that never release memory, so scale-down never happens.
- Forgetting that new pods need time, and expecting capacity in seconds.
- Letting sidecars or startup bursts distort CPU measurements.
- Ignoring the node layer, and discovering Pending pods only during an incident.
- Using the same settings for every service, even though their traffic patterns differ.
- Making the stabilization window so short that the system flaps, or so long that cost never falls.
- Running Metrics Server with no redundancy, then losing autoscaling when it restarts.
- Not testing what happens when the metrics pipeline itself is down.
Start simple. A single HPA on CPU, with good requests and a sensible ceiling, solves a surprising share of real cases. Add custom metrics, event-driven scaling and smarter node provisioning only when you can name the problem they solve.
21Real-World Usage
These ideas run in production across the industry. Here are common patterns, along with the projects behind them.
Retail and e-commerce sales events
Online shops face sharp, predictable spikes during sales and holiday events. A common approach combines scheduled increases of minimum replicas before the event, an HPA that follows request rate, placeholder pods that keep spare node capacity ready, and strict ceilings that protect payment and inventory databases from being overwhelmed.
Streaming and queue-based processing
Companies that process events from message queues and streaming platforms often scale their worker fleets with KEDA, based on queue length or consumer lag. The workers can drop to zero during quiet hours and expand within minutes when data floods in. KEDA is a Cloud Native Computing Foundation project that was created by Microsoft and Red Hat.
Cost-focused node provisioning
Karpenter, which began at Amazon Web Services and is now part of the Kubernetes autoscaling community, is widely adopted by teams that want faster node startup and lower cost through diverse and spot instance choices, and continuous consolidation. Cloud providers also offer their own managed node auto-provisioning that follows similar ideas.
Managed Kubernetes services
Major managed Kubernetes platforms ship with Metrics Server, support for the HPA and the VPA, and integrated node autoscaling. Some also offer modes where the provider manages nodes entirely and scales them based on pod requests, which pushes accurate resource requests to the center of everything.
Right-sizing programs
Organizations with hundreds of services frequently run the VPA in recommendation mode to build reports of over- and under-provisioned workloads. Open-source tools such as Goldilocks present these recommendations to teams in a friendly form, turning cost reduction into a routine engineering practice.
Serverless on Kubernetes
Knative Serving brings request-driven scaling to zero and back to Kubernetes, with an autoscaler built on concurrency per pod. Teams use it to run function-style workloads with a developer experience similar to hosted serverless platforms.
22Frequently Asked Questions
Short answers to questions that come up again and again.
23Summary and Key Takeaways
What to remember
- Autoscaling is layered. The HPA changes pod count, the VPA changes pod size, KEDA reacts to events, and the Cluster Autoscaler or Karpenter changes machine count. They talk through metrics and Pending pods.
- Everything is a control loop. Each autoscaler compares desired and actual state on a timer and nudges the system closer. Loops are periodic by design, which keeps them stable.
- The metrics pipeline decides the quality of every decision. Resource metrics flow from the kernel, through cAdvisor and the kubelet, to Metrics Server and the API. Custom metrics add Prometheus and an adapter, and each hop adds delay.
- The HPA formula is simple. Current replicas multiplied by the ratio of current to target metric, rounded up, then smoothed by tolerance, stabilization windows and rate policies.
- Requests are the foundation. CPU utilization is relative to requests, and node autoscalers plan machines from them. Accurate requests, often found with the VPA, improve everything else.
- Scaling is slower than it feels. Measurement, decisions, scheduling, node creation, image pulls and startup add up. Win time back with leading signals, headroom, small images and fast startup.
- Treat autoscalers as production systems. Set floors and ceilings, protect dependencies, secure adapters, watch the whole chain on one dashboard and test failures before they happen.