Kubernetes autoscaling: A complete guide to HPA, VPA, Cluster Autoscaler, and KEDA

All posts

Kubernetes autoscaling adds or removes computing power for your apps as traffic goes up and down. The goal is simple. Your apps always have enough capacity to stay fast, and you stop paying for servers that sit idle. A cluster that scales itself also handles a sudden traffic spike without anyone resizing it by hand.

When demand changes, you have two basic options. You can run more or fewer copies of an app, which is horizontal scaling, or give each copy more or less CPU and memory, which is vertical scaling. Manual changes come late and need someone watching, so an autoscaler makes them for you. Autoscalers have limits too. Each one reacts a little after demand changes, and each one works well only when every app states how much CPU and memory it needs.

Four techniques cover the parts of a cluster you may need to scale:

  • Horizontal Pod Autoscaler (HPA) changes how many copies of an app run. Each running copy is called a pod.
  • Vertical Pod Autoscaler (VPA) changes how much CPU and memory each pod gets.
  • Cluster Autoscaler changes how many servers, called nodes, the cluster has.
  • KEDA adds pods when outside events arrive, such as new jobs in a queue, and can remove every pod when there is no work.

This guide explains how each technique works, compares all four in one table, and gives you a two-question selector for choosing between them. It then covers sample configuration files, cost savings, common fixes, and a step-by-step rollout for your own cluster.

Try it yourself: every cluster on Rackspace Spot includes a managed Cluster Autoscaler at no extra cost, so nodes join and leave on their own as load changes. A working cluster starts at $7.20 a month, which undercuts what most other providers charge for a comparable managed cluster.

Understanding Kubernetes autoscaling fundamentals

What is Kubernetes autoscaling?

Kubernetes autoscaling is software that resizes your cluster for you. Without it, someone has to notice a slowdown and run kubectl scale by hand. An autoscaler checks a number such as CPU use every 10 to 30 seconds, compares it with your target, and adds or removes capacity when the two drift apart.

‍

Core components enabling Kubernetes autoscaling

Every autoscaler relies on the same four parts of Kubernetes:

  • Metrics Server checks how much CPU and memory each pod uses every 15 seconds.
  • The API server is the cluster's central record. It stores each autoscaler's settings and the pod count it asks for.
  • Controllers, such as a Deployment, read that pod count and start or stop pods to match it.
  • The scheduler picks a node for each new pod. If no node has room, the pod waits in a Pending state.
Four autoscalers, two layers of one clusterPod autoscalers change pods. The Cluster Autoscaler changes nodes.Event sourcesQueues, streams, schedulesMetrics ServerCPU and memory per pod, every 15 secondsKEDAScales on events,down to zeroHPASets how manypods runVPASets CPU andmemory per podCluster AutoscalerSets how manynodes runfeedsCLUSTERNode 1podpodpodNode 2podpodnew podlarger podmore CPU and memoryNode 3joins on demandPending podlands here oncethe node is readystarts the first podadds or removes podsresizes a podadds orremoves nodesBased on Kubernetes v1.37, VPA 1.6 and KEDA v2.21
How HPA, VPA, KEDA and the Cluster Autoscaler act on pods and nodes

‍

Key metrics that drive autoscaling decisions

CPU use is the most common trigger. Kubernetes measures it against the pod's CPU request, which is the amount of CPU the pod asks for. A pod that requests 500m, or half a CPU, and uses 400m runs at 80%. Memory can trigger scaling too, but it is a weaker signal, because memory use often stays high after traffic drops.

Kubernetes counts some cached memory as in use, even though the operating system cannot always free it. As a result, an HPA that scales on memory can keep extra pods running after the busy period ends. If a container sets no CPU request, the HPA has nothing to measure against and does not scale it.

Autoscalers can also scale on two other kinds of numbers:

  • Custom metrics come from inside the cluster, such as how many requests each pod handles per second.
  • External metrics come from outside the cluster, such as the number of messages waiting in a RabbitMQ queue.

The four pillars of Kubernetes autoscaling

The table below compares all four autoscalers side by side:

Kubernetes autoscaling techniques compared

TechniqueWhat it scalesMain triggerBest use caseScale to zeroKey limitation
HPAPod replicasCPU, memory, custom metricsStateless web apps and APIsBeta in v1.37, not on CPU or memoryLags sudden spikes
VPAPod CPU and memoryPast CPU and memory useSteady or single-instance workloadsNoCan restart pods
Cluster AutoscalerNodesWaiting pods, idle nodesAny cluster with variable demandNodes only, where the provider allowsNew nodes take minutes
KEDAPod replicas and JobsQueues, streams, schedulesBursty or mostly idle event workloadsYes, by defaultOne more tool to install and run

‍

Which autoscaling technique to use when

Select one problem and one environment, and the recommended starting point appears below with the reason.

Find the right starting point

Choose the situation that best describes your workload.

1. What problem are you trying to solve?

2. Where does the cluster run?

Recommended starting point

Select one option in each list to see your starting point.

Most live clusters use two autoscalers, one for pods and one for nodes.

‍

Horizontal Pod Autoscaler (HPA)

The Horizontal Pod Autoscaler adds or removes pods to keep a number, such as CPU use, close to a target you set. Every 15 seconds, it runs three steps:

  1. Observe: check the current CPU use, or another chosen number, for each pod.
  2. Compare: divide the average by the target to see how far off it is.
  3. Act: set desiredReplicas = ceil(currentReplicas × currentValue / targetValue).

For example, four pods target 50% CPU but run at 80%. The HPA multiplies 4 by 80/50, rounds 6.4 up, and scales to seven pods. It ignores changes within 10% of the target, so small swings never trigger scaling.

Scale-up starts within one or two 15-second checks, while scale-down waits five minutes so a short dip keeps its pods. Each new pod also needs a node with free space. On Rackspace Spot, you can set one node pool to autoscale, so the cluster adds nodes when pods cannot be scheduled and removes them once they sit unused.

‍

HPA configuration deep dive

This Kubernetes autoscaling YAML example targets a Deployment named web:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
  behavior:
    scaleDown:
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60

You tune four fields most often:

  • minReplicas sets the lowest pod count. With two pods, the app stays up even while one node shuts down for maintenance.
  • averageUtilization sets the CPU target. Apps that must respond fast, such as APIs, usually aim for 50 to 60%. Background jobs can run at 80%.
  • behavior.scaleDown.policies removes at most half the pods each minute, so the HPA does not cut pods it needs again a few minutes later.
  • tolerance changes the 10% range the HPA ignores, with separate values for scaling up and down. It is stable from Kubernetes v1.37.

‍

Vertical Pod Autoscaler (VPA)

Some apps cannot simply run more copies, such as a database that runs as a single instance. The Vertical Pod Autoscaler helps these apps by giving each pod more or less CPU and memory, based on how much it used in the past. Its update mode decides how it applies each change:

  • Off suggests new sizes and changes nothing.
  • Initial applies new sizes only when a pod starts.
  • Recreate restarts a pod at the new size when its current size is far off.
  • Auto behaves like Recreate and is deprecated.
  • InPlaceOrRecreate, generally available since VPA 1.6.0, resizes a pod while it keeps running and restarts it only if that fails.

Restarts are the main downside of VPA. A restarted pod drops open connections and loses data it held in memory. Before you turn on Recreate, set a PodDisruptionBudget, which limits how many pods can restart at the same time.

‍

VPA configuration and tuning

Start in recommendation-only mode:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: db
spec:
  targetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: db
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed: {cpu: 100m, memory: 256Mi}
        maxAllowed: {cpu: 2, memory: 4Gi}

minAllowed and maxAllowed set a lower and upper limit, so one unusual week cannot shrink a pod too far or grow it bigger than a node. Run kubectl describe vpa db after a week to read the suggested sizes.

