Skip to content

Migrating from Managed Node Groups to Auto Mode

Supported Versions: EKS 1.29+, EKS Auto Mode GA Last Updated: July 3, 2026

This guide covers how to migrate from existing EKS Managed Node Groups to Auto Mode, including step-by-step instructions, coexistence strategies, and important cautions.


Migration Overview


Step 1: Analyze Current State

Before migration, thoroughly analyze your current cluster configuration.

bash
# Check current node groups
eksctl get nodegroup --cluster my-cluster

# Analyze node resource usage
kubectl top nodes

# Check workload distribution
kubectl get pods -A -o wide | awk '{print $8}' | sort | uniq -c

# Nodes by current instance type
kubectl get nodes -o custom-columns=\
NAME:.metadata.name,\
TYPE:.metadata.labels.node\\.kubernetes\\.io/instance-type,\
ZONE:.metadata.labels.topology\\.kubernetes\\.io/zone

# Check for node selectors in deployments
kubectl get deployments -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.spec.template.spec.nodeSelector}{"\n"}{end}'

# Check for node affinities
kubectl get deployments -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.spec.template.spec.affinity.nodeAffinity}{"\n"}{end}'

Pre-Migration Checklist

ItemCheckNotes
EKS version>= 1.29Required for Auto Mode
Node group instance typesDocument allMap to NodePool requirements
Custom AMIsDocumentMay need NodeClass config
User data scriptsReviewEnsure compatibility
IAM rolesDocumentAuto Mode creates new roles
Security groupsDocumentConfigure in NodeClass
TagsDocumentAdd to NodeClass
Node selectors in workloadsIdentifyMay need updates

Step 2: Enable Auto Mode

Enable Auto Mode on your existing cluster.

bash
# 1. Enable Auto Mode
aws eks update-cluster-config \
    --name my-cluster \
    --compute-config enabled=true,nodePools=general-purpose,nodePools=system

# 2. Check activation status
aws eks describe-cluster --name my-cluster \
    --query 'cluster.computeConfig'

# 3. Wait for update to complete
aws eks wait cluster-active --name my-cluster

# 4. Verify NodePools are created
kubectl get nodepools

Step 3: Configure Custom NodePools

Create NodePools that match your current node group configurations.

yaml
# custom-nodepools.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: migrated-workloads
spec:
  template:
    metadata:
      labels:
        migration: auto-mode
    spec:
      requirements:
        # Instance types similar to existing node groups
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["m", "c", "r"]
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
      nodeClassRef:
        group: eks.amazonaws.com
        kind: NodeClass
        name: default

Mapping Node Groups to NodePools

Node Group ConfigNodePool Equivalent
Instance typeskarpenter.k8s.aws/instance-category, instance-size
Capacity typekarpenter.sh/capacity-type
Labelstemplate.metadata.labels
Taintstemplate.spec.taints
Scaling limitsspec.limits
Subnet tagsNodeClass subnetSelectorTerms
Security groupsNodeClass securityGroupSelectorTerms
AMI typeNodeClass amiFamily

Step 4: Migrate Workloads

Gradually move workloads from existing node groups to Auto Mode nodes.

bash
# Apply cordon to existing nodes (prevent new Pod scheduling)
kubectl cordon -l eks.amazonaws.com/nodegroup=old-nodegroup

# Gradually drain Pods
for node in $(kubectl get nodes -l eks.amazonaws.com/nodegroup=old-nodegroup -o name); do
    kubectl drain $node --ignore-daemonsets --delete-emptydir-data
    sleep 60  # Wait time between each node
done

Safe Migration Script

bash
#!/bin/bash
# migrate-workloads.sh

NODE_GROUP="old-nodegroup"
DRAIN_INTERVAL=60

# Get nodes in the old node group
NODES=$(kubectl get nodes -l eks.amazonaws.com/nodegroup=$NODE_GROUP -o name)

echo "Starting migration of node group: $NODE_GROUP"
echo "Found $(echo "$NODES" | wc -l) nodes to migrate"

# Cordon all nodes first
for node in $NODES; do
    echo "Cordoning $node..."
    kubectl cordon $node
done

echo "All nodes cordoned. Starting drain process..."

# Drain nodes one by one
for node in $NODES; do
    echo "Draining $node..."
    kubectl drain $node \
        --ignore-daemonsets \
        --delete-emptydir-data \
        --timeout=300s

    if [ $? -ne 0 ]; then
        echo "WARNING: Failed to drain $node, continuing..."
    fi

    echo "Waiting ${DRAIN_INTERVAL}s before next node..."
    sleep $DRAIN_INTERVAL
done

echo "Migration complete. Verify with: kubectl get pods -A -o wide"

Step 5: Scale Down Existing Node Groups

After workloads are migrated, scale down the old node groups.

bash
# Scale down node group
eksctl scale nodegroup \
    --cluster my-cluster \
    --name old-nodegroup \
    --nodes 0 \
    --nodes-min 0

# Verify nodes are terminating
kubectl get nodes -l eks.amazonaws.com/nodegroup=old-nodegroup -w

Step 6: Delete Existing Node Groups

Once confirmed that all workloads are running on Auto Mode nodes, delete the old node groups.

bash
# Delete node group
eksctl delete nodegroup \
    --cluster my-cluster \
    --name old-nodegroup

# Verify deletion
eksctl get nodegroup --cluster my-cluster

Step 7: Validation and Optimization

Validate the migration and optimize configurations.

bash
# Verify all pods are running
kubectl get pods -A --field-selector=status.phase!=Running

