HPA, VPA and Cluster Autoscaler
HPA adds Pods, VPA resizes them, Cluster Autoscaler adds nodes. Only one of the three ships with Kubernetes, and confusing them is why "we have autoscaling" so often means "Pods pile up Pending".
Cloud Native Architecture Guide 31 of 46 Beginner
- Kubernetes1.36.4
- containerdv2.2.6
- CNICalico v3.25.0
- metrics-serverv1beta1.metrics.k8s.io
- TimeAbout 13 min
- Reviewed22 August 2026
Written against the versions above. The interfaces below are versioned standards and change slowly; the implementations plugged into them change constantly. That is the point of them.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA1001 | 192.168.0.175 | Ubuntu 26.04 LTS | Control Plane Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE01 | 192.168.0.176 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE02 | 192.168.0.177 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE03 | 192.168.0.178 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- The HPA guide, so the difference between usage and requests is already clear.
-
What is installed, and what is not
Three autoscalers, three different jobs:
- HPA - more replicas of the same Pod. Built in, an API object, needs a metrics source.
- VPA - the same replica count, with bigger or smaller requests. Not built in. A separate project.
- Cluster Autoscaler - more nodes. Not built in, and it needs to talk to whatever provides your machines, so on bare metal there is usually nothing for it to call.
On this cluster: neither VPA nor Cluster Autoscaler exists. That is the normal state of a kubeadm cluster, and it has a direct consequence - if every node is full, an HPA scaling from 2 to 10 produces eight
PendingPods and no extra capacity. The HPA did its job; nothing was listening for the result.bash Example session kubectl --context cka1001 get deploy -A | grep -i -E 'cluster-autoscaler|vpa' || echo "no cluster-autoscaler and no VPA on this cluster"no cluster-autoscaler and no VPA on this clusterExpected resultConfirmation that neither is running.
Success conditionYou know which autoscalers your cluster actually has.
-
The metrics APIs, and which ones exist
Autoscaling reads metrics through aggregated APIs, and there are three:
metrics.k8s.io- CPU and memory, served by metrics-server. Present here.custom.metrics.k8s.io- anything from your own monitoring, usually via the Prometheus Adapter. Absent.external.metrics.k8s.io- metrics from outside the cluster entirely, like a queue depth. Absent.
Only the first is listed, which bounds what an HPA here can do: CPU and memory only. "Scale on requests per second" needs the second API, and installing it is a separate project decision - not a flag.
bash Example session kubectl --context cka1001 get apiservices | grep -E 'metrics|custom.metrics|external.metrics'v1beta1.metrics.k8s.io kube-system/metrics-server True 14hExpected resultJust
v1beta1.metrics.k8s.io, Available and backed by metrics-server.Success conditionYou can say which metric families are available to an HPA on this cluster.
Troubleshooting
The HPA scaled up but the new Pods are
Pending.Why: No node has room, and nothing adds nodes.
Fix:This is the missing Cluster Autoscaler.
kubectl describe podwill sayInsufficient cpuorInsufficient memory. Until nodes are added, the HPA maximum is a wish.HPA and VPA both configured, and replicas behave strangely.
Why: Both are changing inputs to the same decision - VPA moves requests, and HPA measures against requests.
Fix:Do not run both on CPU or memory for the same workload. VPA in
Offrecommendation mode alongside an HPA is safe and genuinely useful for sizing.An HPA on a custom metric reports
<unknown>regardless of requests.Why:
custom.metrics.k8s.iois not registered.Fix:
kubectl get apiservices | grep custom.metrics. If nothing is there, install an adapter; no amount of HPA configuration substitutes for it.