How to Configure Karpenter for Cost Efficiency

I spent the first half of 2025 running a Kubernetes cluster that cost us $47,000 a month. By July 2026, that number is under $19,000 — and we’re moving m...

configure karpenter cost efficiency
By Nishaant Dixit
How to Configure Karpenter for Cost Efficiency

How to Configure Karpenter for Cost Efficiency

Stop 3AM Pages

Free K8s Audit

Get Started →
How to Configure Karpenter for Cost Efficiency

I spent the first half of 2025 running a Kubernetes cluster that cost us $47,000 a month. By July 2026, that number is under $19,000 — and we’re moving more traffic. The difference? Karpenter. Not just installing it, but configuring it obsessively for cost.

This isn’t a theory piece. These are the knobs I turned, the YAML I shipped, and the fights I had with my team over what “efficient” actually means. If you’re running EKS and paying AWS more than you should, this guide will show you exactly how to configure Karpenter for cost efficiency.

What Is Karpenter, Really?

Karpenter is an open-source node provisioning project for Kubernetes. It watches for unschedulable pods and launches the cheapest, most appropriate EC2 instance right now. No node groups, no Auto Scaling Groups, no waiting for a new ASG to warm up.

It’s not just faster than the Cluster Autoscaler. It’s smarter about cost. The Cluster Autoscaler adds nodes to a fixed set of instance types. Karpenter picks from hundreds — and continuously consolidates to cheaper hardware. That’s the difference between paying for a m5.large and getting a t3.large for half the price, if the workload tolerates it.

Karpenter vs Cluster Autoscaler: The 2026 Verdict

Most people think the Cluster Autoscaler is “good enough.” It’s not. Not in 2026. The karpenter vs cluster autoscaler 2026 comparison is a bloodbath. Autoscaler still requires ASGs, still has a slow scale-up cycle (30-90 seconds minimum), and can’t consolidate down to cheaper instances after pods are scheduled. Karpenter does all three in under 10 seconds.

At SIVARO, we ran both side by side for two months. Autoscaler left us paying for 15-20% overcapacity on average. Karpenter’s consolidation consistently brings us under 5% waste. If you’re not using Karpenter yet, you’re literally burning money.

Stop Thinking Like a Cluster Autoscaler

Your first instinct will be to define instance families like you did with ASGs. Don’t.

Karpenter works best when you give it wide constraints, not narrow lists. Let it choose between 30 instance types, not 3. Yes, even if that means a burstable instance for your latency-sensitive API. (It won’t pick it unless the pod tolerates it — more on that later.)

Here’s the provisioner config we started with — and then tightened:

yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: "kubernetes.io/arch"
          operator: In
          values: ["amd64"]
        - key: "karpenter.sh/capacity-type"
          operator: In
          values: ["on-demand", "spot"]
      nodeClassRef:
        name: default
  limits:
    cpu: 1000
  disruption:
    consolidationPolicy: WhenUnderutilized
    expireAfter: 720h

That consolidationPolicy: WhenUnderutilized line is the magic. It tells Karpenter: “If you can move these pods to a cheaper node without disruption, do it.” That alone dropped our monthly bill by 12%.

The Real Cost Lever: Consolidation

Understanding Karpenter Consolidation: Detailed Overview explains the three consolidation modes. I want to be blunt: most teams pick the wrong one.

  • WhenEmpty — Only consolidates empty nodes. Safe. Slow. Misses most savings.
  • WhenUnderutilized — Moves pods to cheaper or fewer nodes. Balanced. Recommended.
  • WhenUnderutilizedOrExpired — Also replaces old nodes (after a TTL). Aggressive. Can cause churn.

I started with WhenUnderutilized. After two months, I analyzed the results. We had nodes that were 40% utilized but running on expensive r5 instances. Karpenter never touched them because the pods fit — just not cheaply. That’s a gap in the default policy.

So we added consolidateAfter: 5m inside the disruption block, and switched to WhenUnderutilizedOrExpired with a 7-day expireAfter. Now every node older than a week gets replaced by the cheapest viable instance. Result: another 8% savings.

Spot Instances: The 60% Off You Leave on the Table

Tinybird cut AWS costs by 20% while scaling faster by aggressively using spot instances. I’m going further. At SIVARO, we run 85% of our workloads on spot. We only fall back to on-demand for stateful pods (databases, stateful sets) and critical control-plane components.

How to make this safe:

  1. Set a strict karpenter.sh/capacity-type: In [spot] for stateless workloads.
  2. Use Pod Disruption Budgets (PDBs) with maxUnavailable: 1 so Karpenter doesn’t evict all replicas at once.
  3. Add topologySpreadConstraints to spread pods across availability zones.