Do not let VPA and the HPA both act on CPU for the same app. If VPA gives each pod more CPU, each pod's usage percentage falls, and the HPA removes pods the app still needs. To use both, have the HPA scale on a different number, such as requests per second.

‍

Cluster Autoscaler

Pod autoscalers only work when the cluster has room for new pods. The Cluster Autoscaler adds nodes when it runs out. Every 10 seconds, it looks for pods stuck in Pending because no node has space. It then works out which node group could fit them and adds a node to that group. A node group is a set of identical servers that grow and shrink together.

Removing nodes works the other way. If the pods on a node ask for less than half of its capacity for 10 minutes, and they can move to other nodes, the autoscaler moves them and shuts the node down. It goes by what pods ask for, which can differ from what they actually use.

Karpenter is an open-source alternative that skips node groups. It reads what the waiting pods need and starts a server of the right size, often in under a minute.

‍

Cluster Autoscaler configuration and behavior

Four flags control most of its behavior:

FlagDefaultEffect
--scale-down-unneeded-time10mHow long a node stays underused before removal
--scale-down-delay-after-add10mHow long to wait after adding a node before removing any
--scale-down-utilization-threshold0.5Below this share of requested capacity, a node counts as underused
--max-node-provision-time15mHow long to wait for a new node before trying another group

‍

KEDA: Event-driven and scale-to-zero autoscaling

CPU shows that a pod is busy. It cannot show 40,000 messages waiting for pods that do not exist yet. KEDA, an open-source project, scales on events like these. Version 2.21 includes 80 scalers, which are ready-made connectors for sources such as Apache Kafka, RabbitMQ, Prometheus, and time-based schedules (Cron).

KEDA works alongside the HPA in three stages:

  1. Activation: every 30 seconds, KEDA checks the event source. When work appears, it starts the first pod.
  2. Scaling: KEDA creates an HPA behind the scenes and passes it the event count. The HPA then adds any pods beyond the first.
  3. Cooldown: after five minutes with no events, KEDA scales the app back to zero pods.

A ScaledObject is the KEDA setting that links an app to its event source:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: orders-worker
spec:
  scaleTargetRef:
    name: orders-worker
  minReplicaCount: 0
  maxReplicaCount: 50
  triggers:
    - type: rabbitmq
      metadata:
        queueName: orders
        mode: QueueLength
        value: "20"
      authenticationRef:
        name: rabbitmq-auth

KEDA targets 20 messages per replica, so 400 waiting messages produce 20 workers, and an empty queue produces none.

Only event or object metrics can take a workload to zeroWorkload should run zero pods when idleIs the load signalCPU or memory?yesZero is not possibleCPU and memory are onlymeasured on running pods.Keep at least one replica.no, event or object metricKubernetes v1.37+ andan external metricsadapter for the signal?yesHPA, minReplicas: 0Beta since v1.37,enabled by default.Object or external only.noKEDA ScaledObject, minReplicaCount: 0Any supported Kubernetes version, 80 event scalers.Wakes on the first event, back to zero after 300 idle s.Sources: Kubernetes HPA documentation (v1.37) and KEDA ScaledObject specification (v2.21)
Scale-to-zero decision flow: HPA vs. KEDA

The HPA can also scale to zero in Kubernetes v1.37 as a beta feature, but never on CPU or memory. KEDA scales to zero on any supported version and can start a one-off Job for each batch of events.

Autoscaling strategies, policies, and best practices

Designing effective autoscaling strategies

Pick the combination that fits each kind of app:

  • Stateless services, such as web front ends that keep no data between requests, pair the HPA or KEDA with the Cluster Autoscaler.
  • Single-instance services, such as a database, pair VPA with the Cluster Autoscaler.
  • Event-driven workers run KEDA from zero, and the Cluster Autoscaler removes the nodes they leave empty.

