Autoscaling Basics
This page is a hands-on intro to Kubernetes autoscaling: scaling pods out (horizontal), scaling pods up (vertical), and scaling nodes (cluster).
Each example builds on realistic manifests and kubectl commands you can run against any conformant cluster.
Prerequisites
- Kubernetes 1.36.2 cluster with
kubectlconfigured against it. - metrics-server installed (required for CPU/memory HPA and
kubectl top). - A workload with resource requests set - autoscaling math depends on them.
- Optional: Helm 3 for installing add-ons like VPA or KEDA.
Quick check that metrics are flowing:
kubectl top nodes
kubectl top pods -ABasic Examples
1. Set resource requests (the foundation)
Autoscaling and scheduling both read requests, so set them on every container.
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mirequestsis what the scheduler reserves and what the HPA measures against.250mmeans a quarter of a CPU core.- Omitting a CPU limit avoids throttling while still reserving a floor.
- A memory limit prevents a leak from taking down the node.
- Without a CPU request, a CPU-based HPA has no denominator and will not work.
2. Manual horizontal scaling
Before automating, scale by hand to confirm the workload spreads.
kubectl scale deployment web --replicas=4
kubectl get pods -l app=webscalesets the replica count directly on the Deployment.- Replicas land on any node with room for their requests.
- This is the same lever the HPA pulls automatically.
- Use it to sanity-check that traffic actually distributes across pods.
3. A CPU-based HPA
Let the HPA hold average CPU near a target.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60averageUtilization: 60targets 60% of each pod's CPU request.- The controller keeps replicas between
minReplicasandmaxReplicas. autoscaling/v2is the stable API for multi-metric and custom metrics.- Utilization is a percentage of request, not of node capacity.
4. Watch the HPA react
Observe decisions rather than guessing.
kubectl get hpa web --watch
kubectl describe hpa webget hpashows current versus target and the live replica count.describeprints events explaining each scale action.- A
<unknown>metric usually means metrics-server is missing or requests are unset. - Scaling up is quick; scaling down waits out a stabilization window.
5. Generate load to trigger scaling
Prove the loop end to end.
kubectl run load --rm -it --image=busybox:1.36 -- \
/bin/sh -c "while true; do wget -q -O- http://web; done"- This pod hammers the
webService to push CPU up. - Within a minute the HPA should raise replicas.
- Deleting the load pod lets utilization fall and triggers scale-down.
- Use it only in non-production namespaces.
6. See node-level pressure
Cluster scaling reacts to pods that cannot be placed.
kubectl get pods -A --field-selector=status.phase=Pending
kubectl describe pod <pending-pod>Pendingwith anInsufficient cpu/memoryevent means the cluster is full.- This is the signal the Cluster Autoscaler acts on.
- More nodes, not more replicas, resolve it.
- Right-sized requests reduce how often this happens.
7. Read a VPA recommendation
Let the VPA suggest better requests without changing anything yet.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Off"updateMode: "Off"produces recommendations only, applying nothing.- Read them with
kubectl describe vpa web. - This is the safe way to right-size before enabling automatic updates.
- The VPA add-on must be installed for this API to exist.
Intermediate Examples
8. Scale on a custom metric
Move beyond CPU to requests-per-second or queue depth.
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"type: Podsaverages a per-pod metric across replicas.- The metric must be exposed through a custom metrics adapter.
AverageValuecompares an absolute number, not a percentage.- This suits throughput-bound services where CPU lags the real signal.
9. Tune scale-down behavior
Stop flapping by slowing down scale-down.
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60- The stabilization window makes the HPA wait before removing replicas.
- The percent policy caps how fast it can shrink.
- Fast scale-up with slow scale-down is a common, safe default.
- Aggressive scale-down risks dropping capacity right before the next spike.
10. Protect availability during scaling
Guard against too many pods leaving at once.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web- A PDB limits voluntary disruptions like node drains.
minAvailable: 2keeps at least two pods serving during consolidation.- Cluster scale-down and node upgrades both respect it.
- Without a PDB, a drain can briefly zero out your service.
11. Cap replicas against real node capacity
Make sure the HPA maximum is something your cluster can actually place.
kubectl describe hpa web | grep -i replicas
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu- Compare
maxReplicastimes the per-pod request against total allocatable CPU. - A maximum the nodes cannot back leaves pods
Pendingduring a spike. - Pair a high maximum with the Cluster Autoscaler so nodes appear on demand.
- Right-sizing requests here keeps the ceiling honest and cost predictable.
12. Confirm the HPA owns the replica count
Once an HPA manages a Deployment, stop scaling it by hand.
kubectl get deployment web -o jsonpath='{.spec.replicas}{"\n"}'
kubectl get hpa web -o jsonpath='{.status.desiredReplicas}{"\n"}'- The HPA continuously overwrites manual
kubectl scalechanges. - Comparing the two values confirms the HPA is in control.
- Manage the floor and ceiling through
minReplicasandmaxReplicasinstead. - Leave replica edits to the autoscaler to avoid a tug-of-war.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).