Horizontal Pod Autoscaler in Kubernetes: Scaling Your Pods with Metrics
Learn how the Horizontal Pod Autoscaler watches CPU, memory, and custom metrics to add or remove pods automatically, how its scaling formula and safety settings work, and how to tune it so your application stays fast under load without wasting money when it is quiet.
Imagine a supermarket that opens more checkout lanes when queues grow long and closes them when the shop empties. A manager who watches the queues all day and shouts “open another lane” is a good manager, but a tired one, and no manager can watch a queue at three in the morning. Now imagine the shop has a smart system that counts the shoppers waiting at each lane every few seconds and opens or closes lanes by itself, following a simple rule. That is the Horizontal Pod Autoscaler. It watches how busy your pods are, and it adds or removes copies of your application automatically, so users are served quickly and resources are not wasted.
1Why Applications Need to Scale Automatically
Traffic is never constant, and a fixed number of pods is always wrong some of the time.
Real applications face changing demand. A shop’s website is quiet at night and crowded during a sale. A news site can be overwhelmed within minutes by a breaking story. A payroll system is idle most of the month and heavily used at the end. If you choose a fixed number of pods, you face an uncomfortable choice. Pick enough for the busiest moment, and you pay for idle capacity almost all the time. Pick enough for the average, and users suffer whenever demand rises above it.
Adjusting the number by hand does not work well either. People cannot watch dashboards around the clock, and they react slowly. By the time someone notices that the application is struggling, users have already been affected. Automation solves this by turning a human reaction into a rule that the cluster follows continuously, in seconds instead of minutes.
Think of a restaurant kitchen during a dinner rush. A good head chef calls in extra cooks when orders pile up and sends some home when the rush passes. The chef does not decide by guessing; she looks at the number of tickets waiting. The autoscaler looks at a chosen measure of load, and adds or releases cooks, which are pods, to keep the queue under control.
What automatic scaling offers
Automatic scaling gives you three benefits at once. Users get consistent performance because capacity follows demand. You save money because idle capacity is released. And operations become calmer because nobody needs to adjust replica counts at unsociable hours. The trade-off is that the autoscaler is another moving part, with settings you must understand and tune.
This guide assumes you know what a pod and a Deployment are, but every new term is explained when it first appears. We concentrate on the metrics-based behaviour of the autoscaler, which is how most teams use it.
2What Is the Horizontal Pod Autoscaler?
A control loop that changes the number of pod copies based on observed metrics.
The Horizontal Pod Autoscaler, usually shortened to HPA, is a built-in Kubernetes feature. You describe which workload it should manage, what to measure, what target to aim for, and the smallest and largest number of pods allowed. The HPA then runs a control loop that regularly reads the metrics, compares them with the target, and changes the replica count of the workload to bring the measurement back toward the target.
The word horizontal is important. Scaling horizontally means adding or removing copies of the same pod. The alternative, scaling vertically, means making each pod bigger or smaller, by giving it more or less CPU and memory. Both are useful, but they answer different problems. Horizontal scaling is ideal for stateless services that can run many identical copies side by side.
| Aspect | Horizontal Scaling | Vertical Scaling |
|---|---|---|
| What changes | The number of pods | The size of each pod |
| Best for | Stateless services that can run many copies | Workloads that cannot easily be split, or need right-sized requests |
| Effect on running pods | Existing pods keep running, new ones are added or removed | Pods usually must be restarted to change size |
| Kubernetes component | Horizontal Pod Autoscaler | Vertical Pod Autoscaler, an optional add-on |
| Limit | Cluster capacity and how well the application spreads across copies | The size of the largest node |
Horizontal scaling is hiring more cashiers. Vertical scaling is training one cashier to work faster. Hiring more cashiers works nicely until you run out of counters. Training helps, but one person has limits, and you cannot train someone while they serve a queue.
The autoscaler does not make your application faster or fix a slow one. It only changes how many copies run. If the application has a bottleneck that more copies cannot relieve, such as a saturated database, adding pods will not help and may make things worse.
3The Control Loop: How the HPA Works Step by Step
Observe, compare, decide, act, and repeat.
The autoscaler is a controller that runs inside the cluster’s control plane. About every fifteen seconds by default, it goes around a loop. It reads the current metric values for the pods of each workload it manages. It compares those values with the targets you set. It computes how many pods would be needed to meet the target. Then it adjusts the workload’s desired replica count, subject to safety limits, and the normal Kubernetes machinery creates or removes pods.
sequenceDiagram
participant H as HPA controller
participant M as Metrics API
participant T as Target workload
participant K as Kubernetes controllers
loop Every sync period
H->>M: Read current metrics for the pods
M-->>H: Values
H->>H: Compare with target and compute desired replicas
H->>H: Apply limits, tolerance, and stabilisation
H->>T: Update desired replica count
T->>K: Create or remove pods
end
Find the pods
The autoscaler uses the target workload’s label selector to find the pods it is responsible for.
Fetch the metrics
It asks the appropriate metrics interface for the current values, for example CPU usage of each pod.
Compute the desired count
It applies a simple formula that compares the current value with the target and scales the replica count in proportion.
Apply safety rules
It respects the minimum and maximum, a tolerance band that ignores tiny differences, and rate limits that prevent violent swings.
Update the workload
It writes the new replica count through the workload’s scale interface, and the workload’s own controller does the rest.
A thermostat works the same way. It reads the room temperature, compares it with the setting, and switches the heater on or off. It does not know how to heat a house; it only decides how much heating is needed. The autoscaler decides how many pods are needed, and the rest of Kubernetes provides them.
4What the HPA Can and Cannot Scale
Only workloads that expose a scale interface can be managed.
The autoscaler works with any workload that supports a scale interface, which is a standard way to read and change a replica count. The most common targets are Deployments, which manage stateless pods, and StatefulSets, which manage stateful ones. ReplicaSets also work, and some custom resources from add-ons do too. You name the target in the autoscaler’s definition, and it takes care of the rest.
Deployment
The most common target. New pods are created or removed by the underlying ReplicaSet as the count changes.
StatefulSet
Supported, but each pod has an identity and often storage. Scaling must respect ordered start-up and the needs of the application.
ReplicaSet and custom resources
Anything exposing the scale interface can be targeted, including many resources defined by add-ons.
DaemonSet
A DaemonSet runs exactly one pod per node, so there is no replica count for the autoscaler to change.
Jobs
Jobs run to completion and do not have a steady replica count in the same sense. Other tools scale workers for batch queues.
Scale to zero
The standard autoscaler keeps at least one pod. Scaling to zero needs an experimental feature or an add-on built for event-driven scaling.
flowchart TD
HPA[HPA definition] -->|names the target| SR[Scale interface of the target]
SR --> D[Deployment]
SR --> S[StatefulSet]
SR --> R[ReplicaSet]
SR --> C[Custom resource with scale support]
D --> P1[Pods created or removed]
S --> P2[Pods created or removed in order]
Autoscaling is easiest and safest for stateless services, where any pod can answer any request. Stateful workloads can be autoscaled, but you must be sure that adding or removing members is safe for the data and the cluster they form.
5Where the Metrics Come From: Three Metrics APIs
The autoscaler asks different interfaces for different kinds of numbers.
The autoscaler does not measure anything itself. It reads metrics from standard interfaces of the Kubernetes API, and it uses different interfaces for different kinds of data. The first covers basic resource use and is provided by the Metrics Server. The second and third cover richer data and must be provided by an adapter that you install, connecting the cluster to a monitoring system or another source.
| Interface | What It Carries | Provided By | Example |
|---|---|---|---|
| Resource metrics interface | CPU and memory of pods and containers | The Metrics Server, installed as a cluster add-on | Average CPU use of the pods of a web service |
| Custom metrics interface | Metrics about Kubernetes objects, such as pods or an Ingress | An adapter connected to a monitoring system | Requests per second handled by each pod |
| External metrics interface | Metrics from outside Kubernetes, not tied to a cluster object | An adapter for a queue, cloud service, or monitoring system | Number of messages waiting in a queue |
flowchart TD
HPA[Horizontal Pod Autoscaler] --> A1[Resource metrics interface]
HPA --> A2[Custom metrics interface]
HPA --> A3[External metrics interface]
A1 --> MS[Metrics Server
reads kubelets]
A2 --> AD1[Metrics adapter
reads monitoring system]
A3 --> AD2[Event or external adapter
reads queues and services]
MS --> K[Kubelets on nodes]
AD1 --> PR[Monitoring system]
AD2 --> EX[Queues, cloud services]
The autoscaler is like a manager who reads three kinds of reports. The first is the standard headcount, which anyone can produce. The second is a detailed report on what each kitchen is doing, which needs a special analyst. The third is a report from outside the company, such as the number of bookings, which needs an external reporter.
If the needed metrics interface is not installed or not working, the autoscaler cannot read anything and will not scale. The most common cause of an autoscaler that shows unknown values is a missing or unhealthy Metrics Server or adapter.
6The Metric Types You Can Scale On
Five kinds of metric, each fitting a different situation.
The autoscaler definition lets you list one or more metrics, and each metric has a type. The type tells the autoscaler where the number comes from and how to interpret it. Choosing the right type is central to good autoscaling, because a metric that does not reflect real load will scale the wrong way at the wrong time.
Resource
CPU or memory of the pods, either as a percentage of what each pod requested, or as an average absolute value per pod.
Container resource
Like resource, but for one named container in each pod. Useful when a pod has sidecars that would distort the total.
Pods
A custom metric measured per pod, such as requests per second. The autoscaler averages it across the pods.
Object
A metric about one other Kubernetes object, such as requests per second reported for an Ingress in front of the workload.
External
A metric that does not belong to any Kubernetes object, such as the length of a message queue or a cloud service statistic.
| Metric Type | Interface Used | Typical Target Style | Good For |
|---|---|---|---|
| Resource | Resource metrics | Average utilisation percentage, or average value per pod | General CPU-bound services |
| Container resource | Resource metrics | Average utilisation of one container | Pods with heavy sidecars |
| Pods | Custom metrics | Average value per pod | Requests per second, active connections per pod |
| Object | Custom metrics | Value or average value for one object | Traffic measured at an Ingress or gateway |
| External | External metrics | Value or average value per pod | Queue length, event backlog, business metrics |
Deciding how many cooks to call in could be based on how tired the cooks are, which is the resource metric, how many orders each cook handles per minute, which is a pods metric, how long the queue at the front door is, which is an object metric, or how many reservations were booked online, which is an external metric. Different restaurants care about different signals.
Begin with CPU utilisation, which needs only the Metrics Server. Move to request rate or queue length when CPU proves a poor indicator of real load, for example in services that wait mostly on the network.
7The Scaling Formula with Worked Examples
One proportional rule sits at the heart of every scaling decision.
The autoscaler uses a simple proportional formula. The desired number of replicas equals the current number of replicas multiplied by the ratio of the current metric value to the desired metric value, and the result is rounded up to the next whole number. In words: if the pods are twice as busy as the target, double the number of pods. If they are half as busy, halve it.
| Current Pods | Current Metric | Target | Ratio | Calculation | Desired Pods |
|---|---|---|---|---|---|
| 4 | 80 percent CPU | 50 percent | 1.6 | 4 times 1.6 equals 6.4, rounded up | 7 |
| 10 | 20 percent CPU | 50 percent | 0.4 | 10 times 0.4 equals 4 | 4 |
| 5 | 52 percent CPU | 50 percent | 1.04 | Within the tolerance band | 5, no change |
| 3 | 100 requests per second per pod | 60 requests per second per pod | 1.67 | 3 times 1.67 equals 5 | 5 |
| 6 | Queue of 900 messages | 50 messages per pod | Uses per-pod average of 150 | 6 times 150 divided by 50 equals 18 | 18 |
flowchart LR
A[Current replicas] --> F[Multiply by ratio]
B[Current metric value] --> R[Ratio of current to target]
C[Target metric value] --> R
R --> F
F --> U[Round up to a whole number]
U --> L[Apply min, max, and rate limits]
L --> D[Desired replicas]
Suppose each waiter can comfortably serve five tables. If there are forty tables and currently four waiters, each is handling ten, twice the comfortable load, so you need eight waiters. The formula does exactly this arithmetic, and it always rounds up so nobody is left overloaded.
Rounding up errs on the side of having a little more capacity than needed. Being one pod too generous for a moment is far cheaper than being one pod short while users wait.
8Utilisation and Resource Requests
Percent utilisation is measured against what each pod asked for, not against the machine.
When you scale on CPU utilisation, the number the autoscaler uses is a percentage. That percentage is the pod’s actual usage divided by the amount of CPU the pod requested. A pod that requested half a core and is using a quarter of a core is at fifty percent. This has an important consequence: the autoscaler needs pods to declare requests, and the quality of scaling depends on how realistic those requests are.
| Request Quality | What the Autoscaler Sees | Result |
|---|---|---|
| Realistic request | Utilisation tracks real load | Scaling behaves sensibly |
| Request far too low | Utilisation looks very high even at modest load | Scales up too eagerly, wasting resources |
| Request far too high | Utilisation looks low even under heavy load | Scales up too late or never |
| No request at all | Cannot compute a percentage | The target shows as unknown, and no scaling happens |
flowchart TD
U[Actual CPU use of the pod] --> P[Divide by the pod CPU request]
R[CPU request declared by the pod] --> P
P --> V[Utilisation percentage]
V --> C[Compare with the target percentage]
C --> D[Decide to scale up, down, or hold]
If a waiter says “I can handle four tables” and is serving three, they are at seventy-five percent. If they had said “I can handle twenty tables,” serving three looks like fifteen percent and nobody would call for help. The claim a waiter makes about their capacity, which is the request, decides how busy they appear.
Pods with several containers
For the standard resource metric, the autoscaler adds up the usage and the requests of all containers in the pod. A pod with a small application container and a large sidecar, such as a logging or proxy container, can therefore show misleading utilisation. Where this matters, the container resource type lets you target a single container, so scaling follows the container that actually carries the load.
Enabling autoscaling on a workload whose containers have no CPU requests, and then wondering why it never scales. Define realistic requests based on observed usage before creating the autoscaler.
9Tolerance, Minimum, and Maximum
Three boundaries stop the autoscaler from overreacting or running away.
Minimum replicas
The lowest number of pods the autoscaler will ever set. It guarantees a baseline of capacity and availability even when the load is nearly zero.
Maximum replicas
The highest number of pods it will ever set. It is required, and it protects your budget and your cluster from runaway scaling.
Tolerance
A band, by default about ten percent either side of the target, within which the autoscaler does nothing. It prevents constant tiny changes.
flowchart TD
A[Computed desired replicas] --> B{Ratio inside the tolerance band?}
B -->|Yes| C[Keep current replicas]
B -->|No| D{Below the minimum?}
D -->|Yes| E[Use the minimum]
D -->|No| F{Above the maximum?}
F -->|Yes| G[Use the maximum]
F -->|No| H[Use the computed value]
Choosing the minimum
The minimum should cover your baseline load and your availability needs. A service that must survive the loss of a node or a zone needs at least two or three replicas, spread across failure domains, regardless of how quiet it is. Setting the minimum to one saves money but leaves a single point of failure and a slow response to sudden traffic.
Choosing the maximum
The maximum should reflect the largest load you can serve and afford. It must also fit within your cluster’s capacity and your namespace quotas. If the maximum is higher than the cluster can actually run, extra pods will sit pending, and the autoscaler will believe it has scaled while users still suffer.
A restaurant always keeps two waiters on duty, even at three in the morning, which is the minimum. It never calls in more than twenty, because that is all the tables it has, which is the maximum. And the manager ignores tiny changes in the queue, which is the tolerance, so nobody is called in or sent home every minute.
An autoscaler that stays at its maximum for long periods is a warning sign. Either the limit is too low for real demand, or something else is wrong. Alert on this condition so that people notice before users complain.
10Scaling on Several Metrics at Once
When you list more than one metric, the largest answer wins.
An autoscaler can be given several metrics at the same time, for example CPU utilisation and requests per second. For every metric, it independently computes how many replicas would be needed. It then takes the largest of those numbers. The reasoning is straightforward: if any one measure says the application is under pressure, the application needs more capacity, and it should not be starved just because another measure looks comfortable.
| Metric | Current | Target | Replicas Needed by This Metric |
|---|---|---|---|
| CPU utilisation | 40 percent | 60 percent | Lower than current, suggests fewer pods |
| Requests per second per pod | 150 | 100 | Higher than current, suggests more pods |
| Queue length | Small | Per-pod target | About the same as current |
| Result | The autoscaler picks the highest of the three, which comes from the requests metric | ||
flowchart TD
M1[Metric 1: CPU utilisation] --> C1[Replicas needed: 6]
M2[Metric 2: requests per pod] --> C2[Replicas needed: 9]
M3[Metric 3: queue length] --> C3[Replicas needed: 7]
C1 --> MAX[Take the maximum]
C2 --> MAX
C3 --> MAX
MAX --> R[Desired replicas: 9]
A restaurant manager considers the queue at the door, the number of unattended tables, and the stress of the kitchen staff. If the kitchen says it needs six cooks, the queue says nine, and the tables say seven, she calls in nine, because the busiest signal decides.
Use multiple metrics to protect against blind spots. CPU alone misses services that wait on other systems, and request rate alone misses heavy requests. Together, they catch both kinds of pressure.
Because the highest recommendation wins, the number of pods only falls when every metric agrees that fewer pods are enough. A single stubborn metric, such as one that never drops, can prevent scale-down completely.
11Behaviour: Stabilisation Windows and Scaling Policies
Controls that decide how fast the autoscaler may act and how long it waits before shrinking.
A purely proportional rule would react to every wobble in the metrics. Real load is noisy, and reacting to noise causes flapping, where the number of pods rises and falls repeatedly. The behaviour settings let you tame this. They are configured separately for scaling up and for scaling down, since the two directions have different risks. Scaling up too slowly hurts users. Scaling down too quickly causes a shortage moments later.
Stabilisation window
A period during which the autoscaler remembers its recent recommendations and uses the most cautious one. By default, scale-down waits five minutes, and scale-up acts at once.
Policies
Limits on how many pods, or what percentage, may be added or removed within a given period. They cap the speed of change.
Select policy
When several policies apply, this decides whether the one allowing the biggest change, the smallest change, or no change at all is used.
sequenceDiagram
participant H as HPA
participant W as Recommendation history
Note over H: Load falls, HPA computes fewer pods
H->>W: Record the recommendation
Note over H,W: Stabilisation window looks at recent recommendations
H->>H: Use the highest recommendation in the window when scaling down
Note over H: Scale down only if all recent recommendations agree
H->>H: Apply rate limit from policies
H->>H: Change the replica count in small steps
| Direction | Default Behaviour | Reason | Common Adjustment |
|---|---|---|---|
| Scale up | No stabilisation wait; can double or add several pods every fifteen seconds | Users are suffering, so react fast | Limit growth if new pods overload a shared dependency |
| Scale down | Five-minute stabilisation, and removal at a modest pace | A shortage after scaling down is costly, and load often returns quickly | Lengthen the wait for spiky traffic, shorten it to save cost |
A shop manager who sends cashiers home the moment the queue is empty for ten seconds will call them back a minute later. A wiser manager waits several minutes of steady quiet before sending anyone home, and sends them one at a time.
If your traffic has predictable sharp peaks, lengthen the scale-down window so the capacity built for one peak is still there when the next wave arrives, and limit how aggressively pods are removed.
12Unready Pods, Missing Metrics, and Start-Up
New and unhealthy pods can distort the numbers, so the autoscaler treats them carefully.
When a pod has just started, its early CPU use is often unusually high, because it is loading code and warming caches, and unrepresentative of steady work. When a pod is not ready, it is not receiving traffic, so its usage says little about load. And sometimes metrics for a pod are simply missing. If the autoscaler took all of these numbers at face value, it could make poor decisions, for example adding pods because new pods look busy while starting.
| Situation | How the Autoscaler Treats It | Why |
|---|---|---|
| Pod started very recently | Its early CPU numbers are ignored for an initial period, and it is not counted as ready until its readiness delay has passed | Start-up spikes are not real load |
| Pod not ready | Treated as using none of the target when scaling up | Prevents a runaway of new pods that are still starting |
| Pod missing metrics | Assumed to use none of the target when scaling up, and all of it when scaling down | Errs on the side of caution in both directions |
| Pod being deleted | Left out of the calculation | It is on its way out |
sequenceDiagram
participant H as HPA
participant P as New pod
participant M as Metrics
H->>P: New pod created because of scale-up
P->>P: Starting, loading, not ready
H->>M: Read metrics
M-->>H: Pod shows high start-up CPU
Note over H: Ignore early readings and treat the pod as not yet contributing
P->>P: Becomes ready and settles
H->>M: Read metrics again
M-->>H: Steady values
Note over H: Pod now counted normally
A new waiter on the first shift runs around frantically finding the kitchen and the till. A sensible manager does not conclude the restaurant is too busy and hire more staff just because the newcomer looks exhausted. She waits until he has settled into a normal rhythm.
The slower your pods become ready, the slower the effective scale-up. A pod that needs two minutes to warm up gives you two minutes of continued overload after the decision. Fast start-up and accurate readiness probes make autoscaling much more effective.
13Custom and External Metrics with Adapters
Going beyond CPU requires a bridge between your monitoring data and the cluster.
CPU and memory are only rough indicators of load. Many services are better measured by how many requests they handle, how many connections are open, or how many jobs wait in a queue. To use such metrics, the cluster needs a component that reads them from wherever they live and presents them through the custom or external metrics interface. This component is called an adapter.
flowchart LR
APP[Application exposes metrics] --> MON[Monitoring system collects them]
MON --> AD[Metrics adapter]
AD --> API[Custom or external metrics interface]
API --> HPA[Horizontal Pod Autoscaler]
HPA --> DEP[Deployment replica count]
DEP --> APP
Monitoring-system adapter
Exposes selected queries from a monitoring system as custom metrics. Good when the useful numbers already live there.
Event-driven autoscaler
An add-on that reads from many event sources, such as queues and streams, and can even scale workloads down to zero and back up.
Cloud provider adapter
Reads metrics from the cloud platform’s own monitoring service and offers them as external metrics.
Custom adapter
For unusual sources, a small program can implement the metrics interface and serve the values you need.
Requests per second per pod
Scaling so that each pod handles roughly a chosen request rate keeps latency stable, and reflects real demand better than CPU for services that spend time waiting on other systems.
Queue length per worker
Workers that process messages from a queue scale so that each worker has a manageable backlog. The backlog is an external metric, and the target is the number of messages per worker.
Active connections or sessions
Services that hold long-lived connections, such as chat or streaming, scale on the number of connections per pod, since CPU may stay low while capacity is exhausted.
The autoscaler depends on these metrics every fifteen seconds. Make sure the queries are fast, the adapter is highly available, and the numbers are stable, because a broken or noisy metric leads directly to broken or noisy scaling.
14Reading HPA Status, Conditions, and Events
The autoscaler tells you what it thinks and why, if you know where to look.
Every autoscaler reports its own state. A quick listing shows the current metric value against its target, the minimum, maximum, and current replica counts. A detailed view adds conditions and recent events that explain what the autoscaler did and why it did not do something. Learning to read these is the fastest route to understanding any autoscaling behaviour.
Able to scale
Says whether the autoscaler is allowed to change the replica count right now, or is waiting because of recent scaling or stabilisation.
Scaling active
Says whether the autoscaler can read metrics and compute recommendations. When false, it is blind, and the message explains why.
Scaling limited
Says whether the desired count was held back by the minimum or maximum. It signals that limits are constraining the outcome.
| What You See | What It Means | What To Do |
|---|---|---|
| Target shows unknown | The autoscaler cannot compute the metric | Check requests on pods, the Metrics Server, and adapters |
| Message about missing request for CPU | Pods have no CPU request | Define realistic requests in the pod template |
| Message about failing to get metrics | The metrics source is unavailable | Check the API service and the Metrics Server or adapter |
| Current replicas equal the maximum | The upper limit is reached | Check demand and raise the maximum if justified |
| Current replicas equal the minimum with low load | Normal quiet state | Nothing, unless the minimum is too costly |
| Desired differs from current for a long time | Constraints such as quotas, pending pods, or policies | Check pod events and cluster capacity |
The status view is the autoscaler’s notebook. It writes down what it measured, what it wanted to do, and why it held back. A manager who reads the notebook understands the decisions instead of guessing.
When you first create an autoscaler, watch its status while you apply artificial load. Seeing the values, the desired count, and the events change together builds an understanding that no document can match.
15Working with Deployments: Replica Counts and Rollouts
Two owners of the replica count cause a fight.
A Deployment normally has its own replica count, written in its definition. When an autoscaler manages the Deployment, it takes over that number. If your deployment manifests still contain a fixed replica count and you apply them regularly, each application of the manifest resets the count to the fixed value, undoing the autoscaler’s work until it corrects again. The result is a sudden drop in pods every time you deploy, a common and painful surprise.
sequenceDiagram
participant H as HPA
participant D as Deployment
participant P as Pipeline applying manifests
H->>D: Set replicas to 12 for current load
P->>D: Apply manifest containing replicas 3
D->>D: Scale down to 3 pods immediately
Note over D: Users suffer a sudden capacity drop
H->>D: Sets replicas back to 12 after next check
Good practice
- Leave the replica count out of the manifest of an autoscaled workload
- Let the autoscaler own the number of pods entirely
- Use the minimum setting of the autoscaler for the baseline
Common problems
- Every release resets capacity to a fixed low value
- Two systems, a pipeline and the autoscaler, disagree constantly
- Sudden drops during deployments cause brief overload
Rolling updates while autoscaling
Autoscaling and rolling updates can happen at the same time. During a rollout, the Deployment holds two ReplicaSets, and the autoscaler’s change in replicas is distributed between them in proportion. This works well in practice. But it does mean a rollout during peak traffic is riskier, because capacity is being both replaced and adjusted, so avoid deploying at moments of extreme load when you can.
Keeping a replica count in a template or chart values file and applying it on every release. Remove it, so that the autoscaler alone decides how many pods should run.
16HPA with the Cluster Autoscaler, VPA, and Disruption Budgets
Several scaling mechanisms coexist, and each covers a different level.
The Horizontal Pod Autoscaler changes the number of pods. But pods need nodes to run on. If the cluster has no free room, the new pods stay pending, and the scale-up achieves nothing. A second mechanism, the cluster autoscaler, adds nodes when pods cannot be scheduled and removes idle nodes. The two work as a chain: the pod autoscaler asks for more pods, and the cluster autoscaler provides the machines for them.
sequenceDiagram
participant L as Load
participant H as HPA
participant S as Scheduler
participant C as Cluster autoscaler
participant N as New node
L->>H: Metrics rise
H->>S: Desired pods increased
S->>S: No room, some pods pending
S->>C: Pending pods cannot be scheduled
C->>N: Add a node
N-->>S: Node ready
S->>N: Schedule pending pods
Note over L,N: Capacity is finally available
| Mechanism | What It Scales | Reacts To | Works With HPA By |
|---|---|---|---|
| Horizontal Pod Autoscaler | Number of pods | Metrics such as CPU, requests, or queues | Deciding how many pods are needed |
| Cluster autoscaler | Number of nodes | Pods that cannot be scheduled, and idle nodes | Providing machines for the pods the HPA requests |
| Vertical Pod Autoscaler | Size of pod requests | Observed resource usage over time | Right-sizing requests, but not on the same metric as the HPA |
| Pod disruption budget | Nothing; limits evictions | Voluntary disruptions such as node drains | Protecting availability during node changes, not HPA scale-down |
The pod autoscaler is the manager calling in more cooks. The cluster autoscaler is the landlord adding kitchen space when the cooks have nowhere to stand. If the manager calls in ten cooks and the kitchen holds only six, four wait in the corridor until more kitchen is built, and building takes time.
The vertical autoscaler and the horizontal autoscaler should not both act on CPU or memory for the same workload. One resizes pods while the other counts them, and they can chase each other in circles. Use the vertical one to right-size requests and the horizontal one with custom metrics, or keep it to recommendations only.
Disruption budgets protect against voluntary evictions, such as node drains. They do not restrict the autoscaler from reducing the replica count. Control the speed of scale-down with the behaviour settings instead.
17Choosing the Right Metric
The best metric is the one that rises before users suffer and falls when capacity is no longer needed.
Picking a metric is the most important design choice for an autoscaler. A good metric reflects real load on the application, moves quickly enough to warn you in time, and falls again when the load falls. A bad metric can be flat while users suffer, or stay high after load has gone, making the autoscaler either useless or wasteful.
| Metric | Good For | Weakness |
|---|---|---|
| CPU utilisation | Compute-bound services, easy to set up with only the Metrics Server | Misleading for services that mostly wait on other systems |
| Memory utilisation | Workloads whose memory grows with load and is released afterwards | Many runtimes hold on to memory, so it does not fall, blocking scale-down |
| Requests per second per pod | Web services and APIs | Ignores the cost of each request, so heavy requests can be missed |
| Latency | Directly reflects user experience | Can rise for reasons that more pods cannot fix, causing pointless scaling |
| Queue length or lag | Workers processing messages or events | Needs an adapter, and targets must consider processing time |
| Active connections | Long-lived connection services | Needs application metrics and an adapter |
flowchart TD
Q{What limits this service?} -->|Processor time| CPU[CPU utilisation]
Q -->|Number of requests| RPS[Requests per second per pod]
Q -->|Backlog of work| QL[Queue length per worker]
Q -->|Open connections| CON[Active connections per pod]
Q -->|Memory that is released after load| MEM[Memory utilisation with care]
CPU --> CHK[Check the metric rises before users suffer]
RPS --> CHK
QL --> CHK
CON --> CHK
MEM --> CHK
A bakery could decide when to bake more bread by watching how tired the bakers are, how many customers enter per minute, or how empty the shelves are. The shelves are the most direct signal of trouble, but bakers’ tiredness is easiest to measure. The best choice depends on what really limits the bakery.
Scaling on memory often looks tempting but works poorly. Many programs never return memory to the system after a peak, so utilisation stays high, and the autoscaler refuses to scale down. Use it only for workloads whose memory rises and falls with load.
18Tuning Targets and Understanding Delay
Autoscaling is never instant, so targets must leave room for the wait.
Between a rise in load and the arrival of new capacity, there are several delays. The metrics are collected on an interval. The autoscaler checks on its own interval. New pods must be scheduled, their images pulled, their containers started, and their readiness checks passed. If nodes are needed, that adds minutes. During all this time, the existing pods carry the extra load. The target you choose determines how much spare capacity exists to absorb it.
Load rises
Demand increases, and the existing pods start working harder.
Metrics are collected
The metrics source picks up the change on its next collection cycle, adding up to about fifteen seconds.
Autoscaler decides
On its next check, the autoscaler notices the change and raises the desired count, adding up to another fifteen seconds.
Pods are scheduled and started
New pods are placed, images pulled, and containers started, which takes from seconds to minutes.
Pods become ready and share the load
Only after passing readiness do the new pods receive traffic and relieve the existing ones.
| Target Utilisation | Headroom | Effect |
|---|---|---|
| Around 50 percent | Large | Absorbs sudden spikes and slow start-up, at higher cost |
| Around 70 percent | Moderate | A common balance for steady services |
| Around 90 percent | Small | Efficient, but risky if pods start slowly or traffic spikes suddenly |
If it takes twenty minutes for an extra chef to arrive, the kitchen must keep enough spare capacity to survive those twenty minutes. A kitchen run at ninety percent of its limit will be swamped before help arrives, while one run at fifty percent absorbs the surge.
Choose the target from the speed of your slowest step. The slower pods become ready, or nodes appear, the lower the target should be. Speed up the slow steps, with smaller images and faster start-up, to be able to run at higher utilisation safely.
19Performance and Scale Considerations
The autoscaler itself is light, but its dependencies and side effects are not.
The HPA controller does modest work: it reads metrics and updates a number. Even hundreds of autoscalers add little load to the control plane. The real costs lie in what it depends on and what it triggers. The metrics source must answer quickly for every autoscaler every fifteen seconds. Each scale-up creates pods, which places work on the scheduler, the nodes, the image registry, and any shared services that the new pods connect to.
Advantages
- Very low overhead on the control plane
- Scales to many workloads, each with its own autoscaler
- No extra components for CPU and memory scaling beyond the Metrics Server
- Responds automatically, around the clock
Disadvantages / Trade-offs
- Depends on the health of the metrics source
- Rapid scale-ups can overload databases and other shared services
- Large numbers of new pods strain image pulls and scheduling
- Reaction time is limited by collection, checks, and start-up
Protect downstream systems
More pods mean more connections to databases and services. Limit the rate of scale-up, and size connection pools, so growth does not overwhelm shared dependencies.
Keep images small and cached
Smaller images and pre-pulled images on nodes make new pods start faster, which shortens the whole scale-up chain.
Set the maximum wisely
A ceiling that fits cluster capacity and quotas prevents pending pods and protects you from runaway cost caused by faulty metrics.
Avoid oversized numbers of tiny autoscalers
Share settings through templates so that many autoscalers remain consistent and understandable.
20Security Considerations
Whoever controls the autoscaler’s inputs and limits controls your capacity and your bill.
Autoscaling looks like a purely technical feature, but it has security and cost implications. The autoscaler decides how much of the cluster a workload may use. Anyone who can change the autoscaler’s maximum can raise your costs and crowd out other workloads. Anyone who can influence the metrics it reads can trigger scaling, deliberately or not. And the metrics adapters themselves hold credentials to your monitoring systems.
Control who can edit autoscalers
Permission to create or change autoscalers is permission to change capacity and cost. Restrict it with role-based access control, and review changes to maximum values in the same way as changes to budgets.
Protect the metrics pipeline
If an attacker or a buggy application can publish false metrics, it can drive the autoscaler up or down. Limit who may write to the metrics source, and validate what adapters expose.
Set sensible quotas
Namespace resource quotas cap how much an autoscaler can consume, giving a second line of defence against runaway scaling caused by mistakes or attacks.
Secure adapter credentials
Adapters often hold credentials to read from monitoring systems, queues, or cloud services. Give them read-only access, store the credentials as secrets, and restrict where the adapter can be reached.
Think about denial of wallet
A flood of traffic, real or malicious, can make an autoscaler add many pods and generate large costs. Combine autoscaling limits with rate limiting and traffic protection at the edge.
An automated system that adds capacity on demand will happily pay for an attacker’s traffic. Put protective limits, such as a sensible maximum and edge rate limiting, in front of any workload that scales automatically.
21Troubleshooting and Monitoring Autoscalers
Most autoscaling problems have plain, visible causes.
Look at the autoscaler status
Read the current and target values, the desired and current replica counts, and the conditions. Unknown values and messages point to the problem area.
Check the metrics source
Confirm that the Metrics Server, or the adapter, is running and that its API service is available. Request usage for the pods directly to see whether data exists.
Check requests on the pods
For utilisation targets, confirm every container has a realistic request. Missing requests are the most frequent cause of unknown values.
Check whether new pods can actually run
If the autoscaler wants more pods but they stay pending, look for lack of node capacity, quotas, or scheduling constraints.
Review behaviour settings
If the autoscaler seems slow or stubborn, check stabilisation windows, policies, and tolerance, which may be holding it back on purpose.
| Symptom | Likely Cause | What To Do |
|---|---|---|
| Target shows unknown | Metrics unavailable or missing requests | Fix the metrics source and define requests |
| Never scales up despite high load | Requests too high, wrong metric, or limits reached | Adjust requests, choose a better metric, review the maximum |
| Never scales down | Memory metric that does not fall, or long stabilisation, or a second metric that stays high | Review metrics and the scale-down window |
| Flaps up and down | Noisy metric or too short stabilisation | Lengthen the scale-down window and smooth the metric |
| Desired replicas not reached | Pending pods because of capacity or quotas | Add capacity, adjust quotas, check the cluster autoscaler |
| Replicas drop after every deployment | Fixed replica count in the manifest | Remove the replica count from the manifest |
| Scales up on start-up | Start-up spike or missing readiness delay | Tune readiness and the initialisation settings |
Current versus desired replicas
A persistent gap shows that something blocks scaling, such as pending pods or rate limits.
Time at the maximum
Alert when a workload sits at its maximum for long. It usually means the limit is too low or something is wrong.
Autoscaler unable to read metrics
Alert on the condition that says scaling is inactive, since the autoscaler is then blind.
Scaling frequency
Very frequent changes signal flapping. Track how often replica counts change per hour.
User-facing latency and errors
The final test of autoscaling is whether users stay happy during load changes, so watch these next to the replica count.
22Design Patterns for Autoscaling
Reliable arrangements used in production clusters.
Pattern: Realistic requests first
Measure real usage and set requests accordingly before creating the autoscaler, so that utilisation percentages carry real meaning.
Pattern: Generous minimum, sensible maximum
Set the minimum to cover baseline load and availability across zones, and the maximum to fit cluster capacity and budget, with an alert when the maximum is reached.
Pattern: Fast up, slow down
Allow quick scale-up to protect users, and use a longer stabilisation window and limited pace for scale-down to avoid flapping and shortages.
Pattern: CPU plus a business metric
Combine CPU utilisation with requests per second or queue length so the autoscaler reacts to both computing pressure and real demand.
Pattern: Warm capacity before known events
Raise the minimum ahead of scheduled peaks, such as a sale or a broadcast, and let the autoscaler handle unexpected variation on top.
Pattern: Autoscaling with a healthy chain
Pair the pod autoscaler with a cluster autoscaler, fast readiness, and protected shared dependencies, so each scale-up actually produces working capacity.
flowchart TD
A[Measure real usage] --> B[Set realistic requests]
B --> C[Choose metrics that reflect load]
C --> D[Set minimum, maximum, and behaviour]
D --> E[Load test the whole chain]
E --> F[Monitor status and user impact]
F --> G[Review and adjust regularly]
G --> A
23Anti-patterns to Avoid
Habits that make autoscaling ineffective or dangerous.
Problem
Autoscaling on utilisation with missing or unrealistic requests.
Why It’s Harmful
The autoscaler cannot compute utilisation, or computes a misleading one, so it never scales or scales far too much.
Correct Approach
Measure real usage, set realistic requests, and verify the autoscaler shows real percentages.
Problem
Keeping a fixed replica count in the manifest of an autoscaled workload.
Why It’s Harmful
Every deployment resets the pod count, causing sudden capacity drops that hurt users.
Correct Approach
Remove the replica count from the manifest and let the autoscaler own it.
Problem
Setting the target utilisation very high to save money.
Why It’s Harmful
There is no headroom to absorb spikes while new pods start, so users suffer during every surge.
Correct Approach
Choose a target that leaves room for the slowest step in the scale-up chain.
Problem
Scaling on memory for workloads that never release memory.
Why It’s Harmful
Utilisation stays high after the peak, and the autoscaler refuses to scale down, wasting money.
Correct Approach
Scale on CPU, request rate, or queue length, and use memory only when it truly tracks load.
Problem
Setting a maximum larger than the cluster can host.
Why It’s Harmful
Extra pods stay pending, and the autoscaler appears to have scaled while users still wait.
Correct Approach
Match the maximum to real capacity and quotas, and pair with a cluster autoscaler if growth is needed.
Problem
Running both horizontal and vertical autoscaling on the same resource.
Why It’s Harmful
They chase each other, one changing pod size while the other changes pod count, producing instability.
Correct Approach
Use the vertical autoscaler for recommendations, and keep the horizontal one on custom metrics or a different signal.
Problem
Trusting autoscaling to solve a bottleneck outside the workload.
Why It’s Harmful
More pods can overload a shared database or service, making the outage worse.
Correct Approach
Find the true bottleneck, protect shared dependencies, and limit the pace of scale-up.
24Best Practices and Common Mistakes
A short checklist for dependable autoscaling.
Best Practices
- Set realistic resource requests before enabling autoscaling
- Choose metrics that reflect what limits your service
- Keep a healthy minimum and a sensible, capacity-aware maximum
- Use fast scale-up and cautious scale-down settings
- Remove fixed replica counts from autoscaled manifests
- Pair with a cluster autoscaler and fast readiness
- Load test the whole chain and monitor status and user impact
Common Mistakes
- Expecting instant scaling
- Ignoring the effect of slow start-up and readiness
- Forgetting that CPU can be a poor indicator for waiting services
- Never testing scale-down behaviour
- Letting scale-ups overwhelm databases and other dependencies
- Not alerting when the maximum is reached
Teams often test only the scale-up. Also test how the application behaves as pods are removed: in-flight requests, connection draining, and caches. A scale-down that drops requests is a failure even if the scale-up was perfect.
25Real-World Scenarios
How teams use the autoscaler in daily operations.
A retailer surviving a sale
An online shop scales its storefront on CPU utilisation and requests per second, with a raised minimum before the announced start of a sale. When traffic triples, the autoscaler adds pods within a couple of minutes, the cluster autoscaler adds nodes, and customers see no slowdown.
A media company scaling queue workers
A media company processes uploaded videos with worker pods. The autoscaler uses the queue length per worker as an external metric. During busy hours, hundreds of workers run, and at night the count falls to a handful, saving a large share of the compute bill.
A startup fixing an outage after every release
A startup kept a replica count of three in its chart, and every release dropped capacity from dozens of pods to three. After removing the fixed value from the chart, releases no longer caused sudden slowdowns.
A bank protecting its database
A bank’s payment service scaled up quickly and overwhelmed its database with connections. The team limited the rate of scale-up, capped the maximum, and added connection pooling, so growth stayed within what the database could handle.
An operations team finding a stuck maximum
An alert showed a workload sitting at its maximum for days. Investigation revealed a slow memory-driven metric that never fell and a limit set years earlier. Changing the metric and raising the maximum brought both cost and latency down.
26Frequently Asked Questions
It automatically changes the number of pods of a workload, up or down, by comparing observed metrics with a target and applying a proportional formula, within a minimum and maximum.
A working Metrics Server, realistic CPU requests on the pods, and an autoscaler definition naming the target workload, the target utilisation, and the minimum and maximum replicas.
Multiply the current replica count by the ratio of the current metric to the target metric, and round up. If the ratio is within the tolerance band, nothing changes.
Usually because the pods have no resource requests, or the metrics source is unavailable. Check requests, the Metrics Server or adapter, and the condition messages on the autoscaler.
Not by default. The standard autoscaler keeps at least one replica. Event-driven add-ons, or an experimental feature, can scale to zero for suitable workloads.
A stabilisation window, by default five minutes, makes it use the highest recent recommendation. This prevents flapping and shortages when load returns quickly.
Yes. It computes a desired count for each metric and uses the largest one, so scale-down happens only when all metrics agree that fewer pods are enough.
No. A fixed count in the manifest resets capacity on every apply. Leave it out and let the autoscaler own the replica count.
Perhaps the pods are pending for lack of capacity, are slow to become ready, or the true bottleneck is elsewhere, such as a database. Check pod status, readiness, and shared dependencies.
Not on the same CPU or memory metric for the same workload, since they interfere. Use the vertical one for recommendations, or apply them to different signals.
The one that rises before users suffer and falls when capacity is no longer needed. CPU is a good start for compute-bound services, and request rate or queue length suits many others.
It is never instant. Metric collection, autoscaler checks, pod scheduling, start-up, and readiness all add delay, so leave headroom in your target and consider warming capacity before known peaks.
27Summary and Key Takeaways
The Horizontal Pod Autoscaler is the Kubernetes control loop that matches the number of pods to the load on an application. On a regular cycle, it reads metrics, from the Metrics Server for CPU and memory or from adapters for custom and external signals, compares them with your targets, and applies a proportional formula to compute the desired number of replicas. Tolerance, minimum and maximum limits, stabilisation windows, and scaling policies keep it from overreacting.
Good autoscaling depends on the surroundings as much as on the autoscaler. Pods need realistic resource requests, fast start-up, and accurate readiness checks. The metric must reflect what truly limits the service. The cluster needs room for new pods, or a cluster autoscaler to provide it, and shared dependencies must survive the growth. Manifests must not fight the autoscaler over the replica count. And the whole chain deserves monitoring, alerts, and load tests. Built that way, autoscaling gives you consistent performance and lower costs without anyone watching the dashboards at night.
Key Takeaways
- It is a proportional control loop — desired replicas equal current replicas times the ratio of current metric to target, rounded up, and checked against tolerance and limits.
- Requests define utilisation — CPU and memory percentages are measured against pod requests, so realistic requests are essential and missing ones stop scaling entirely.
- Pick the right metric — start with CPU, and move to request rate or queue length through adapters when CPU does not reflect real load.
- Fast up, slow down — use behaviour settings so scale-up protects users quickly while scale-down waits for steady low demand to avoid flapping.
- Autoscaling is never instant — metric collection, checks, scheduling, start-up, and readiness add delay, so leave headroom and warm capacity before known peaks.
- Do not fight the autoscaler — remove fixed replica counts from manifests, avoid running vertical and horizontal scaling on the same metric, and pair with a cluster autoscaler for node capacity.
- Protect and observe — set a capacity-aware maximum and quotas, guard shared dependencies and metrics pipelines, and alert on unknown metrics, stuck maximums, and flapping.