Run Kubernetes Clusters for Less with Amazon EC2 Spot and Karpenter


Running Kubernetes workloads on Amazon EKS (Elastic Kubernetes Service) is powerful but can become costly, especially for high-scale environments. Fortunately, AWS offers EC2 Spot Instances to cut compute costs by up to 90%, and Karpenter, an open-source autoscaler, to optimize instance provisioning in real-time.
In this guide, I will walk you through:
What Spot Instances and Karpenter are
Why they are cost-effective together
A step-by-step guide to deploy Karpenter with EKS
Best practices
What Are EC2 Spot Instances?
Spot Instances let you use AWSโs unused EC2 capacity at reduced prices. However, they can be interrupted with just a 2-minute warning if AWS reclaims capacity.
Pros:
Up to 90% cheaper than On-Demand
Access to high-performance instances
Cons:
Not suitable for stateful, long-running jobs unless fault-tolerant
Can be terminated any time
๐ What is Karpenter?
Karpenter is an open-source autoscaler for Kubernetes, purpose-built for AWS. Unlike the default Cluster Autoscaler:
It launches instances dynamically based on pod needs.
It selects the best instance types and capacity pools.
Supports Spot, On-Demand, and Graviton instances.
Supports zones, architectures, taints, labels, affinities, etc.
๐ Why Use EC2 Spot + Karpenter?
Combining both gives you:
Cost-efficient workloads (Spot)
Smart and dynamic scaling (Karpenter)
Minimized waste through pod-to-instance right-sizing
๐ ๏ธ Prerequisites
Requirement | Value |
Kubernetes Cluster | Amazon EKS |
IAM Permissions | eks:DescribeCluster , iam:PassRole , etc. |
kubectl & eksctl | Installed |
AWS CLI | Installed |
Helm | Installed |
Region | e.g., us-east-1 |
๐งฐ Step-by-Step Guide
Step 1: Create an EKS Cluster
eksctl create cluster \
--name karpenter-demo \
--region us-east-1 \
--version 1.29 \
--nodegroup-name default-ng \
--nodes 2 \
--nodes-min 1 \
--nodes-max 5 \
--node-type t3.medium \
--managed
Step 2: Install Karpenter Controller
2.1 Add Helm Repo
helm repo add karpenter https://charts.karpenter.sh
helm repo update
Step 3: Create Karpenter IAM Role & Instance Profile
3.1 Download AWS CloudFormation Template
curl -fsSL https://karpenter.sh/docs/getting-started/cloudformation.yaml -o karpenter-iam.yaml
aws cloudformation deploy \
--stack-name "KarpenterIRSA" \
--template-file karpenter-iam.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
ClusterName=karpenter-demo \
ServiceAccountNamespace=karpenter \
ServiceAccountName=karpenter \
ClusterEndpoint=$(aws eks describe-cluster --name karpenter-demo --region us-east-1 --query "cluster.endpoint" --output text) \
OIDCProvider=$(aws eks describe-cluster --name karpenter-demo --region us-east-1 --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///")
Step 4: Install Karpenter via Helm
helm upgrade --install karpenter karpenter/karpenter \
--namespace karpenter --create-namespace \
--set serviceAccount.create=false \
--set serviceAccount.name=karpenter \
--set settings.clusterName=karpenter-demo \
--set settings.clusterEndpoint=$(aws eks describe-cluster --name karpenter-demo --region us-east-1 --query "cluster.endpoint" --output text) \
--set settings.aws.defaultInstanceProfile=KarpenterNodeInstanceProfile-karpenter-demo
Step 5: Create a Provisioner
# karpenter-provisioner.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: default
spec:
requirements:
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot"] # Only use spot instances
- key: "node.kubernetes.io/instance-type"
operator: In
values: ["m5.large", "m5.xlarge", "t3.medium"]
limits:
resources:
cpu: 1000
provider:
subnetSelector:
kubernetes.io/cluster/karpenter-demo: owned
securityGroupSelector:
kubernetes.io/cluster/karpenter-demo: owned
ttlSecondsAfterEmpty: 30
Apply it:
kubectl apply -f karpenter-provisioner.yaml
Step 6: Deploy Sample Workload
# sample-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inflate
spec:
replicas: 5
selector:
matchLabels:
app: inflate
template:
metadata:
labels:
app: inflate
spec:
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.5
resources:
requests:
cpu: "1"
Deploy it:
kubectl apply -f sample-deployment.yaml
Karpenter will observe that the default nodes donโt have capacity and will launch Spot Instances that fit.
๐ Cost Comparison Illustration
Instance Type | On-Demand Price | Spot Price | Savings |
m5.large | $0.096/hr | ~$0.028/hr | ~70% |
t3.medium | $0.0416/hr | ~$0.012/hr | ~71% |
Provisioning 100 pods needing 1 vCPU
each on Spot vs On-Demand could reduce your bill from $230/month to ~$70/month.
๐ Monitoring Karpenter
You can view logs and activity:
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter
To check provisioned nodes:
kubectl get nodes -l karpenter.sh/provisioner-name=default
๐ก๏ธ Spot Best Practices with Karpenter
Diversify instance types in the
Provisioner
.Set
taints
andaffinity
for critical workloads to run on On-Demand.Use Node Termination Handler (NTH) to drain pods gracefully.
Enable Pod Disruption Budgets (PDBs) to protect critical workloads.
Track Spot Interruption metrics with Prometheus.
๐ Final Thoughts
Using Karpenter with EC2 Spot Instances offers a powerful way to cut infrastructure costs while still delivering resilient, scalable Kubernetes workloads.
Benefit | Description |
โก Fast Scaling | Karpenter reacts in seconds |
๐ธ Cost Savings | Spot = up to 90% cheaper |
๐ Flexibility | Mix and match instance types, zones, families |
โ๏ธ Cloud-Native | Built for AWS and Kubernetes |
Subscribe to my newsletter
Read articles from Lloyd Theophilus Osabutey-Anikon directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Lloyd Theophilus Osabutey-Anikon
Lloyd Theophilus Osabutey-Anikon
DevOps and Cloud Engineer | Resolving Issues and Improving Systems for Optimal Business Outcomes | AWS Community Builder | 1x AWS Certified | YouTube ${Technology and Beyond}