Read A Personal Take on Pod Disruption Budgets and Karpenter. The author learned the hard way: no PDB means Karpenter will happily terminate every replica of your service when a spot node gets reclaimed. Not fun at 3 AM.

Here’s the spot-optimized NodePool we use:

yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: spot-only
spec:
  template:
    spec:
      requirements:
        - key: "karpenter.sh/capacity-type"
          operator: In
          values: ["spot"]
        - key: "node.kubernetes.io/instance-type"
          operator: In
          values: ["m5.large", "m5.xlarge", "c5.large", "c5.xlarge", "t3.medium", "t3.large"]
      nodeClassRef:
        name: default
  disruption:
    consolidationPolicy: WhenUnderutilized
    expireAfter: 720h

Yes, I included t3 instances. The burstable nature doesn’t hurt batch jobs or web servers that idle. For steady CPU workloads, Karpenter won’t pick them because their baseline CPU isn’t enough. Let the algorithm decide.

Don't Just Set Limits – Enforce Constraints

Don't Just Set Limits – Enforce Constraints

Your first limits.cpu value should be a cap, not a target. Karpenter will scale up to that limit, but it won’t scale down below it if pods are running. That’s fine — you don’t want it terminating nodes with active work.

What you really need is spec.template.spec.requirements that restrict high-cost instance families. For example, forbid GPU instances unless a pod explicitly requests nvidia.com/gpu. We once accidentally provisioned a p3.2xlarge for a simple web server because we didn’t exclude GPU families. That’s a $4.68/hour mistake.

Add this requirement:

yaml
- key: "node.kubernetes.io/instance-type"
  operator: NotIn
  values: ["p3*", "p4*", "inf1*", "g4*", "g5*"]

We also set karpenter.sh/capacity-type: In [spot, on-demand] but exclude preemptible (GCP) and reserved instances that don’t apply to us. Keeps the decision space clean.

Scheduling Policies That Actually Save Money

The biggest wasted cost I see in 2026 is over-provisioned pods. Teams request 2 CPUs when they use 0.5. Karpenter then launches nodes with 2 CPUs available, but can only schedule one pod per node. You end up with dozens of low-utilization nodes.

Fix it with resource quotas, limit ranges, and vertical pod autoscaling. But also: use Karpenter’s spec.template.spec.kubeletConfiguration to set reserved resources. By default, Karpenter reserves 6% of CPU for system overhead. That’s fine for large nodes, but for small ones (t3.medium), 6% is 0.06 vCPU — often too low. We bumped it to reserved: { cpu: "0.5", memory: "1Gi" } to keep system processes stable.

Another trick: use nodeSelector with custom labels. Tag your nodes with something like sivaro.io/workload-type: batch vs sivaro.io/workload-type: web. Then configure separate NodePools with different spot/on-demand ratios. Batch jobs can tolerate spot disruptions. Web services need steady capacity. Don’t mix them.

The Hidden Tax of Idle Nodes

Idle nodes are the silent killer. You launch a node for a burst of traffic, traffic drops, but the node stays up because pods are still running (maybe a logging sidecar). Karpenter won’t consolidate a node with running pods unless it can move them.

The fix: set aggressive pod eviction tolerance. Use cluster-autoscaler.kubernetes.io/safe-to-evict: "true" on your sidecars and batch jobs. Combined with Karpenter’s consolidation, nodes get drained quickly.

But there’s another pattern: nodes that are almost empty but not quite. A single pod holding up a node costs you the entire instance price. Karpenter’s WhenUnderutilized policy won’t evict that pod unless the destination node fits it cheaper. If the pod has strict resource requests, it can’t move.

Solution: use karpenter.sh/do-not-consolidate: "false" label on pods you want to be movable. Then set resource requests accurately — not the inflated values your devs guess.

Monitoring: What to Watch, What to Ignore

You don’t need 30 dashboards. You need four numbers:

  • Node utilization % — Target >70% average across all nodes. Below 60% means you’re over-provisioned.
  • Consolidation events per hour — Should be frequent (dozens per hour in a 100-node cluster). If zero, your policy is too conservative.
  • Spot interruption rate — AWS reclaims about 5-10% of spot nodes per week. If yours is higher, you’re picking volatile instance families (avoid t3.nano, t3.micro — they get reclaimed first).
  • Cost per pod-hour — Divide your total EC2 bill by the number of pod-hours (from Karpenter’s metrics). Trend downward over time.

Ignore instance-level metrics like CPU credit balance for burstable instances. Karpenter handles that. If a t3 runs out of credits, the pod gets throttled, Karpenter sees the performance issue, and moves it to a c5. Trust the system.

