A lot of "our Kubernetes autoscaling isn't working" tickets come down to one misunderstanding: HPA and the cluster autoscaler solve different problems, and turning on one without the other only gets you halfway.
HPA scales pods, within the nodes you have
The Horizontal Pod Autoscaler watches a metric — CPU, memory, or a custom metric from Prometheus — and adds or removes pod replicas to keep it near a target. If CPU usage climbs past 70%, HPA adds replicas.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70This works right up until your existing nodes run out of room to schedule the new pods. Then those pods sit in Pending, and nothing about HPA fixes that — it's not HPA's job.
Cluster autoscaler scales nodes, to fit the pods you have
The cluster autoscaler watches for Pending pods that can't be scheduled due to insufficient resources, and adds nodes to the underlying node group (or removes them when nodes sit underutilized). It doesn't know or care what your application-level scaling policy is — it just reacts to scheduling pressure.
Why you need both
HPA without cluster autoscaler: pods try to scale up, run out of node capacity, and stall in Pending during your traffic spike — the exact moment you needed the extra capacity.
Cluster autoscaler without HPA: your node count grows and shrinks with resource pressure, but pod count is static, so you're not actually responding to load — you're just paying for infrastructure that isn't doing more work.
Run both, and set resource requests on every pod honestly (not padded "to be safe") — the cluster autoscaler's scheduling decisions are only as good as the requests you've declared. This is one of the first things we check in a Kubernetes management engagement — mismatched requests/limits are the single most common cause of surprise cloud bills we see.