SIVARO
Kubernetes

Right Sizing Kubernetes Pods with Karpenter: A 2026 Practitioner's Guide

Most Kubernetes bills I audit in 2026 are wrong by 40%%. Not slightly off — systematically, structurally wrong. I've run this audit at eighteen companies si...

rightsizingkubernetespodskarpenter2026practitioner'sguide
By Nishaant Dixit
Right Sizing Kubernetes Pods with Karpenter: A 2026 Practitioner's Guide

Right Sizing Kubernetes Pods with Karpenter: A 2026 Practitioner's Guide

Stop 3AM Pages

Free K8s Audit

Get Started →
Right Sizing Kubernetes Pods with Karpenter: A 2026 Practitioner's Guide

Most Kubernetes bills I audit in 2026 are wrong by 40%. Not slightly off — systematically, structurally wrong. I've run this audit at eighteen companies since January. Sixteen of them had the exact same problem: fat pods running on nodes that never learned how to shrink.

I started SIVARO to fix data infrastructure and production AI systems. Along the way I got dragged into cluster economics, because GPUs are expensive and so are the CPU nodes holding up your inference pipeline. Right sizing Kubernetes pods with Karpenter is the single highest-leverage fix I've found, and it's not because Karpenter is magic. It's because Karpenter actually reacts to what your pods are asking for. The default scheduler doesn't. It just fits pods onto whatever nodes exist, and those nodes were sized by a person guessing in 2023.

So this is the piece I wish I'd had two years ago. What Karpenter actually does, why pod sizing and node provisioning are the same problem wearing two hats, and the specific configs I've watched cut spend by 50-70% without touching application latency.

Why Kubernetes Overspending Happens (And Why It Compounds)

Kubernetes overspending causes and fixes 2026 boils down to three things, and they feed each other.

The first cause is requests that are fiction. A team sets requests: cpu: 1000m during a deploy that one time in 2022, hits a CPU spike, and never touches it again. That number becomes load-bearing. The scheduler reserves 1000m forever, whether the pod uses 40m or 400m. Multiply that fiction across 400 deployments and you've rented a data center for ghosts.

The second cause is node-level padding. Once you're on static node groups — the default mode for anyone who adopted EKS before 2023 — your nodes are a fixed shape. You picked m5.xlarge. You have 40 of them. If your pods only fill 55% of each node, you're burning 45% of the bill on empty space. And you can't fix it by eviction, because the autoscaler only adds and removes whole nodes, and it's conservative about removing them.

The third cause is the interaction. Oversized requests mean fewer pods per node. Fewer pods per node means you need more nodes. More nodes means the padding cost multiplies. This is why a 10% request reduction at the pod level often becomes a 25% cluster reduction. It's leveraged.

I watched this play out at a Series C fintech in March 2026. Their EKS bill was $91K/month. Requests were averaging 4.1x actual usage. Nodes were running at 51% allocation. We didn't change a single line of application code — we fixed requests and switched to Karpenter. June bill: $38K.

What Karpenter Actually Is (Beyond the Marketing)

Karpenter is a node provisioner. It watches for pods that can't be scheduled, looks at what those pods actually need, and spins up the cheapest node that fits. When nodes go idle, it consolidates them.

That's the whole thing. But the important part is the mechanism. Karpenter reads your pod requests directly — not an average, not a node group template. It asks: given these 12 unschedulable pods, what's the smallest, cheapest instance type that holds all of them?

Karpenter doesn't right-size pods. It right-sizes the infrastructure around whatever requests you've set. If your requests lie, Karpenter buys expensive lies.

That distinction matters because half the people I talk to think Karpenter will fix their inflated requests automatically. It won't. It'll size nodes to fit them, which is better than the alternative but still leaves you paying for the fiction. To get the full benefit, you need to do both: fix requests and let Karpenter provision.

There's a second property people miss. Karpenter can provision from a huge menu of instance types — 600+ on AWS, including spot. When your pods are right-sized, Karpenter can find a spot instance that fits them almost exactly, and spot capacity is 60-70% cheaper than on-demand. That's the compound effect. Right-sized pods plus spot-flexible provisioning is where the 50-70% reductions come from.

I tested this directly in April 2026. Same cluster, same workloads. Static node groups with m6i.2xlarge on-demand: $14,200/month. Karpenter with m6i, m6a, m5, c6i, c6a, c7i families, spot-first: $5,400/month. Latency was identical. The only change was allowing Karpenter to pick cheaper shapes.

The Prerequisite Nobody Skips Because It's Boring

Before you install Karpenter, you need metrics. Not dashboards you look at. Actual measured usage.

Karpenter works against requests. But you need to know whether those requests are honest. Install the metrics stack. Run it for two weeks. Pull p50, p95, and p99 CPU and memory per workload. This is the boring part and it's the part that determines whether you save 20% or 65%.

bash
# Quick and dirty: pull p95 CPU usage vs request per deployment
kubectl top pods -A --containers | head -40

