What Karpenter is and why teams adopt it
Karpenter is a node lifecycle controller for Kubernetes. When the scheduler cannot place a pod, Karpenter looks at the pending pods’ CPU, memory, architecture, zone and other constraints, then asks the cloud for an instance that fits. When nodes sit idle or can be packed more tightly, it removes or replaces them.
The alternative many teams start with is a set of managed node groups sized by guesswork. That works until workloads diversify: one team needs memory-heavy pods, another needs GPUs, a third runs bursty CI. Karpenter replaces the question “which node group?” with “what does this pod need?”. If you are looking for Karpenter consulting, the usual drivers are an Amazon EKS bill that feels too high, slow scale-out, or the operational burden of many node groups.
Be clear about scope. Karpenter’s most mature implementation is on AWS. Other providers exist, but we recommend Karpenter primarily for EKS, and say so when a client’s platform makes another option better.
Karpenter versus Cluster Autoscaler
| Aspect | Karpenter | Cluster Autoscaler |
|---|---|---|
| Unit of scaling | Individual instances chosen per pending pod | Node groups of a fixed shape |
| Instance selection | Flexible across families and sizes | Defined per group |
| Consolidation | Built in, replaces or removes underused nodes | Scale-down of underused nodes |
| Cloud support | Strongest on AWS | Broad, long-established |
| Operational model | NodePool and node class resources | Node groups managed in the cloud |
| Risk profile | More power, more to tune | Simpler, more predictable |
Neither wins everywhere. Cluster Autoscaler remains a sound choice for steady workloads, and our Kubernetes consulting is tool-neutral on this point.
How we configure it in production
NodePools and node classes
A NodePool describes what Karpenter may launch: allowed instance categories, architectures, capacity types and zones, plus limits. A node class holds AWS-specific settings such as the AMI family, subnets, security groups and the node role. Field names and API versions have changed between releases, so pin the version and follow its documentation when applying anything below.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"]
limits:
cpu: "400"
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
budgets:
- nodes: "10%"
The limits block is a safety net: a runaway deployment cannot provision unbounded capacity. The disruption budget caps how many nodes Karpenter may voluntarily disrupt at once.
Keep the controller off its own capacity
Run Karpenter on a small managed node group or Fargate. If it depends on nodes it launches, a misconfiguration can strand it.
Security and access
Karpenter needs permission to create and terminate instances, so scope its IAM role tightly, use IAM roles for service accounts or EKS Pod Identity, and restrict which subnets, security groups and AMIs the node class can reference. Node roles should carry only what nodes need. Pin AMIs deliberately or use an aliased family so patching is controlled; drift detection can then roll nodes when the AMI changes.
Cost and capacity
Savings come from three levers: better packing, Spot capacity and removing idle nodes. All depend on honest pod requests. We compare requests with real usage using Prometheus and Grafana before tuning anything, because inflated requests make Karpenter provision generously. Our EKS cost optimisation guide covers the surrounding levers, such as commitments and Graviton. Spot is for interruption-tolerant pods only, and diversifying instance types improves the odds of getting capacity.
Making workloads Karpenter-friendly
Karpenter schedules what you ask for, so the pod specification matters more than the autoscaler. Set CPU and memory requests from observed usage, keep memory limits close to requests to avoid surprises, and use topology spread constraints so replicas land across zones. Workloads that cannot be interrupted can use the karpenter.sh/do-not-disrupt annotation, though overusing it defeats consolidation. Where you scale pods on queue depth or events, pair KEDA or the Horizontal Pod Autoscaler with Karpenter so replica count and node count follow demand together.
Day-2 operations
- Upgrades: upgrade the controller and its CRDs deliberately, reading release notes for API changes, in a non-production cluster first.
- Monitoring: alert on pending pods, failed launches, and NodeClaims that never become ready.
- Disruption control: use budgets and maintenance windows so consolidation does not coincide with a release or peak traffic.
- Interruption handling: confirm Spot interruption notices reach the controller and that pods shut down within the grace period.
Common failure modes
- Missing PodDisruptionBudgets, so consolidation evicts too many replicas at once.
- No limits on a NodePool, allowing a bug to scale costs.
- Pods without requests, leading to poor packing and noisy neighbours.
- A single instance family, which fails when that Spot pool dries up.
- DaemonSet overhead ignored, so small instances waste a large share on system pods.
When not to use Karpenter
Skip it if you are not on AWS and your provider is immature, if your cluster is small and static, or if you would rather AWS manage compute through EKS Auto Mode. If you want a measured view of your own node costs first, run the EKS cost calculator or ask us to review your cluster through our AWS EKS consulting service.
Official documentation: karpenter.sh