The Cluster Autoscaler solves node scaling while staying locked into a structural constraint: it can only add nodes belonging to a group already defined ahead of time (an AWS Auto Scaling Group, a GKE node pool). Karpenter removes that constraint by provisioning directly the instance matching the pending pod, with no detour through a preconfigured group.

The node-group constraint

Cluster Autoscaler picks, among existing node groups, the one whose instance type best matches pending pods, then requests one more machine from that specific group. If no group fits the real need well (a pod needing lots of memory and little CPU, while every configured group is balanced), the provisioned node is either oversized (waste) or simply absent if nobody anticipated that profile in advance.

# Cluster Autoscaler: the choice is limited to already-defined
# node groups, regardless of the pod's real profile
apiVersion: v1
kind: ConfigMap
data:
  nodes.max: "20"
  # Instance type is fixed at the node group level,
  # not decided dynamically per pod

Karpenter: the pod describes its need, Karpenter picks the instance

Karpenter has no concept of a preconfigured node group. It watches pending pods directly and queries the cloud provider’s API to choose, among all available instance types, the one that best matches the actual need (CPU, memory, architecture, GPU requirements), factoring in price.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]

This NodePool doesn’t fix any specific instance type: it sets constraints (architecture, capacity type), and Karpenter dynamically resolves the optimal instance at every provisioning decision, pod by pod.

Consolidation, not just scale-down

Cluster Autoscaler removes an underutilized node, but only if all its pods can be rescheduled without violating a constraint. Karpenter goes further with active consolidation: it can proactively replace several underutilized nodes with fewer, better-sized ones, a continuous rebin-packing that Cluster Autoscaler doesn’t do natively.

disruption:
  consolidationPolicy: WhenEmptyOrUnderutilized
  consolidateAfter: 30s

What it actually changes in practice

The absence of preconfigured node groups eliminates an entire class of operational work: no more anticipating and maintaining a list of instance types per workload profile, Karpenter decides on the fly. The trade-off is real: Karpenter, historically tied to AWS EKS, has multi-cloud support that’s more recent and less mature than Cluster Autoscaler’s already well-established support on GKE or AKS; checking support maturity for the cloud provider in question is the first step before migrating.

Takeaway

Cluster Autoscaler stays locked into node groups preconfigured ahead of time, forcing a trade-off between workload-profile coverage and operational complexity. Karpenter eliminates that constraint by dynamically choosing the exact instance each pending pod requires, with active consolidation on top. The choice between the two mostly comes down to Karpenter’s support maturity on the cloud provider in use, one of the trade-offs that gets decided from the design stage of a Kubernetes migration.