All Articles

Karpenter Complete Guide 2026: Smarter Kubernetes Node Autoscaling

Karpenter replaces Cluster Autoscaler with faster, more cost-efficient node provisioning. Learn architecture, NodePools, disruption budgets, Spot integration, and production best practices.

DevOpsBoysMar 30, 20265 min read
Share:Tweet

Cluster Autoscaler has been the default Kubernetes node autoscaling solution for years. It works — but it's slow, it's tied to Auto Scaling Groups, and it makes suboptimal instance choices. Karpenter was built to fix all of that.

This guide covers Karpenter architecture, configuration, Spot instance integration, and production-ready patterns for 2026.

What is Karpenter?

Karpenter is an open-source Kubernetes node provisioner built by AWS (now a CNCF project). Instead of working through Auto Scaling Groups, Karpenter talks directly to cloud provider APIs to provision exactly the right nodes for pending pods.

Key differences from Cluster Autoscaler:

FeatureCluster AutoscalerKarpenter
Provisioning speed2-5 minutes30-60 seconds
Instance selectionFixed ASG typesBest-fit from any instance family
Spot supportASG-based spotFirst-class, multi-family spot
Bin packingLimitedAggressive consolidation
AWS-nativePartialDeep integration

Architecture Overview

┌─────────────────────────────────────────┐
│              Kubernetes Cluster          │
│                                         │
│  Pod (Pending) → Karpenter Controller   │
│                       ↓                 │
│            NodePool + EC2NodeClass       │
│                       ↓                 │
│         AWS EC2 Fleet API               │
│                       ↓                 │
│     New Node Joins Cluster (under 60s)  │
└─────────────────────────────────────────┘

Karpenter watches for unschedulable pods, evaluates which node types would fit, and calls EC2 directly to provision the best-fit instance. No ASG required.

Installing Karpenter on EKS

Prerequisites:

  • EKS cluster (1.23+)
  • AWS CLI configured
  • helm and kubectl installed

Step 1: Set environment variables

bash
export CLUSTER_NAME="my-eks-cluster"
export AWS_DEFAULT_REGION="us-east-1"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export TEMPOUT=$(mktemp)

Step 2: Create IAM roles

bash
# Download and apply CloudFormation template
curl -fsSL https://raw.githubusercontent.com/aws/karpenter-provider-aws/v1.0.0/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml > $TEMPOUT
 
aws cloudformation deploy \
  --stack-name "Karpenter-${CLUSTER_NAME}" \
  --template-file "${TEMPOUT}" \
  --capabilities CLOUD_FORMATION_DECLARE_IAM_RESOURCES \
  --parameter-overrides "ClusterName=${CLUSTER_NAME}"

Step 3: Install with Helm

bash
helm registry logout public.ecr.aws
 
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --version "1.0.0" \
  --namespace "kube-system" \
  --set "settings.clusterName=${CLUSTER_NAME}" \
  --set "settings.interruptionQueue=${CLUSTER_NAME}" \
  --set controller.resources.requests.cpu=1 \
  --set controller.resources.requests.memory=1Gi \
  --set controller.resources.limits.cpu=1 \
  --set controller.resources.limits.memory=1Gi \
  --wait

Verify:

bash
kubectl get pods -n kube-system -l app.kubernetes.io/name=karpenter

Learn Karpenter hands-on: KodeKloud has EKS labs that walk through Karpenter installation and configuration in real AWS environments.

NodePool Configuration

NodePool (formerly Provisioner) defines what kinds of nodes Karpenter can create.

Basic NodePool:

yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: kubernetes.io/os
          operator: In
          values: ["linux"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: Gt
          values: ["2"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  limits:
    cpu: 1000
    memory: 1000Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

NodePool with Spot instances:

yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-pool
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]  # Spot preferred, on-demand fallback
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-size
          operator: NotIn
          values: ["nano", "micro", "small"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s
    budgets:
    - nodes: "20%"   # Don't disrupt more than 20% of nodes at once

EC2NodeClass Configuration

EC2NodeClass defines AWS-specific node configuration (AMI, subnets, security groups).

yaml
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiSelectorTerms:
  - alias: al2023@latest  # Amazon Linux 2023, always latest
 
  subnetSelectorTerms:
  - tags:
      karpenter.sh/discovery: my-eks-cluster
 
  securityGroupSelectorTerms:
  - tags:
      karpenter.sh/discovery: my-eks-cluster
 
  role: "KarpenterNodeRole-my-eks-cluster"
 
  blockDeviceMappings:
  - deviceName: /dev/xvda
    ebs:
      volumeSize: 50Gi
      volumeType: gp3
      encrypted: true
      iops: 3000
      throughput: 125
 
  tags:
    Environment: production
    ManagedBy: Karpenter

Spot Instance Best Practices

Karpenter's Spot integration is superior to Cluster Autoscaler because it diversifies across instance families automatically.

Best practices for Spot:

yaml
# Allow many instance types to maximize Spot availability
requirements:
  - key: karpenter.sh/capacity-type
    operator: In
    values: ["spot"]
  - key: karpenter.k8s.aws/instance-category
    operator: In
    values: ["c", "m", "r", "t"]
  - key: karpenter.k8s.aws/instance-cpu
    operator: In
    values: ["4", "8", "16", "32"]

Handle Spot interruptions with pod disruption budgets:

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: "75%"
  selector:
    matchLabels:
      app: my-app

Use node affinity for critical workloads to prefer on-demand:

yaml
affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      preference:
        matchExpressions:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]

Disruption and Consolidation

Karpenter's consolidation actively removes underutilized nodes to save cost — a feature Cluster Autoscaler struggles with.

yaml
disruption:
  consolidationPolicy: WhenEmptyOrUnderutilized
  consolidateAfter: 5m
  budgets:
  # Never disrupt during business hours for production
  - schedule: "0 9 * * 1-5"   # 9 AM Mon-Fri UTC
    duration: 9h
    nodes: "0"                  # 0 = no disruption during this window
  # Allow normal consolidation otherwise
  - nodes: "10%"

Monitoring Karpenter

Key metrics to watch:

bash
# Pending pods (should be near 0 in steady state)
karpenter_pods_state{state="Pending"}
 
# Node provisioning latency
karpenter_nodes_termination_time_seconds_bucket
 
# Node count by capacity type
karpenter_nodes_total{capacity_type="spot"}
karpenter_nodes_total{capacity_type="on-demand"}

Useful kubectl commands:

bash
# See what Karpenter is doing
kubectl logs -n kube-system -l app.kubernetes.io/name=karpenter --follow
 
# List all Karpenter-managed nodes
kubectl get nodes -l karpenter.sh/nodepool
 
# Check NodePool status
kubectl get nodepools
kubectl describe nodepool default
 
# View NodeClaims (Karpenter's node records)
kubectl get nodeclaims

Common Issues

Pods still pending after Karpenter install:

bash
# Check if Karpenter can see pending pods
kubectl get events --field-selector reason=FailedScheduling
 
# Check Karpenter controller logs
kubectl logs -n kube-system deploy/karpenter -c controller | grep -i error

Karpenter not consolidating nodes:

  • Check that consolidationPolicy is set on your NodePool
  • Verify pods don't have karpenter.sh/do-not-disrupt: "true" annotation
  • Check disruption budgets aren't blocking

Instance not available in region:

  • Broaden your instance category/size requirements
  • Add more instance families (c, m, r, t, i, d)

Karpenter vs Cluster Autoscaler: When to Use Which

Use Karpenter when:

  • Running on EKS (first-class support)
  • Cost optimization is a priority
  • You need fast provisioning (under 60s)
  • Using Spot instances heavily

Stick with Cluster Autoscaler when:

  • Running on GKE or AKS (Karpenter AWS-specific)
  • Using managed node groups extensively
  • Team is not ready to manage NodePool configs

Karpenter can cut your AWS EC2 costs by 40-60% on variable workloads. Deploy it on DigitalOcean Kubernetes managed clusters or EKS for immediate cost savings.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments