Karpenter Cut My K8s Bill 60%%—Here's How

I spent $47,000 on Kubernetes last month. This month? $19,000. Same workloads. Same team. The only difference? I finally got Karpenter configured right. I'm ...

karpenter bill 60%%—here's
By Nishaant Dixit
Karpenter Cut My K8s Bill 60%—Here's How

Karpenter Cut My K8s Bill 60%—Here's How

Stop 3AM Pages

Free K8s Audit

Get Started →
Karpenter Cut My K8s Bill 60%—Here's How

I spent $47,000 on Kubernetes last month. This month? $19,000. Same workloads. Same team. The only difference? I finally got Karpenter configured right.

I'm Nishaant Dixit. I run SIVARO, a product engineering shop that builds data infrastructure and production AI systems. We run hundreds of Kubernetes clusters for clients across fintech, healthcare, and logistics. And for years, I watched those bills climb while telling myself "this is just the cost of doing business at scale."

Turns out, I was wrong.

Here's what this guide covers: Exactly how to reduce Kubernetes costs with Karpenter—the concrete configs, the spot instance dance, the cluster autoscaler migration, and the traps I fell into so you don't have to. No theory. Just what worked.


Why Your Kubernetes Bill Is Too Damn High

Most people think Kubernetes is expensive. Why Companies Are Leaving Kubernetes? points to complexity, but I'll tell you the real reason: you're over-provisioning.

Standard Cluster Autoscaler (the default) is dumb. Not malicious—just dumb. It scales based on pending pods. Doesn't consider instance types. Doesn't optimize for cost. Doesn't care if you're running a c5.4xlarge at 12% utilization because one pod needed 16GB of memory for a startup task.

That's how you burn money.

Karpenter solves this differently. It watches actual pod resource requests and schedules them onto the cheapest available instance that fits. No node groups. No instance type lists you manually maintain. Just "here's what I need—find the cheapest way to run it."

I moved our first client cluster from standard CA to Karpenter in February 2024. Savings hit 43% in the first month. The client called, suspicious. "Did you reduce our resource limits?" No. We just stopped paying for empty space.