Base each target on the response time you promise users, often written into a service level agreement (SLA). Run a load test to find the CPU level where responses get too slow, then set the HPA target 15 points lower. If responses slow down at 75% CPU, for example, target 60%.

For predictable peaks, such as a 9 a.m. login rush, use KEDA's Cron scaler to add pods before the rush begins.

‍

Resource management for optimal autoscaling

Every autoscaler in this guide depends on resource requests, the CPU and memory each container says it needs. The HPA compares actual use against them, VPA changes them, and the Cluster Autoscaler adds them up to decide whether a node is full. If the requests are wrong, all three make wrong decisions.

A common mistake is asking for too much. Pods then look less busy than they are, so the HPA scales late, and nodes fill up with reserved capacity that nothing uses. Run VPA in Off mode to see what pods really use, then set requests from its suggestions. Our guide to Kubernetes resource optimization covers request sizing in depth.

‍

Cost optimization through autoscaling

Autoscaling saves money when it removes nodes, because nodes are what you pay for. Three settings lower the bill:

  • Lower floors: drop minReplicas and node group minimums in test environments outside working hours.
  • Faster scale-down: shorten --scale-down-unneeded-time on clusters that run batch jobs, so idle nodes go away sooner.
  • Cheaper capacity: run work that can restart safely on spot nodes. They cost less because the provider can take them back.

Combining the Cluster Autoscaler with spot nodes saves money twice. The autoscaler removes nodes you do not need, and each node it keeps costs less. On Rackspace Spot, for example, you autoscale a spot node pool by setting minimum and maximum node counts on a bid.

Track cost per request instead of total spend. Total spend rises whenever traffic grows, even when autoscaling works well.

‍

Autoscaling best practices

Confirm each item before you rely on an autoscaler in production:

  • Every container sets CPU and memory requests.
  • You load-tested your targets in a test environment.
  • Each namespace has a ResourceQuota, so one team cannot consume the cluster.
  • HPA and VPA never act on the same metric for one workload.
  • PodDisruptionBudgets limit how many pods of each app can restart at once.
  • Autoscaling settings live in version control, such as Git, with a note explaining each target.

Monitoring, troubleshooting, and autoscaling limitations

Monitoring Kubernetes autoscaling in production

Start with the status each autoscaler reports:

kubectl describe hpa web
kubectl describe vpa db
kubectl -n kube-system logs deploy/cluster-autoscaler
kubectl get events --field-selector reason=TriggeredScaleUp

To spot trends over time, chart pod counts, node counts, and CPU use in a tool such as Prometheus with Grafana. Set alerts for three warning signs:

  • An HPA stuck at maxReplicas for 15 minutes, which means it wants more pods than you allow
  • Pods stuck in Pending for five minutes
  • Failed attempts by the Cluster Autoscaler to add nodes

‍

Troubleshooting common autoscaling issues

Match the symptom to its fix:

SymptomLikely causeFix
HPA targets show <unknown>Metrics Server down, or pods lack CPU requestsCheck kubectl top pods, add requests
Scaling lags a spikeSlow pod startup or node provisioningRaise minReplicas, shorten startup
Pods restart every few hoursVPA Recreate evicting on new recommendationsNarrow bounds or use InPlaceOrRecreate
Pods stay Pending, no new nodesNode group at maximum or cloud quota reachedRead autoscaler logs, raise the limit
Replica count swingsHPA and VPA both acting on CPUMove the HPA to an external metric
Nodes never scale downLocal storage, unmanaged pods, or strict PodDisruptionBudgetsAdd safe-to-evict annotations where safe

‍

Known autoscaling limitations

Some limits remain no matter how you configure things:

  • Metrics delay: Metrics Server reads usage every 15 seconds, and the HPA checks every 15 seconds, so up to 30 seconds can pass before the HPA asks for a new pod.
  • Indirect signals: CPU and memory show how hard a pod works, which can differ from how many users are waiting. A pod stuck waiting on a slow database uses little CPU, so it looks idle while requests pile up.
  • Local storage: by default, the Cluster Autoscaler keeps any node running a pod that stores data on that node's own disk, so clusters with many such pods shrink slowly.
  • Bursty traffic: a spike that ends faster than a new pod can start is over before the extra capacity arrives.

Implementation guide for Kubernetes autoscaling

Planning your autoscaling implementation

Before you change anything, record a week of normal CPU use, memory use, request rate, and queue length for each app. Run each app through the selector, then write down how fast it must respond and how much it may cost.

‍

Step-by-step autoscaling configuration

This Kubernetes autoscaling tutorial sets things up in order, because each step relies on the one before it:

  1. Install Metrics Server: apply its components.yaml from the latest release, then confirm kubectl top nodes returns data.
  2. Add the HPA: apply the web example from this guide and check that its targets show percentages.
  3. Add VPA for memory-intensive workloads: install VPA, apply the db manifest in Off mode, and review recommendations after a week.
  4. Enable the Cluster Autoscaler: turn it on for each node pool through your provider's console or command-line tool, and set a minimum and maximum node count.
  5. Validate with load: send test traffic with a load-testing tool such as k6, then watch kubectl get hpa web --watch and the node count.

‍

Scaling autoscaling across large and complex clusters

When many teams share one cluster, give autoscaling the same rules and reviews as any other setting:

  • Templates: put tested HPA and VPA settings in Helm charts, which are reusable app packages, so every team starts from the same defaults.
  • GitOps: store the settings in Git and apply them with Argo CD or Flux, so someone reviews every change.
  • Policy enforcement: use Kyverno or OPA Gatekeeper to reject pods without resource requests.

Frequently asked questions

What is autoscaling in Kubernetes?

Kubernetes autoscaling adds or removes capacity automatically as demand changes. The HPA changes how many pods run, VPA changes each pod's CPU and memory, the Cluster Autoscaler adds and removes nodes, and KEDA scales apps on outside events, even down to zero.

‍

How do you scale pods in Kubernetes?

Run kubectl scale deployment web --replicas=5 for a one-time change. For automatic scaling, create a HorizontalPodAutoscaler with kubectl autoscale or a YAML manifest. The HPA then adjusts the replica count every 15 seconds against your target.

‍

What are the different types of Kubernetes autoscaling?

Four types cover most clusters: the Horizontal Pod Autoscaler for replicas, the Vertical Pod Autoscaler for per-pod resources, the Cluster Autoscaler for nodes, and KEDA for event-driven scaling. Karpenter is a faster alternative to the Cluster Autoscaler.

‍

Can HPA scale to zero?

Yes, with limits. Kubernetes v1.37 enables minReplicas: 0 by default as a beta feature, but only for HPAs that scale on object or external metrics. CPU and memory cannot trigger it. KEDA scales to zero on any supported version.

‍

Do managed Kubernetes services support autoscaling?

Yes. The HPA and VPA run on any standard Kubernetes cluster, managed or not. For nodes, most managed services offer a built-in Cluster Autoscaler that you switch on per node pool. Rackspace Spot includes one in every cluster at no extra cost.

Choosing your Kubernetes autoscaling stack

Each autoscaler covers one part of the cluster, but nodes are what you pay for. Autoscaling cuts how many nodes you run, and the price of each node sets how low the bill can go.

Rackspace Spot prices nodes through an open-market auction. You bid for servers, bids start at $0.001 an hour, and prices rise only when more people bid for the same capacity. The managed control plane, the part of Kubernetes that runs the cluster itself, costs nothing extra, and a working cluster starts at $7.20 a month.

Its built-in autoscaler is the same Cluster Autoscaler covered above, run for you, and one bid per cluster can autoscale at a time. A spot node can be taken back when someone outbids you, so spot capacity suits work that can restart safely, such as queue workers, batch jobs, and build and test runners.

Pick your pair from the selector, apply the manifests in this guide, and get started with Rackspace Spot to watch them scale on real nodes.