Amazon EKS — Karpenter for Cost Savings
Karpenter’s node lifecycle management directly reduces costs through consolidation, right-sized provisioning, and intelligent Spot usage. It’s the most impactful cost optimization tool for EKS.
How Karpenter Saves Money
1. Consolidation
Karpenter monitors node utilization and replaces underutilized nodes with smaller ones:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m
Example: You have 3 nodes at 30% CPU each. Karpenter:
- Detects underutilization
- Launches 1 smaller node that can fit all pods
- Drains the 3 old nodes
- Result: 1 node instead of 3
2. Just-in-Time Provisioning
Unlike ASG-based node groups (pre-provisioned), Karpenter launches nodes only when pods are pending:
| Approach | Behavior | Waste |
|---|---|---|
| ASG (min=3) | 3 nodes always running | High (idle during low traffic) |
| Karpenter | 0 nodes until pods need them | Minimal |
3. Right-Sized Instances
Karpenter inspects pod resource requests and selects the optimal instance type:
Pending pods need:
- 3 vCPU total
- 8 GiB memory total
ASG approach: Always launches m6i.xlarge (4 vCPU, 16 GiB) — 25% CPU waste
Karpenter: Launches m6i.large (2 vCPU, 8 GiB) + c6i.large (2 vCPU, 4 GiB) — packed tightly
4. Bin-Packing Efficiency
Karpenter packs pods tightly onto nodes, reducing the total node count:
Without Karpenter (ASG, one instance type):
┌──────────┐ ┌──────────┐ ┌──────────┐
│ ████░░░░ │ │ ██░░░░░░ │ │ ██████░░ │
│ 50% used │ │ 25% used │ │ 75% used │
└──────────┘ └──────────┘ └──────────┘
3 nodes, average 50% utilization
With Karpenter (mixed instance types):
┌──────────┐ ┌──────────┐
│ ████████ │ │ ██████░░ │
│ 95% used │ │ 80% used │
└──────────┘ └──────────┘
2 nodes, average 87% utilization
5. Spot + On-Demand Mixing
Use separate NodePools for critical (On-Demand) and non-critical (Spot) workloads:
# Spot NodePool
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m6i.large", "m6i.xlarge", "m5.large", "m5.xlarge", "c6i.large", "c6i.xlarge", "r6i.large"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "100"
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
---
# On-Demand NodePool (critical only)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: on-demand
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m6i.xlarge", "m6i.2xlarge"]
taints:
- key: critical
value: "true"
effect: NoSchedule
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
limits:
cpu: "32"
Measuring Karpenter Savings
eks-node-viewer
Visualize node utilization in real-time:
go install github.com/awslabs/eks-node-viewer/cmd/eks-node-viewer@latest
eks-node-viewer --resources cpu,memory
Output:
3 nodes (875m/5790m) 15.1% cpu ████░░░░░░░░░░░░░░░░░░░░
3 nodes (1.2Gi/11Gi) 10.9% memory ███░░░░░░░░░░░░░░░░░░░░░
Prometheus Metrics
Karpenter exposes metrics for cost tracking:
# Total nodes by capacity type
count(karpenter_nodes_total) by (capacity_type)
# Node utilization
karpenter_nodes_allocatable{resource_type="cpu"}
vs
karpenter_nodes_total_pod_requests{resource_type="cpu"}
Best Practices
- Enable consolidation — single biggest money saver
- Set aggressive
consolidateAfter— 30s-1m for non-production, 5m for production - Diversify instance types — at least 6-10 types in NodePool
- Use Spot for non-critical workloads — 60-90% savings
- Set
limits.cpuon NodePools — prevent runaway scaling - Monitor with eks-node-viewer — visual confirmation of utilization