Scale out with the Horizontal Pod Autoscaler
Every cluster created with Syself Autopilot runs the metrics-server , so a Horizontal Pod Autoscaler that scales on CPU or memory works the moment you apply it, with no need to install additional tools. It watches live usage, compares it to a target you set, and moves the replica count until the two line up.
How it decides#
The math is a ratio. If your pods average 80% CPU against a 50% target, the HPA figures it needs roughly 80/50 (that is, 1.6) times the replicas it has now, and it adds them. When load drops, it works the same ratio in reverse.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef: # the workload whose replica count the HPA owns
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2 # floor: 2+ so a node drain never leaves you at zero
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # target is a % of the CPU request, not the node
Important
A CPU or memory target is a percentage of the request, not of the node. If a container has no CPU request, "60% of CPU" has no denominator and the HPA simply cannot scale it. Set requests before you use resource-based HPAs.
Min, max, and stabilization#
minReplicas and maxReplicas set the range the HPA moves within. Between them sits stabilization, which keeps the replica count from flapping: the HPA scales up fast, but by default waits 5 minutes of sustained low load before scaling down, so a short drop in usage does not shed capacity you will want back a minute later. Tune behavior.scaleUp and behavior.scaleDown when the defaults do not fit your traffic shape.
Your HPA during a node roll#
minReplicas: 1 is a common mistake on this platform. Nodes here can get drained and replaced constantly. Every Kubernetes upgrade rolls them out, and self-healing replaces any node that goes unhealthy. When a node drains, its pods are evicted with up to the 180-second default drain window to reschedule elsewhere. A single-replica workload has no second copy to carry traffic across that gap, so it can sit at zero running pods mid-drain even though the HPA is behaving exactly as configured. Keep minReplicas at 2 or higher for anything that has to stay reachable.
Beyond CPU and memory#
CPU and memory are not the only signals an autoscaling/v2 HPA can read. Point it at a custom metric from your own application, or an external one like queue depth: install an adapter that publishes the value to the Kubernetes metrics API, then reference it under metrics the same way you would CPU. One limit stays fixed, though: the HPA only changes pod count, never node count. When it scales past what the current nodes hold, pair it with the cluster autoscaler .
Verify#
kubectl get hpa web shows current usage against your target under TARGETS: 35%/60% means it is reading metrics and has headroom. If you see <unknown>/60%, the HPA cannot read usage at all, almost always a missing CPU request or metrics-server not reaching the pod. Fix that first; a blind HPA will not scale.
Then prove the important case: cordon and drain the node running your workload by hand, and watch it. If minReplicas is 2 or more, traffic never notices. If it is 1, you will see the gap the next real upgrade would have shown you in production.
Reserve nodes with taints and tolerations
Custom node taints don't survive here, so keep general workloads off dedicated nodes with a dedicated pool, a label, and a nodeSelector instead.
Right-size with the Vertical Pod Autoscaler
Let the VPA recommend or apply the CPU and memory requests a workload actually needs instead of guessing.