How to Configure Kubernetes HPA Autoscaling
A HorizontalPodAutoscaler watches your Deployment's CPU (or memory) usage and adjusts the replica count automatically - scale out under load, scale back in when traffic drops. Here's how to write and apply one with autoscaling/v2.
What an HPA does
The HorizontalPodAutoscaler controller runs in the cluster control plane and polls the
metrics server every 15 seconds. When average CPU utilization across the Deployment's pods
rises above your target, it increases the replica count up to maxReplicas. When utilization
falls, it scales back down - after a stabilization window - to avoid thrashing.
The autoscaling/v2 API (stable since Kubernetes 1.26) supports multiple metric types in a
single object: Resource (CPU, memory), Pods, Object, and External. Use v2 for
any new HPA - v1 only supports CPU and is kept for backwards compatibility.
Write the manifest
Create /workspace/hpa.yaml:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
averageUtilization: 70 means the HPA targets 70% of each pod's CPU request. If pods
are on average at 140% (double the target), the controller doubles the replica count.
Apply and verify
kubectl apply -f /workspace/hpa.yaml
kubectl get hpa
kubectl describe hpa api
The kubectl get hpa output shows current vs target utilization and the live replica count:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
api Deployment/api 18%/70% 2 10 2 30s
<unknown>/70% in TARGETS is normal for the first minute - the metrics server needs a
scrape cycle to collect data. Wait 60 seconds and re-run.
Tuning scale-down behavior
By default the HPA won't scale below your target for 5 minutes after a spike. You can
tighten or loosen this with behavior:
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 120 # wait 2 min before scaling in
policies:
- type: Percent
value: 25
periodSeconds: 60 # remove at most 25% of pods per minute
Slower scale-in protects services with long-lived connections (databases, WebSockets) from losing capacity before those connections drain.
Adding memory or custom metrics
Append more entries to metrics: to target multiple signals simultaneously:
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
The HPA scales to satisfy the most demanding metric - the replica count is the maximum of what each metric independently requires.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Writing an autoscaling/v2 HPA manifest targeting a Deployment with CPU utilization
- Applying and inspecting an HPA with kubectl get hpa and kubectl describe hpa
- Tuning scale-down behavior with stabilizationWindowSeconds and scaleDown policies
FAQ
What is the difference between autoscaling/v1 and autoscaling/v2?
autoscaling/v1 only supports CPU-based scaling. autoscaling/v2 (stable since Kubernetes 1.26) supports multiple metric types - CPU, memory, custom Pods metrics, Object metrics, and External metrics - all in one HPA object. Use v2 for any new HPA.
Why does kubectl get hpa show <unknown> for TARGETS?
The metrics server needs one or two scrape cycles (about 60 seconds) after the HPA is created before it has data. If <unknown> persists beyond a few minutes, check that metrics-server is running with kubectl get pods -n kube-system and that your pods have CPU requests set.
How do I prevent an HPA from scaling in too aggressively?
Set spec.behavior.scaleDown.stabilizationWindowSeconds to a higher value (default is 300 seconds) and add a scaleDown policy that limits how many pods can be removed per interval. This is important for services with long-lived connections or slow startup.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →