Scaling and HPA¶
Assumes: Pods and Deployments and Resource Requests and Limits - HPA measures utilization against requests, so that page comes first.
Scaling in Kubernetes has three layers:
- workload scaling: change pod replicas
- node scaling: add or remove cluster nodes
- resource sizing: change CPU or memory requests per pod
This page focuses on workload scaling with Horizontal Pod Autoscaler (HPA).
Manual scaling¶
Manual scaling is still useful for planned events:
For live traffic variability, manual scaling does not react quickly enough.
How HPA works¶
HPA is a closed control loop driven by metrics:
flowchart LR
MS[Metrics Server\nor custom adapter] -->|current usage| HPA[HPA controller]
HPA -->|desired replicas| DEP[Deployment / StatefulSet]
DEP -->|pod metrics| MS
Loop steps:
- Read metrics for current pods (via Metrics API). The loop runs every 15 seconds by default.
- Compare current utilization to the configured target.
- Compute desired replica count:
desiredReplicas = ceil(currentReplicas × currentUtil / targetUtil). - Apply stabilization window to avoid oscillation.
- Update the target workload replica count.
Details of the algorithm that explain real-world behavior:
- Tolerance: if the ratio
currentUtil / targetUtilis within ±10% of 1.0, the HPA does nothing. This dead band prevents constant micro-adjustments. - Utilization is measured against
requests, not limits. A pod using 300m CPU with a 200m request is at 150% utilization - even if its limit is 2 cores. - Missing metrics are handled conservatively: pods without metrics count as 0% when deciding to scale up and 100% when deciding to scale down, and not-yet-ready pods are ignored for scale-up. The bias is always toward not making things worse.
- Multiple metrics: when several metrics are configured, the HPA computes a desired count for each and takes the highest. You cannot use a second metric to hold replicas down.
- Scale to zero isn't supported for CPU/memory targets -
minReplicasmust be at least 1 there. HPA can go tominReplicas: 0with anObjectorExternalmetric instead, but true scale-to-zero-when-idle behavior is still better served by KEDA or a request-driven activator.
Cluster Requirements¶
HPA is only as good as metric quality.
Required baseline:
- metrics pipeline available (
metrics-serverfor CPU or memory) - workload has realistic
resources.requests - readiness probes are configured so new pods enter traffic safely
If requests are missing, percentage-based resource targets become unreliable.
HPA example¶
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 12
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 20
periodSeconds: 60
This configuration scales up aggressively and scales down more cautiously to reduce flapping.
HPA troubleshooting¶
Common failure patterns:
Unknowntargets due to missing metrics pipeline- very slow response because pods have long startup times
- oscillation caused by too-tight thresholds and no stabilization
HPA, VPA, and node autoscaling¶
- HPA scales pod count horizontally.
- VPA (Vertical Pod Autoscaler) adjusts pod resource requests over time - do not use HPA and VPA together on the same CPU/memory signal; they will conflict. VPA is safe to combine with HPA when HPA uses custom/external metrics instead.
- Node autoscaler or Karpenter adds infrastructure capacity when pods cannot be scheduled.
Custom and external metrics¶
The built-in autoscaling/v2 HPA supports three metric types:
Resource: CPU or memory utilization against pod requests.Pods: custom per-pod metric from an adapter (e.g. requests per second).External: metric from an external system (e.g. queue depth from SQS or Kafka lag).
For event-driven scaling needs beyond what HPA covers natively, consider KEDA (Kubernetes Event-driven Autoscaling). KEDA extends HPA with out-of-the-box scalers for message queues, databases, HTTP traffic, and 70+ other sources - scaling to zero when idle is a key advantage.
Practical guidance¶
- Start with CPU utilization targets around 50 to 70 percent
- Tune using real production latency and error metrics, not only CPU
- Set sensible min and max replica limits to protect cost and stability
- Validate behavior with load tests before relying on autoscaling in production
Certification notes¶
kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=60creates a working HPA imperatively - the fastest exam path.- Remember the formula and that utilization is relative to requests. Scenario questions often hinge on a missing or wrong
resources.requests. kubectl get hpashowing<unknown>/60%means the metrics pipeline is broken or requests are unset - know both causes.
Summary¶
HPA is a control loop, not a magic switch. It works well when metrics are trustworthy, pod requests are accurate, and rollout health checks are disciplined.
Check Yourself¶
Your HPA reports <unknown>/70% and never scales. What are the two most likely causes?
Either metrics-server is not installed or not healthy, so no utilization data exists at all, or the target pods have no CPU requests set, leaving nothing to compute a percentage against. Both show up identically in kubectl describe hpa.
A pod uses 300m CPU and requests 200m. What utilization does the HPA see, and is that a problem?
150%. Utilization is measured against the request, not the limit, so under-requesting makes every pod look permanently overloaded and the HPA scales out to compensate. You end up paying for replicas to fix a sizing error.
Traffic spikes hard and the HPA adds replicas, but latency stays bad for two minutes. Why is autoscaling not instant?
The loop has latency at every stage: metrics are scraped on an interval, the HPA evaluates periodically, new pods must be scheduled, images pulled, and readiness probes passed before they receive traffic. HPA absorbs sustained load changes, not sudden bursts - headroom or a queue does that.
Why should you not run an HPA and a VPA in Auto mode against the same Deployment on CPU?
They fight. The VPA changes the CPU request, which is the denominator of the HPA's utilization calculation, so each one's action changes the other's input and the pair oscillates. Use VPA for memory or in recommendation mode, and let HPA own replica count.
Related Concepts¶
- Learning Paths - where this page sits in your track
- Pods and Deployments
- Resource Requests and Limits
- Kubernetes Troubleshooting
Beginner track - step 10 of 12. Next: Kubernetes API. Back to the track overview.