Cluster Autoscaler vs Karpenter on EKS: scale-up speed, bin-packing, Spot handling, Auto Mode limits and a safe migration path, with v1 NodePool YAML.
A platform team moves an EKS cluster from Cluster Autoscaler to Karpenter and finds the node count barely changed. They are disappointed until they check utilisation: nodes now run noticeably fuller, because Karpenter chose instance types that fit the pending pods instead of dropping each pod into a fixed-size node group. The savings were inside the nodes, not in the count. (This is an illustrative scenario, not a measured benchmark.)
That is the practical difference between the two autoscalers. It is less about which adds nodes faster and more about how much capacity each one wastes while doing it.
Cluster Autoscaler scales pre-defined node groups, while Karpenter provisions individual instances directly from the cloud API based on what pending pods need. Both react to unschedulable pods, but they work from different menus.
With Cluster Autoscaler on EKS, you define Auto Scaling Groups, each with a fixed set of instance types, and the autoscaler changes their desired size. With Karpenter you define a NodePool of constraints, and it launches whichever instance type fits. Cluster Autoscaler and Karpenter are both Kubernetes SIG Autoscaling projects, not CNCF graduated projects, and Karpenter reached v1 (stable APIs) in 2024.
Karpenter usually brings capacity online faster and packs pods more tightly. It launches instances directly without waiting on an Auto Scaling Group, and often has a node ready within roughly a minute, while Cluster Autoscaler commonly takes a few minutes. Exact times depend on your AMI, bootstrap scripts and image pulls, so measure your own.
Packing matters more than speed for cost. If pending pods need 2.5 vCPU and the only node group uses 4 vCPU instances, Cluster Autoscaler adds a 4 vCPU node and leaves 1.5 vCPU idle. Karpenter can choose a smaller or differently shaped instance. It also continuously consolidates, removing empty nodes and replacing underused ones with cheaper capacity, which Cluster Autoscaler does only in a limited scale-down sense.
Define a NodePool with explicit instance constraints, limits and disruption controls, plus an EC2NodeClass that selects subnets, security groups and AMI. Constraints keep Karpenter from picking odd instance types, and limits cap your spend.
apiVersion: karpenter.sh/v1kind: NodePoolmetadata: name: generalspec: template: spec: nodeClassRef: group: karpenter.k8s.aws kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: karpenter.k8s.aws/instance-category operator: In values: ["c", "m", "r"] - key: karpenter.k8s.aws/instance-generation operator: Gt values: ["4"] expireAfter: 720h limits: cpu: "1000" disruption: consolidationPolicy: WhenEmptyOrUnderutilized consolidateAfter: 1m budgets: - nodes: "10%"apiVersion: karpenter.k8s.aws/v1kind: EC2NodeClassmetadata: name: defaultspec: role: KarpenterNodeRole-prod-eks amiSelectorTerms: - alias: al2023@v20250101 subnetSelectorTerms: - tags: karpenter.sh/discovery: prod-eks securityGroupSelectorTerms: - tags: karpenter.sh/discovery: prod-eksPin the AMI alias to a tested version in production, because @latest rolls new AMIs through your fleet automatically. Replace the version above with a current one. Run the Karpenter controller itself on a small managed node group or Fargate, since it cannot provision the node it runs on. Spot interruptions are handled through an SQS interruption queue that Karpenter watches.
Tag your Auto Scaling Groups for discovery, then set sensible expander and balancing flags. The autoscaler can only choose among the instance types already in those groups.
## container args in the cluster-autoscaler Deploymentcommand: - ./cluster-autoscaler - --cloud-provider=aws - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/prod-eks - --expander=least-waste - --balance-similar-node-groups - --skip-nodes-with-local-storage=falseThe least-waste expander picks the group that leaves the least idle capacity, which partly offsets fixed node sizes. Using mixed instance types in each group improves Spot availability, but every type should have similar vCPU and memory so scaling decisions stay accurate.
EKS Auto Mode runs a managed Karpenter-based capability for you, so you do not install, scale or upgrade the controller. AWS also manages node AMIs and lifecycle, which removes much of the work that made Cluster Autoscaler the low-maintenance option.
The constraints matter before you adopt it. Nodes run an AWS-managed OS, you cannot SSH into them, nodes are replaced on a maximum lifetime of 21 days, and AWS charges a management fee on top of EC2 pricing, so check the current rate. Teams that need a custom AMI, host access for debugging or tight control over per-instance cost should run Karpenter themselves.
Stay on Cluster Autoscaler when you span several clouds, run it on GKE, or already have a stable setup with no pain point. It has long-standing support across AWS, GCP and Azure, and its behaviour is well understood.
Check the alternatives for each cloud first. AKS offers Node Auto Provisioning built on Karpenter, and GKE has its own node auto-provisioning and Autopilot. Karpenter support on other clouds is still maturing, so verify provider maturity at the time you decide. Stable GPU or machine-learning pools with fixed, pre-sized nodes also suit Cluster Autoscaler well, because they do not benefit from dynamic instance selection.
Both autoscalers size nodes from pod requests, not actual usage, so inflated requests waste money on either one. If a service requests 2 CPUs and uses 0.2, the cluster reserves 10 times what it needs, and no node autoscaler can see past that.
Compare requests with usage before you promise finance any savings:
## requested CPU, by namespacesum by (namespace) ( kube_pod_container_resource_requests{resource="cpu"}) ## used CPU, by namespacesum by (namespace) ( rate(container_cpu_usage_seconds_total{container!=""}[5m]))Use the Vertical Pod Autoscaler in recommendation mode, or a tool such as Goldilocks, to propose new requests. Right-sizing requests and then adding Karpenter's consolidation is a stronger combination than either on its own. Published benchmark savings vary widely, so treat any single percentage as marketing until you reproduce it on your own workloads.
Run both side by side with separate capacity, then drain the old node groups gradually. Karpenter manages only its own NodePool nodes, so the two can coexist as long as Cluster Autoscaler is not responsible for the same pods or nodes.
kubectl scale deploy cluster-autoscaler \ -n kube-system --replicas=0kubectl cordon ip-10-0-1-23.ec2.internalkubectl drain ip-10-0-1-23.ec2.internal \ --ignore-daemonsets --delete-emptydir-dataPlan for two to four weeks including testing, and rehearse the rollback in a staging cluster first.
| Factor | Cluster Autoscaler | Karpenter |
|---|---|---|
| Provisioning | Pre-defined node groups | Direct from cloud API |
| Scale-up | Minutes, group dependent | Often about a minute |
| Packing | Group-constrained | Flexible, with consolidation |
| Factor | Cluster Autoscaler | Karpenter |
|---|---|---|
| Cloud coverage | Mature on major clouds | AWS mature, others vary |
| Best fit | Multi-cloud, stable pools | AWS, variable workloads |
| Ops effort | Tune ASGs | Tune NodePools and budgets |
limits, and use disruption budgets so consolidation cannot drain too much at once.karpenter.sh/do-not-disrupt for jobs that must not be interrupted.References and Further Reading
- Karpenter Documentation - NodePool, EC2NodeClass and disruption reference
- Amazon EKS Autoscaling - Karpenter and Cluster Autoscaler on EKS
- Cluster Autoscaler - Flags, expanders and cloud providers
No. Karpenter's gain often shows up as higher utilisation per node, because it picks instance types that fit pending pods, not as fewer nodes. Compare CPU and memory utilisation, cost per workload and pending-pod time before and after, not node count alone.
Consolidation is working as configured. With the `WhenEmptyOrUnderutilized` policy Karpenter replaces nodes it can pack more cheaply. Slow it down with `consolidateAfter`, disruption budgets, PodDisruptionBudgets, and the `karpenter.sh/do-not-disrupt` annotation on fragile pods.
Partly. The core lives in the vendor-neutral `kubernetes-sigs/karpenter` project, AKS offers Node Auto Provisioning built on Karpenter, and community providers exist for other clouds. For GKE and multi-cloud estates, Cluster Autoscaler or the provider's own node autoscaling is usually the practical choice. Verify provider maturity before committing.
Node control. Auto Mode runs AWS-managed nodes with no SSH access, rotates them on a fixed maximum lifetime (21 days), and adds a per-instance management fee on top of EC2 pricing. In return AWS runs the controller, AMI updates and node lifecycle for you.
Pod requests usually dominate. Both autoscalers size nodes from what pods request, not what they use, so requests set far above real usage waste money on any autoscaler. Measure requested versus used CPU and memory per namespace before attributing savings to a migration.
Discussion0