# Check node distribution
kubectl get nodes -o wide -L karpenter.sh/nodepool,karpenter.sh/capacity-type

# Verify resource utilization
kubectl top nodes
kubectl top pods -A

# Check for any pending pods
kubectl get pods -A --field-selector=status.phase=Pending

Coexistence Period Operations

During migration, existing node groups and Auto Mode can coexist.

yaml
# coexistence-config.yaml
# Existing node group workloads
apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy-app
spec:
  replicas: 3
  template:
    spec:
      # Pin to existing node group
      nodeSelector:
        eks.amazonaws.com/nodegroup: old-nodegroup
      containers:
        - name: app
          image: legacy-app:latest
---
# Auto Mode workloads
apiVersion: apps/v1
kind: Deployment
metadata:
  name: new-app
spec:
  replicas: 3
  template:
    spec:
      # Prefer Auto Mode nodes
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: karpenter.sh/nodepool
                    operator: Exists
      containers:
        - name: app
          image: new-app:latest

Coexistence Best Practices

PracticeDescription
Use node selectorsPin workloads to specific infrastructure
Gradual migrationMove workloads in phases
Monitor bothTrack metrics from both node types
Test thoroughlyValidate behavior before full migration
Rollback planKeep node groups scaled down, not deleted initially

Migrating from Self-Managed Karpenter (Official kubectl-Based Path)

If you are running self-managed Karpenter directly rather than managed node groups, AWS provides an officially supported kubectl-based migration path as an alternative to the node group cutover procedure described above.

Prerequisites

  • Self-managed Karpenter v1.1 or later must already be installed on the cluster
  • Document your existing Karpenter NodePool/EC2NodeClass configuration

Migration Steps

  1. Enable Auto Mode: Enable Auto Mode while leaving the existing Karpenter controller and NodePools in place.

  2. Create a tainted Auto Mode NodePool: Add a taint so workloads aren't scheduled onto Auto Mode nodes unintentionally.

    yaml
    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: auto-mode-migration
    spec:
      template:
        spec:
          taints:
            - key: eks.amazonaws.com/auto-mode
              value: "true"
              effect: NoSchedule
          nodeClassRef:
            group: eks.amazonaws.com
            kind: NodeClass
            name: default
  3. Add matching tolerations/nodeSelector to workloads: Add a toleration for the taint above and a nodeSelector targeting the Auto Mode NodePool to the workloads you want to migrate.

    yaml
    spec:
      template:
        spec:
          tolerations:
            - key: eks.amazonaws.com/auto-mode
              value: "true"
              effect: NoSchedule
          nodeSelector:
            karpenter.sh/nodepool: auto-mode-migration
  4. Migrate incrementally: Add the toleration/nodeSelector to one workload group at a time, moving workloads onto Auto Mode nodes while Karpenter-managed nodes and Auto Mode nodes run side by side in the same cluster.

  5. Remove self-managed Karpenter: Once all workloads are confirmed running on Auto Mode nodes, remove the self-managed Karpenter controller and its associated resources (NodePools, EC2NodeClasses, IAM roles, Helm release, etc.).

This path is intended for clusters already running self-managed Karpenter. If you are migrating directly from managed node groups, follow Steps 1-7 above instead.


Migration Cautions

ItemCautionMitigation
AMI CompatibilityAuto Mode supports only AL2023 or BottlerocketTest workloads on new AMI
User DataVerify existing bootstrap script compatibilityReview and test userData
IAM RoleAuto Mode IAM role is auto-createdVerify permissions for workloads
Security GroupsReconfigure in NodeClassDocument and replicate rules
TagsReflect existing tag policies in NodeClassAudit and add tags
MonitoringNew metrics collection setup requiredUpdate dashboards and alerts
Node SelectorsWorkloads with eks.amazonaws.com/nodegroup won't scheduleUpdate selectors
Persistent VolumesEBS volumes are AZ-specificPlan for volume migration

Common Migration Issues

IssueSymptomSolution
Pods not schedulingPending stateUpdate node selectors, tolerations
Application errorsRuntime failuresCheck AMI compatibility
Performance degradationLatency increaseVerify instance types match
Cost increaseHigher EC2 billsReview NodePool limits, Spot config

Rollback Procedure

If migration issues occur, you can rollback.

bash
# 1. Scale up old node groups
eksctl scale nodegroup \
    --cluster my-cluster \
    --name old-nodegroup \
    --nodes 3 \
    --nodes-min 1 \
    --nodes-max 10

# 2. Uncordon old nodes (if cordoned)
kubectl uncordon -l eks.amazonaws.com/nodegroup=old-nodegroup

# 3. Cordon Auto Mode nodes
kubectl cordon -l karpenter.sh/nodepool

# 4. Drain Auto Mode nodes
for node in $(kubectl get nodes -l karpenter.sh/nodepool -o name); do
    kubectl drain $node --ignore-daemonsets --delete-emptydir-data
done

# 5. Verify workloads are back on old nodes
kubectl get pods -A -o wide

# 6. Optionally disable Auto Mode
aws eks update-cluster-config \
    --name my-cluster \
    --compute-config enabled=false

Post-Migration Optimization

After successful migration:

  1. Review NodePool configurations: Optimize requirements based on actual workload needs
  2. Enable Spot instances: For suitable workloads to reduce costs
  3. Configure consolidation: Enable cost optimization through node consolidation
  4. Set up monitoring: Create dashboards for Auto Mode metrics
  5. Document changes: Update runbooks and documentation
  6. Train team: Ensure operations team understands new management model

< Previous: Workload Optimization | Table of Contents | Back to EKS Topics >