What Karpenter Actually Does (And Doesn't)

Karpenter is a node autoscaler built by AWS. It launches EC2 instances directly (skipping the node group abstraction) and terminates them when pods leave. It picks instance types dynamically based on what your pods actually need.

What it doesn't do: Magic. You still need to configure it. You still need to understand your workload patterns. Getting it wrong means paying more, not less.

The core mechanism

Standard autoscaler: Pod pending → scales up node group → new instance boots → pod schedules. Takes 3-8 minutes.

Karpenter: Pod pending → picks cheapest fitting instance type → launches instance directly → pod schedules. Takes 45-90 seconds.

That speed matters. During traffic spikes, standard autoscaler often overscales because it's slow. By the time new nodes come up, the spike is over—but you're paying for the next hour anyway. Karpenter's speed reduces that over-provisioning entirely.


How to Configure Karpenter for Spot Instances (The Money Saver)

I Deleted Kubernetes from 70% of Our Services in 2026 talks about teams abandoning Kubernetes entirely. I get it. But here's the thing—Kubernetes isn't dead, you just misused it. The teams that leave usually had their cost story wrong from day one.

Spot instances are where Karpenter really shines. Here's the config I use:

yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: "karpenter.sh/capacity-type"
          operator: In
          values: ["spot", "on-demand"]
        - key: "kubernetes.io/arch"
          operator: In
          values: ["amd64", "arm64"]
        - key: "karpenter.k8s.aws/instance-size"
          operator: In
          values: ["nano", "micro", "small", "medium", "large", "xlarge"]
      kubelet:
        maxPods: 110
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
  limits:
    cpu: 1000
  weight: 100
---
apiVersion: karpenter.sh/v1beta1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiFamily: Bottlerocket
  role: "karpenter-node-role"
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "my-cluster"
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: "my-cluster"
  tags:
    Name: karpenter-node

Key decisions in that config:

  1. consolidationPolicy: WhenEmptyOrUnderutilized — This is the magic. Karpenter constantly checks if it can consolidate pods onto fewer, cheaper instances. If a node drops below 80% utilization across its resources, Karpenter tries to evict the pods and terminate the box.

  2. consolidateAfter: 1m — Wait one minute before consolidating. Prevents thrashing during brief traffic dips.

  3. Priority via weight — If I want spot but the spot market is tight, Karpenter falls back to on-demand. The spot, on-demand requirement means "prefer spot, accept on-demand."

  4. maxPods: 110 — Bottlerocket handles 110 pods per node easily. If you're running many small pods, this keeps node count low.

The spot instance trap

Most people think "spot = cheap, let's use all spot." Wrong.

The trap: Stateful workloads on spot. Pods with persistent volumes, database workers, or anything that can't survive a 2-minute termination notice will get destroyed when spot prices spike. We're leaving Kubernetes talks about this exact pain—stateful services that can't handle rescheduling.

The fix: Use pod topology spread constraints and karpenter.sh/capacity-type node selectors. Keep your critical stateful workloads pinned to on-demand, and let everything else ride spot.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-critical-worker
spec:
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: karpenter.sh/capacity-type
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app: my-app
  nodeSelector:
    karpenter.sh/capacity-type: on-demand

That maxSkew: 1 keeps a single spot interruption from taking down all replicas. The nodeSelector pins the pod to on-demand explicitly.


Migrating From Cluster Autoscaler Without Pain

If you're running Cluster Autoscaler today, you're probably bleeding money on over-provisioned nodes. But you can't just rip it out.

Here's the exact migration I did for a production cluster:

  1. Day 1: Deploy Karpenter alongside CA. Set Karpenter's weight to 100, CA's to 0. Karpenter starts handling new pods. CA stops scaling.

  2. Day 3-5: Manually cordon and drain nodes launched by CA. Karpenter replaces them with cheaper instances.

  3. Day 7: Remove CA helm chart. Clean up node groups.

Critical detail: Set --expander=priority on CA with a node group priority of 0 before migrating. This prevents CA from fighting Karpenter for new pods.

bash
# Current CA config before migration
helm upgrade cluster-autoscaler   --set autoDiscovery.clusterName=my-cluster   --set expander=priority   --set extraArgs.expander-priority-config=/config/priorities

The gotcha: Karpenter doesn't work with node groups at all. If you have taints or labels on your node groups, you need to move those into your NodePool spec. I lost a weekend on this because one client had GPU nodes with a custom label that wasn't in the NodePool requirements.


How to Reduce Kubernetes Costs With Karpenter: The 4 Levers

How to Reduce Kubernetes Costs With Karpenter: The 4 Levers

1. Instance Diversity

Karpenter gets cheaper when you let it choose from more instance types. If you restrict it to one family, you're paying for flexibility you don't use.

yaml
requirements:
  - key: "karpenter.k8s.aws/instance-family"
    operator: NotIn
    values: ["c5", "m5"]  # Avoid older (or more expensive) families

I let Karpenter choose from 47 instance types per Availability Zone. That's 141 possible instances. Karpenter picks the cheapest one that fits.

Result: Our average per-vCPU cost dropped from $0.042/hr to $0.019/hr. That's a 55% reduction just from instance diversity.

2. Bin Packing

Standard autoscaler leaves 30-50% of your CPU untouched because it's waiting for a pod that needs that exact resource profile. Karpenter packs tighter.

I run binpack with a target of 80% CPU and 85% memory on each node. Below that, Karpenter consolidates.

yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: binpack-aggressive
spec:
  template:
    spec:
      kubelet:
        maxPods: 110
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30s
    budgets:
      - nodes: 10%
  limits:
    cpu: 500
  weight: 50

Trade-off: Aggressive bin packing means more pods move around. If your applications don't handle pod restarts well, you'll see errors. Start with 70% CPU target and dial up slowly.

3. Spot Instance Mix

Here's how to configure Karpenter for spot instances without waking up to a pager:

yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: spot-pool
spec:
  template:
    spec:
      requirements:
        - key: "karpenter.sh/capacity-type"
          operator: In
          values: ["spot"]
        - key: "node.kubernetes.io/instance-type"
          operator: In
          values:
            - "c7i.xlarge"
            - "c6i.xlarge"
            - "m7i.xlarge"
            - "m6i.xlarge"
            - "r7i.xlarge"
            - "r6i.xlarge"
  disruption:
    consolidationPolicy: WhenEmpty
    expireAfter: 720h

Notice expireAfter: 720h — that's 30 days. Spot instances get replaced monthly to avoid running on old hardware that's more likely to be reclaimed.

The real trick: Run a mix of generations. c7i and c6i. If c7i spot price spikes, Karpenter falls back to c6i. I've seen this save 30% during instance famines.

4. Reserved Capacity + Karpenter

You can run Karpenter with reserved instances. It works. But you need to tell Karpenter about your reservations via tags or instance families.

yaml
metadata:
  name: reserved-pool
spec:
  template:
    spec:
      requirements:
        - key: "karpenter.k8s.aws/instance-family"
          operator: In
          values: ["c7i", "m7i"]  # Match your reservations

Why this matters: If you buy 3-year reserved instances for c7i.2xlarge, and Karpenter launches a c7i.xlarge, you're not covering that with your reservation. You're paying on-demand rates for the xlarge and your reservation sits unused. Match the families.


The Karpenter Cost Dashboard You Need

Metrics matter. Here's what I track:

Metric What It Tells You Action if High
Average node utilization How full your boxes are Lower binpack threshold
Spot interruption rate How often spot gets reclaimed Increase instance diversity
Time to pod schedule How fast Karpenter responds Check subnet/AMI caching
Cost per vCPU-hour True cost efficiency Adjust instance family mix

I use Karpenter's built-in metrics with Prometheus:

yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: karpenter
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: karpenter
      app.kubernetes.io/name: karpenter
  endpoints:
  - port: http-metrics
    interval: 15s

The dashboard query I check daily: sum(karpenter_nodes_created) by (instance_type, capacity_type). This tells me exactly what Karpenter is spending on. If I see expensive m5.4xlarges in the on-demand column, I know my spot config is broken.


When Karpenter Isn't the Answer

I'm not going to sell you snake oil. Karpenter has problems.

Small clusters (<5 nodes): The savings aren't worth the complexity. Standard autoscaler is fine. You're saving $200/month max.

Strict compliance environments: If you need to audit every instance launch and run pre-approved golden AMIs, Karpenter's dynamic instance selection works against you. You'd spend more time on compliance than you save on compute.

GPU workloads: Karpenter doesn't handle GPU bin packing well. The instance diversity matters less when you're running p4d.24xlarges that cost $30/hr regardless.

Teams that can't handle pod churn: If your applications crash on reschedule, don't use consolidation. Set consolidationPolicy: WhenEmpty instead of WhenUnderutilized.

I had a client in early 2025 who ran a legacy Java app that took 12 minutes to warm up the JIT compiler. Karpenter kept consolidating their nodes, killing the JVM, and re-creating it on a different box. The app never got stable. They turned off consolidation and went back to standard autoscaler.

Kubernetes isn't dead, you just misused it. In this case, they misused Karpenter on a workload that needed static placement.


Real Numbers: What I Saved

I manage 12 production clusters. Here's the before/after for the three largest:

Cluster Before (Standard CA) After (Karpenter) Savings
Fintech prod $47,200/month $19,100/month 59.5%
AI training $89,000/month $52,000/month 41.6%
Log aggregation $12,800/month $5,200/month 59.4%

The AI training cluster saved less because GPU instances don't benefit as much from spot pricing or bin packing. The log aggregation cluster was the biggest win—stateless, small-pod workloads that Karpenter packed beautifully.

Total annual savings: ~$898,000 across 12 clusters.


FAQ

Q: Can Karpenter work alongside existing Cluster Autoscaler?

Yes. Deploy Karpenter with weight: 100 and CA with --expander=priority --priority=0 for all node groups. Karpenter handles new pods, CA handles existing node groups you haven't migrated yet. I did this transition over 7 days in production—zero downtime.

Q: How do I prevent Karpenter from launching too many on-demand instances during a spot market shortage?

Set limits.cpu in your spot NodePool to a number that caps your on-demand spend. Or configure a single NodePool with values: ["spot", "on-demand"] and let Karpenter prioritize spot naturally. The first approach is safer—you won't wake up to a $100K bill if AWS runs out of spot capacity in your region.

Q: Does Karpenter handle Windows nodes?

Technically yes, but practically no. The AMI family support for Windows is limited, and consolidation behavior is less tested. I don't run Windows in production with Karpenter. If you must, use a separate NodePool and don't mix OS families.

Q: How often should I update Karpenter?

Monthly. AWS releases patches frequently. I've seen bugs fixed that caused nodes to not consolidate properly. Run kubectl apply -f https://karpenter.sh/v1beta1/karpenter.yaml on your schedule and watch for breaking changes in their releases.

Q: What happens during a spot interruption?

AWS sends a 2-minute termination notice. Karpenter catches this via the instance metadata service and marks the node as NoSchedule immediately. It drains pods, creates replacement instances, and the new pods schedule onto them. The disruption budget you set in your NodePool controls how many nodes can be replaced simultaneously.

Q: Can Karpenter reduce costs for burstable instances (t3/t4g)?

Yes, but careful. Burstable instances are cheap until you burn your CPU credits. Pods that spike CPU will degrade performance. I only use burstable for batch processing or workloads with sustained low CPU (<20%). For anything spiky, stick with standard instances.

Q: How does Karpenter handle spot pricing differences between regions?

It doesn't directly monitor spot market prices—it uses instance type availability and your node selector requirements. If a spot instance type becomes unavailable in your AZ, Karpenter falls back to the next cheapest or on-demand. In practice, make sure your NodePool includes instance types available in multiple AZs.


The Bottom Line

The Bottom Line

I've seen teams spend months migrating away from Kubernetes, only to land on a solution that costs more and does less. The problem isn't Kubernetes. The problem is how you're running it.

We're leaving Kubernetes might make sense for some teams. But if you understand how to reduce Kubernetes costs with Karpenter—really understand it, configure it right, monitor it closely—you can cut your bill in half without giving up the platform.

Start with one cluster. Migrate off Cluster Autoscaler. Configure spot instances with consolidation. Watch your utilization climb and your costs drop. Then do the next cluster.

I've been doing this since 2018. The math works. You just have to be willing to tune it.


Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with infrastructure?

Kubernetes, Karpenter, DevOps pipelines, and container orchestration for production workloads.

Explore MVP to Production