Back to Blog
Architecture10 min read2026-07-07

Horizontal Pod Autoscaler (HPA) Explained

The HPA adds and removes pod replicas to match load. Understand its control loop, the scaling algorithm, why metrics and requests matter, and where it stops — so you can scale safely.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Scaling out, not up

When a service gets busy, you have two options: make each instance bigger (vertical scaling) or run more instances (horizontal scaling). Horizontal scaling is the cloud-native default — it's more resilient (no single big point of failure), can scale further, and works without restarting workloads. In Kubernetes, the Horizontal Pod Autoscaler (HPA) automates it: it watches a metric and adjusts the number of pod replicas to match demand.

How the control loop works

The HPA is a controller that runs a loop, by default about every 15 seconds:

  1. 1Observe the current value of the target metric across the pods (e.g. average CPU utilization).
  2. 2Compute the desired replica count to bring the metric to its target.
  3. 3Act by scaling the Deployment/ReplicaSet up or down.

The core formula is simple:

desiredReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue))

Example: 4 replicas averaging 80% CPU, target 50%. ceil(4 * (80/50)) = ceil(6.4) = 7 replicas. The HPA scales to 7.

Resource requests are not optional

This trips people up constantly: CPU/memory-based HPA scales on utilization relative to the pod's resource *request*, not absolute usage. If your pods have no CPU request set, percentage-based autoscaling has no denominator and won't work correctly.

resources:
  requests:
    cpu: 250m      # HPA target % is measured against THIS
    memory: 256Mi

Set sensible requests, or the HPA's math is meaningless.

A basic HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60

Keep average CPU near 60%; never below 2 replicas, never above 20.

Beyond CPU: custom and external metrics

The autoscaling/v2 API lets you scale on more than CPU/memory:

Metric typeSourceExample
ResourceBuilt-inCPU, memory utilization
PodsCustom metrics adapterrequests/sec per pod
ObjectCustom metrics adapteringress request rate
ExternalExternal metrics adapterqueue depth, lag

For Pods/Object you need a metrics adapter (e.g. Prometheus Adapter) exposing the custom metric. For external event sources (queues, Kafka lag) most teams use KEDA, which feeds external metrics into an HPA *and* adds the one thing HPA can't do natively — scale to zero.

Avoiding thrash: stabilization and behavior

Naive autoscaling flaps — scaling up and down repeatedly around a threshold, churning pods. Modern HPA has a behavior section to tame it:

  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300   # wait 5 min before scaling down
      policies:
        - type: Percent
          value: 50          # remove at most 50% of pods per step
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0     # scale up promptly
      policies:
        - type: Percent
          value: 100         # can double quickly under load
          periodSeconds: 30

The usual pattern: scale up fast, scale down slow. You'd rather over-provision briefly than drop capacity during a temporary dip.

What the HPA does *not* do

Knowing the boundaries prevents misuse:

  • It doesn't change pod size. That's the Vertical Pod Autoscaler (VPA) — and you generally shouldn't run VPA and HPA on the same CPU/memory metric (they fight).
  • It doesn't add nodes. If there's no room to schedule new pods, they sit Pending. You need the Cluster Autoscaler (or Karpenter) to add nodes.
  • It doesn't scale to zero. Minimum is 1 (use KEDA for zero).

The full autoscaling story is HPA (replicas) + Cluster Autoscaler (nodes), and KEDA when you need event-driven or scale-to-zero behavior.

How a platform applies this

You shouldn't have to author HPA YAML and tune behavior windows for every app. On PandaStack, apps deploy via Helm to multi-region GKE with autoscaling configured as part of the release — the HPA scales your app's replicas against its compute tier's resource requests. Compute tiers range from Free (0.25 CPU/512MB) up to C2-2XCompute (8 CPU/16GB), so the request values the HPA measures against come from the tier you pick. For free-tier apps that need to go all the way to zero when idle, the platform layers KEDA scale-to-zero on top — the capability plain HPA lacks.

The takeaway: HPA handles the 1→N elasticity, KEDA handles the 0↔1 idle case, and the platform wires both so you just choose a tier.

Practical tips

  • Always set resource requests — HPA depends on them.
  • Pick a realistic target (e.g. 60–70% CPU) to leave headroom for spikes during scale-up lag.
  • Tune scale-down stabilization to avoid thrash.
  • Pair with Cluster Autoscaler so pods aren't left Pending.
  • Don't HPA and VPA the same metric.
  • Load test to confirm the metric you scale on actually correlates with user-facing load.

References

  • [Kubernetes HPA documentation](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)
  • [HPA walkthrough](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/)
  • [Kubernetes resource requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)
  • [Cluster Autoscaler FAQ](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md)
  • [KEDA documentation](https://keda.sh/docs/)

---

Want autoscaling configured for you — HPA elasticity plus scale-to-zero when idle? PandaStack wires both into every deploy. Pick a compute tier and push code at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Architecture

Browse all Architecture articles →

See also