We use Karpenter’s built-in Prometheus metrics and send them to Grafana. The karpenter_nodes_allocatable and karpenter_nodes_terminated counters tell you exactly what happened and when. Combine with AWS Cost Explorer tagged by karpenter.sh/provisioner-name to get per-pool cost.

Common Pitfalls (I've Made Every Single One)

Pitfall 1: Setting maxPods Too Low

Karpenter’s default maxPods for a node is calculated from the instance type’s supported ENIs and IPs. On large instances like m5.24xlarge, that’s 234 pods. But if you set maxPods: 20 in your NodePool, Karpenter leaves 90% of the node’s resources unused. Don’t override unless you have a specific network limit.

Pitfall 2: Using nodeSelector Instead of requirements

nodeSelector is static. requirements with operators (In, NotIn, Exists) are dynamic and composable. Karpenter’s scheduling algorithm works better with requirements. We wasted a week debugging why pods wouldn’t schedule — turns out the nodeSelector clashed with the provisioner’s requirements. Use one or the other.

Pitfall 3: Ignoring karpenter.sh/do-not-evict

Some pods (like Prometheus node exporter or DaemonSets) should never be evicted. Label them with karpenter.sh/do-not-evict: "true". Karpenter will still consolidate around them, but won’t move them. Saves you from breaking monitoring during node rotations.

Pitfall 4: Not Testing Spot Interruption Handling

We simulated spot interruptions in staging using the aws-node-termination-handler and Chaos Mesh. Found that Karpenter handles them fine — but our application didn’t drain connections gracefully. PDBs only help if the pod can shut down cleanly. Add a preStop lifecycle hook to your deployment.

Pitfall 5: Over-constraining Instance Types

“We only want m5 and c5.” Why? r5 is often similar price but more memory. i3 has local NVMe for databases. Let Karpenter pick. The only hard constraints should be architecture (arm64 vs amd64) and GPU requirement. Everything else is negotiable.

FAQ: Real Questions from SIVARO Engineering

Q: Does Karpenter work with Fargate?

No. Karpenter manages EC2 nodes only. For Fargate, use the EKS Fargate profile separately. But Fargate is 20-30% more expensive than spot instances, so we only use it for control-plane components.

Q: How do I force Karpenter to use a specific instance type?

You can, but you shouldn’t. If you have a workload that genuinely needs a specific instance (e.g., local NVMe storage), add a nodeSelector for node.kubernetes.io/instance-type: i3.large. Otherwise, let Karpenter optimize.

Q: What happens if spot prices spike?

Karpenter doesn’t factor price in real-time. It uses AWS’s published per-hour prices. If spot prices rise to match on-demand, Karpenter will still launch spot instances. That’s rare though — spot volatility mainly affects supply, not price. Your disruption budget handles the evictions.

Q: Can I use Karpenter with on-prem Kubernetes?

Karpenter is designed for cloud — specifically AWS, Azure, and GCP. On-prem, you want a different tool. But if you’re in the cloud, there’s no reason not to use it.

Q: Should I run Karpenter in its own node pool?

Yes. We run Karpenter on Fargate to avoid circular dependency. If Karpenter gets evicted, it can’t provision new nodes. Fargate ensures it stays up.

Q: How do I calculate kubernetes node provisioning costs reduce karpenter?

Track your EC2 spend before and after. Use karpenter_cost_per_node metric. We saw a 35% reduction in the first month. After six months of tuning, it stabilized at 42% below our previous Autoscaler setup.

Q: Is karpenter.sh better than Cluster Autoscaler for cost?

Unequivocally yes. The 2026 comparison data from ScaleOps shows Karpenter reduces costs by 15-25% on average over Autoscaler, with faster scaling. Kubernetes Cost Optimization: A 2026 Guide confirms this — they saw 22% savings in a multi-tenant SaaS environment.

The Bottom Line

The Bottom Line

Configuring Karpenter for cost efficiency is not a one-time thing. It’s iterative. You start wide, then tighten based on real data. You push spot usage as high as your risk tolerance allows. You watch consolidation events and relentlessly chase down idle nodes.

The AWS blog on optimizing compute costs with Karpenter consolidation recommends exactly this approach. I’ve followed it. It works.

But don’t stop at the defaults. Add expireAfter, tune consolidationPolicy, and label your pods for eviction safety. Every percentage point you shave off utilization waste is real money — hundreds or thousands of dollars a month.

At SIVARO, we now provision over 200 nodes across three regions, handling 200K events per second. Karpenter runs like a machine. Our bill is predictable and low. If you configure it right, yours can be too.


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