
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Your pods are stuck in Pending because the scheduler cannot find a node with enough CPU or memory, yet your cloud bill keeps climbing during quiet hours. This mismatch between resource demand and provisioned capacity is exactly what Cluster Autoscaler solves by dynamically adjusting node count. Understanding how Cluster Autoscaler works—and where it fails—is essential before you enable it in any production environment.
How does Cluster Autoscaler decide when to add or remove nodes?
Cluster Autoscaler operates on two independent control loops: scale-up and scale-down. Unlike the Horizontal Pod Autoscaler, which responds to CPU/memory metrics or custom metrics, Cluster Autoscaler watches the Kubernetes scheduler directly. It asks one question on each loop: "Are there pods that cannot be scheduled right now?" If yes, it calculates the minimum number of nodes needed to fit those pods and requests them from the cloud provider API.
Scale-up logic
When the scheduler marks a pod as Unschedulable with a condition like Insufficient cpu or Insufficient memory, Cluster Autoscaler simulates scheduling that pod onto hypothetical new nodes from your configured node groups. It selects the node group that can accommodate the pending pods most efficiently, respecting constraints like max nodes per group, availability zones, and instance types. On AWS EKS, this means calling the Auto Scaling Group API; on GKE, it calls the Managed Instance Group API; on Azure AKS, it uses the VMSS API.
Scale-down logic
Scale-down is more conservative. A node is considered underutilized only if its total requested resources (sum of all pod requests, not actual usage) fall below the --scale-down-utilization-threshold (default 0.5). Crucially, the node must remain underutilized for the entire --scale-down-delay-after-add (default 10 minutes) and --scale-down-unneeded-time (default 10 minutes) windows. This prevents flapping when workloads briefly dip. Before terminating a node, Cluster Autoscaler drains it gracefully, respecting PodDisruptionBudgets and evicting pods in priority order.
What is the difference between Cluster Autoscaler and HPA?
A common mistake I see in teams new to Kubernetes is conflating pod scaling with node scaling. These operate at fundamentally different layers and serve different purposes. Getting this wrong leads to either stranded pods or wasted spend.
| Dimension | Cluster Autoscaler | Horizontal Pod Autoscaler |
|---|---|---|
| Scope | Node-level infrastructure | Pod-level application replicas |
| Trigger | Unschedulable pods (scheduler events) | Metric thresholds (CPU, memory, custom) |
| Action | Add/remove cloud VM instances | Increase/decrease Deployment/StatefulSet replicas |
| Latency | Minutes (node boot + kubelet ready) | Seconds (pod creation only) |
| Cost impact | Direct (VM billing) | Indirect (more pods may trigger CA later) |
| Dependency | Cloud provider API access | Metrics Server availability |
In practice, you almost always need both. HPA scales your application within existing capacity; Cluster Autoscaler ensures that capacity exists. For example, during a flash sale, HPA might scale your Laravel API from 3 to 20 replicas. Once those 20 replicas exhaust current node resources, pending pods trigger Cluster Autoscaler to add three more nodes. When traffic subsides, HPA scales back to 3 replicas, and after the grace period, Cluster Autoscaler removes the extra nodes. Without proper resource requests and limits on your pods, neither system can make correct decisions.
How do you configure Cluster Autoscaler safely in production?
Default settings rarely suit production workloads. I have seen clusters oscillate wildly or fail to scale during critical events because teams skipped these tuning steps. Start with explicit resource requests on every pod—without them, Cluster Autoscaler cannot accurately simulate scheduling and will make poor scaling decisions.
- Set appropriate scale-down delays. For stateless web APIs,
--scale-down-unneeded-time=5mand--scale-down-delay-after-add=10mbalance responsiveness with stability. For batch or ML workloads with long startup times, increase both to 15–30 minutes to avoid killing nodes mid-job. - Define node group boundaries. Always set
--nodes=min:max:group-nameor use auto-discovery with tags. Never leave max unbounded in production; a misconfigured HPA combined with unlimited CA can spin up hundreds of nodes in minutes. Set max based on your budget ceiling and quota limits. - Enable Pod Disruption Budgets. Without PDBs, scale-down can terminate nodes running single-replica databases or critical services. Create PDBs for every non-trivial workload to ensure at least one replica remains available during drain operations.
- Use priority classes for critical workloads. Mark system components and business-critical pods with high PriorityClasses. During scale-down, lower-priority pods are evicted first. During scale-up failures, high-priority pods preempt lower ones, ensuring essential services get resources even when capacity is constrained.
- Monitor CA health explicitly. Deploy Prometheus rules for
cluster_autoscaler_unschedulable_pods_countandcluster_autoscaler_scaled_up_nodes_total. Alert if unschedulable pods persist beyond 5 minutes—that indicates CA cannot fulfill demand due to quota exhaustion, missing instance types, or misconfiguration. See the four golden signals of monitoring for framing saturation correctly.
# Example Helm values for EKS production cluster
autoscalingGroups:
- name: eks-ng-app-m5xlarge
minSize: 3
maxSize: 20
desiredCapacity: 5
extraArgs:
scale-down-unneeded-time: 5m
scale-down-delay-after-add: 10m
scale-down-utilization-threshold: "0.6"
skip-nodes-with-local-storage: true
skip-nodes-with-system-pods: true
balance-similar-node-groups: true
expander: priority
rbac:
create: true
pspEnabled: false
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi Why isn't Cluster Autoscaler scaling my nodes as expected?
Troubleshooting CA requires understanding its simulation-based approach. The most frequent issues I encounter stem from mismatches between what CA thinks fits and what the scheduler actually accepts.
Pods lack resource requests
If your Deployment omits resources.requests, CA treats the pod as requesting zero resources. It may believe a tiny node can fit ten such pods, request one node, and then watch all pods stay pending because the scheduler enforces default quotas or node allocatable reservations. Always define explicit CPU and memory requests. Refer to Kubernetes resource limits and requests for sizing guidance.
Node group constraints block scaling
Check your ASG/MIG/VMSS max size, vCPU quotas, and spot instance availability. CA logs Unable to scale up: max size reached or Quota exceeded when hitting these walls. On AWS, verify Service Quotas for EC2 instances in your region; on Azure, check subscription compute quotas. These are invisible to Kubernetes but fatal to autoscaling.
Taints and tolerations mismatch
If your node group has taints (e.g., nvidia.com/gpu=true:NoSchedule) but pending pods lack matching tolerations, CA will never consider that node group as a valid target. Conversely, if all nodes have taints and no untainted node group exists, general workloads will remain permanently pending. Audit taint/toleration pairs across node groups and workloads systematically.
Local storage or system pods block scale-down
By default, CA skips nodes with local storage volumes or system pods (kube-proxy, CNI agents). Enable --skip-nodes-with-local-storage=false only if you accept data loss risk, or migrate stateful workloads to PVCs. For system pods, use --skip-nodes-with-system-pods=false cautiously—ensure critical daemons have PDBs or run on dedicated node pools.
Should you use Cluster Autoscaler or Karpenter in 2026?
Karpenter has matured significantly since its CNCF graduation and now serves as the preferred node autoscaler for many greenfield EKS clusters. However, Cluster Autoscaler remains relevant, especially outside AWS or in regulated environments requiring proven audit trails.
Choose Cluster Autoscaler if you run on GKE/Azure/OpenStack, require extensive audit documentation for ISO 27001 or SOC 2, or manage heterogeneous node groups with strict placement rules. Choose Karpenter if you are AWS-native, prioritize provisioning speed for bursty AI/ML workloads, or want automatic instance type optimization without maintaining dozens of ASGs. Many organizations run both during migration periods, using Karpenter for new workloads and CA for legacy systems until full transition.
Making Cluster Autoscaler Work Reliably
Cluster Autoscaler explained properly means acknowledging it is a powerful but blunt instrument. It excels at ensuring pods eventually get scheduled, but it does not optimize for cost, bin packing, or provisioning speed out of the box. Treat it as one layer in your autoscaling stack, paired with HPA/VPA for application efficiency and strong observability for early failure detection. Before enabling it, validate resource requests across all workloads, set conservative scale-down timers, cap node group sizes, and establish alerts for sustained unschedulable pods. If you need help designing an autoscaling strategy that balances reliability, cost, and compliance for your specific workload profile, reach out to discuss your infrastructure.