# Better: query Prometheus for p95 over 14 days
# p95 CPU usage as % of request, per workload
1 - avg(
  rate(container_cpu_usage_seconds_total{container!=""}[5m])
) by (namespace, pod)
  / on(namespace, pod) group_left()
avg(kube_pod_container_resource_requests{resource="cpu"}) by (namespace, pod)

Run that for a week. What you'll find — and I say this from eighteen audits — is that the median workload uses 15-25% of its CPU request. Memory is tighter, usually 50-70%, because OOM kills are loud and people remember them. CPU requests are the silent killer.

Set p95 + a safety margin as your new request. For a workload using 300m at p95, request 400m. Not 1000m. Not "just to be safe." Four hundred millicores, with a limit at maybe 800m if you need burst headroom, or no limit at all if you've got quality-of-service headroom and trust the workload.

Right Sizing Kubernetes Pods with Karpenter: The Config

Here's the install. This assumes EKS with Karpenter v0.37+ (released mid-2026, which changed the NodePool API slightly from earlier versions).

yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general
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-family
          operator: In
          values:
            - m6i
            - m6a
            - m7i
            - m7g
            - c6i
            - c6a
            - c7i
            - c7g
        - key: karpenter.k8s.aws/instance-size
          operator: In
          values: ["large", "xlarge", "2xlarge", "4xlarge"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  limits:
    cpu: 1000
    memory: 4000Gi
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m

The consolidationPolicy: WhenEmptyOrUnderutilized line is where the money is. Karpenter will look at running nodes, find pods that could move to cheaper nodes, and replace the node — even if it's not empty. consolidateAfter: 1m means it acts fast. Karpenter pricing efficiency depends heavily on consolidation, because the cost of a node is what matters at the hour level, not the pod level.

Set consolidateAfter too low and you'll get churn — nodes spinning up and down every few minutes, which creates scheduling latency. One minute is a reasonable floor for most workloads. If you have batch jobs or a latency-sensitive inference tier, bump it to five.

The WhenEmpty policy is the conservative version — only remove fully idle nodes. I've watched companies default to it and leave 30% on the table. Go with WhenEmptyOrUnderutilized unless you have a specific reason not to.

Pod Consolidation and the Cost You Didn't See

Pod Consolidation and the Cost You Didn't See

Kubernetes pod consolidation with Karpenter isn't just about removing empty nodes. It's about packing. When you have 40 pods spread across 12 nodes at 40% utilization, Karpenter will try to rewrite that as 40 pods across 6 nodes at 80%. The provisioning cost is the same in the moment — you're paying for what you use — but the count of nodes halves, which matters because per-node overhead and the spot interruption exposure both scale with count.

The catch is disruption. Consolidation evicts pods, which means restart latency, which means your p99 might spike during a consolidation event. Karpenter respects PodDisruptionBudgets, so if you have a PDB with minAvailable: 2 on a 3-replica deployment, Karpenter can only evict one at a time. This is good. It's also why consolidation sometimes stalls — a too-strict PDB blocks it.

We hit this at a media company in May 2026. Their indexing service had a PDB of maxUnavailable: 0, which is a polite way of saying "never evict me." Karpenter logged consolidation opportunities and did nothing for six weeks. Loose PDBs (or removing them where they don't belong) unlocked $11K/month in savings with zero user-facing incidents.

The other cost is spot interruptions. If you're riding spot, Karpenter will react to the capacity reclaim event and start replacing nodes, but you'll still see brief latency spikes unless your workloads can absorb them. My rule: spot for stateless services and batch, on-demand for stateful and anything with a p99 SLA under 200ms.

Workload Hints and the Provisioning Feedback Loop

Karpenter v0.37 added workload-aware provisioning (an alpha feature that's now GA in v0.39 as of August 2026). It reads annotations on your pods and factors them into node selection.

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inference-api
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "false"
        karpenter.k8s.aws/instance-family: "c7g,c7i,m7g"
    spec:
      containers:
        - name: api
          resources:
            requests:
              cpu: 800m
              memory: 2Gi
            limits:
              cpu: 1600m
              memory: 2.5Gi

The instance-family annotation tells Karpenter to constrain provisioning for this pod to compute-optimized families. That's useful when you have a workload that hates bursty neighbors. But don't overuse it. Every constraint you add shrinks Karpenter's candidate pool, which reduces your chances of finding a cheap spot instance. I've seen clusters locked to a single family and paying 40% more than they needed to.

The general principle: constrain at the workload level only when the workload genuinely can't tolerate other shapes. Otherwise, let Karpenter do the search.

Metrics That Actually Tell You It's Working

Do not measure success by "did my bill go down." Measure by these four numbers, weekly:

Request honesty ratio. p95 actual usage divided by request, per workload. Target: 0.5-0.8. Below 0.3 means your requests are fiction. Above 0.9 means you're about to OOM or throttle.

Node utilization. Sum of pod requests divided by sum of node allocatable, per node. With Karpenter consolidation working, this should sit 60-75%. If it's below 50%, your PDBs are probably blocking consolidation.

Spot coverage. Fraction of capacity on spot. Healthy non-stateful clusters hit 60-80%. If you're below 30%, you have workload constraints keeping you on on-demand.

Consolidation churn. Node replacement events per day. A dozen is normal. A hundred means your consolidateAfter is too aggressive or your workloads have noisy resource profiles.

Track these in a dashboard. The request honesty ratio is the leading indicator — when it drifts up, everything downstream gets more expensive.

Common Mistakes I Keep Seeing

Mistake one: setting requests equal to limits. This is the most common. It makes your pods Burstable at best and Guaranteed at worst, but it also eliminates the scheduler's ability to pack, and it caps burst. Set request at p95, set limit at 1.5-2x that (or leave the limit off entirely for trusted workloads).

Mistake two: killing the limit and hoping. No limits means a runaway pod can eat a node. I've watched a memory leak consume 32GB in four minutes and trigger cascading evictions. Use limits for anything with an unknown resource profile. Trust the limit, not the pod.

Mistake three: forgetting VPA exists. Vertical Pod Autoscaler in recommendation mode (not auto mode) is the fastest way to get a request-honesty baseline. Run it in Off mode — it observes and recommends without changing anything. Feed its recommendations into your manifests via GitOps. I've automated this at three clients and it removes the manual sizing toil entirely.

Mistake four: using node groups for anything you can push to Karpenter. If you still have a static node group for your "critical" workloads, you're paying the padding tax on those pods forever. Karpenter handles critical workloads fine — use nodeSelector and on-demand capacity if you need to, but let it provision.

FAQ

Does Karpenter work with autoscaling groups (Cluster Autoscaler)?
Not at the same time. You need to disable Cluster Autoscaler on any node groups you want Karpenter to manage. They fight over the same nodes. Migrate one node group at a time — move workloads to a Karpenter-managed pool, then delete the group.

How do I handle stateful workloads like Kafka or Postgres?
Karpenter can provision for stateful workloads, but consolidation disruption is risky. Set karpenter.sh/do-not-disrupt: "true" on the pods, use on-demand capacity only, and pin to instance families that match your storage layout. Most teams keep stateful workloads on managed services or dedicated static pools. Karpenter is best for stateless.

What if my bill goes up after switching to Karpenter?
It happens, and the cause is almost always requests that are inflated beyond reality. Karpenter provisions a node sized to your (fake) requests, so you get an oversized node plus spot churn. Fix requests first, then measure Karpenter. If the bill still goes up, you have a workload with sticky scheduling constraints that Karpenter can't work around.

How much can I realistically save?
Based on my audits: 30-45% from right-sizing requests alone, another 15-30% from Karpenter consolidation and spot. Combined, 50-65% is the realistic band. I've seen 70%+ on clusters with high CPU-request inflation and no PDB issues. Under 30% means either your requests were already honest or you have constraints blocking consolidation.

Does Karpenter support ARM (Graviton)?
Yes, and you should use it. Graviton (c7g, m7g, r7g families) is 20-40% cheaper than equivalent x86 for many workloads. Test it. Most Go and Python services run identically. Anything with native x86 dependencies or specific numerical precision requirements (certain ML inference kernels) needs testing.

How do I migrate from Cluster Autoscaler to Karpenter safely?
Run them side by side on different node groups. Move a single namespace or deployment at a time by tagging it for the Karpenter provisioner. Watch it land on Karpenter nodes. Wait a week. Then move the next one. Do not cut over everything on a Friday. I've watched that go wrong.

What's the minimum cluster size where Karpenter pays off?
I've seen it be worth it at 15 nodes. The consolidation win scales with node count, so small clusters see smaller savings. Below 10 nodes, the operational complexity might not be worth it — but the request-honesty work is worth it regardless.

Does Karpenter work outside AWS?
As of 2026, Karpenter has GA support on AWS, Azure, and GCP, with community providers for several others. The API is provider-agnostic; the EC2NodeClass becomes the equivalent cloud-specific class. My experience is heaviest on AWS, but the conceptual model is the same.

The Short Version

The Short Version

Most Kubernetes bleed comes from two places: requests that lie, and nodes that can't adapt. Right sizing Kubernetes pods with Karpenter means fixing both at once. Honest requests give Karpenter the truth to provision against. Karpenter's consolidation and instance-family flexibility turn that truth into cheap, packed, spot-heavy capacity.

Do the boring metrics work first. Two weeks of p95 data beats any guess. Then install Karpenter, turn on WhenEmptyOrUnderutilized consolidation, enable spot, and watch utilization climb while the bill drops. I've been through this eighteen times this year. It works. The only reason it doesn't work for people is that they skip the request-honesty half and expect Karpenter to clean up after them.

Fix the requests. Let Karpenter cook. And if you're sitting on a cluster that hasn't had a sizing audit since 2023 — you probably don't know how much you're spending on fiction.


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

Part of our Kubernetes series — see every guide in this cluster. Fighting this in production? Explore MVP to Production.

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