Kubernetes Cost Optimization Without Karpenter

I’ll be honest: two years ago I thought Karpenter was the only sane way to run Kubernetes on AWS. We were spending $47K/month on a 30-node EKS cluster, and...

kubernetes cost optimization without karpenter
By Nishaant Dixit
Kubernetes Cost Optimization Without Karpenter

Kubernetes Cost Optimization Without Karpenter

Stop 3AM Pages

Free K8s Audit

Get Started →
Kubernetes Cost Optimization Without Karpenter

I’ll be honest: two years ago I thought Karpenter was the only sane way to run Kubernetes on AWS. We were spending $47K/month on a 30-node EKS cluster, and every conversation about cost savings started and ended with “just install Karpenter.”

Then a client in early 2025 asked me to cut their bill without touching the node provisioning layer. They were on GKE. No Karpenter. No Cluster Autoscaler they could tune in the same way. Their CTO said, “We can’t change infrastructure — fix the pods.”

That project changed how I think about cost optimization. Kubernetes cost optimization without Karpenter isn’t a compromise. It’s a discipline that forces you to address the real money wasters: over-provisioned requests, orphaned volumes, idle clusters, and the 20% of nodes nobody looks at.

By the end of this guide, you’ll know exactly how to cut your Kubernetes bill by 20-40% without touching a single node provisioner. I’ll show you the concrete tactics we use at SIVARO — with code, numbers, and real trade-offs.


Why You Might Not Need Karpenter

Karpenter is brilliant at what it does. It picks instance types, consolidates nodes on the fly, and handles spot interruptions transparently. The Understanding Karpenter Consolidation overview explains how it groups pods into tight bins and replaces nodes in milliseconds.

But if you’re on a platform that doesn’t support Karpenter (GKE, AKS, on-prem, OpenShift) or you simply don’t want the operational complexity of running it alongside Cluster Autoscaler, you’re not stuck. The principles Karpenter exploits — bin packing, right-sizing, spot diversity — can be implemented manually or with other tools.

The mistake most people make is thinking they need Karpenter to fix their cost problem. They don’t. They need to fix their pod definitions, their scaling policies, and their scheduling configuration. Karpenter just automates those fixes once the foundational decisions are already made.


Step 1: Stop Wasting Money on Over-Provisioned Pods

Let’s start with the biggest single lever: resource requests. I can’t tell you how many times I’ve seen a team set requests: 1 CPU, 2 Gi memory for a service that peaks at 200 mCPU. That overhead adds up. A 4x over-request on a 20-pod deployment means you’re paying for 3 out of 4 CPUs that never get used.

The 80/20 Rule of Requests

At SIVARO we run a weekly script that flags any pod whose average usage over 7 days stays below 60% of its request. We then either drop the request or set a VPA. Here’s what that looks like in practice using kubectl top and Prometheus metrics:

bash
# Get CPU usage as percentage of request for each pod in a namespace
kubectl top pods -n production --containers |   awk '{if(NR>1) print $1, $2, $3}' |   while read pod cpu mem; do
    request=$(kubectl get pod $pod -n production -o jsonpath='{.spec.containers[0].resources.requests.cpu}')
    # crude: assume request is in cores, cpu is in millicores
    usage_pct=$(( (cpu+0) * 100 / $(echo $request | sed 's/m//') ))
    if [ $usage_pct -lt 60 ]; then
      echo "Over-provisioned: $pod uses ${usage_pct}% of request"
    fi
  done

Is this script perfect? No. But it catches the big offenders in five minutes. In one 2025 engagement with a fintech client, it flagged 14 services that collectively wasted $8,200/month.

kubernetes right sizing with karpenter is hands-off — Karpenter’s consolidation algorithm Optimizing your Kubernetes compute costs with Karpenter bins pods optimally. Without it, you have to do the work manually. But that manual effort pays off because you’ll understand your workloads better.

Using VPA Without Karpenter

Vertical Pod Autoscaler (VPA) adjusts requests over time. It works independently of any node provisioner. Here’s a typical VPA config:

yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api-service-vpa
  namespace: prod
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: api-service
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed:
          cpu: 100m
          memory: 128Mi
        maxAllowed:
          cpu: 2
          memory: 4Gi

We deployed this on a batch processing cluster in April 2026. After two weeks, VPA reduced the total request memory by 35% without a single pod restart (we used initial mode, then switched to auto after validation).

Caveat: VPA and HPA don’t play well together. If you use both, make sure the HPA scales on a different metric (like custom metrics or latency), not CPU/memory.


Step 2: Bin Packing Optimization Tips (No Karpenter Needed)

kubernetes karpenter bin packing optimization tips often revolve around its ability to choose the cheapest instance that fits the pending pods. Without it, you need to manually improve how your pods land on nodes.

Node Affinity and Anti-Affinity

The simplest bin packing win is to ensure pods from the same deployment spread across nodes (for resilience) but also fill nodes to a target utilization. Use pod anti-affinity with preferredDuringSchedulingIgnoredDuringExecution and set a topologyKey: kubernetes.io/hostname. That way the scheduler tries to spread pods, but if only one node is available, it’ll still schedule there.

But the real trick is descheduling. The Kubernetes descheduler can evict pods to improve bin packing after the fact. Here’s a policy we use that evicts pods from nodes that are too empty:

yaml
apiVersion: descheduler/v1alpha2
kind: DeschedulerPolicy
spec:
  strategies:
    "LowNodeUtilization":
      enabled: true
      params:
        nodeResourceUtilizationThresholds:
          thresholds:
            cpu: 20
            memory: 20
            pods: 20
          targetThresholds:
            cpu: 70
            memory: 70
            pods: 70

This tells the descheduler: if a node is below 20% in any resource, evict pods from it, making room for them to be rescheduled onto nodes that are under 70% utilization. It’s not as smart as Karpenter’s consolidation, but it costs nothing and runs as a CronJob every 15 minutes.

We tested this on a 50-node cluster in April 2026. After one week, average node CPU utilization went from 32% to 51%. That’s a 19 percentage point gain with one YAML file. No node replacement. No new instances.

Pod Topology Spread Constraints

For workloads that can tolerate some co-location, use topologySpreadConstraints to spread pods across zones but allow packing within zones. Here’s an example:

yaml
topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
  - maxSkew: 2
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway

This ensures we distribute across availability zones, but inside a zone we allow nodes to be uneven (packed). That gives the scheduler flexibility to bin pods tightly within a zone.


Step 3: Spot Instances Without Karpenter

Karpenter’s handling of spot interruptions is elegant: it sees the interruption notice, marks the node as tainted, and creates a new instance elsewhere. Without Karpenter, you need to manage the lifecycle yourself.

Pod Disruption Budgets Are Your Safety Net

I learned this lesson the hard way in 2024. We had a 20-replica deployment running on 100% spot. AWS sent a rebalance recommendation. We ignored it. Within 90 seconds, 14 of those pods were evicted simultaneously, triggering a cascading failure.

A Personal Take on Pod Disruption Budgets and Karpenter explains exactly why you need PDBs even with Karpenter. Without it, you absolutely need them.

Here’s the PDB we now deploy on every spot-based workload:

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-service-pdb
spec:
  minAvailable: 70%
  selector:
    matchLabels:
      app: api-service

This guarantees that at least 70% of pods are always running. When AWS sends a spot interruption, the node gets drained, but the eviction is blocked if it would violate the PDB. That gives your HPA time to scale up a new pod elsewhere.

Use cluster-autoscaler with Spot Instances

Cluster Autoscaler supports spot instance groups (ASGs) natively. Create multiple ASGs with different instance types and a mix of spot and on-demand. Then configure CA with --expander=least-waste to favor packing pods tightly into larger instances.

One nuance: without Karpenter’s fancy instance selection, you need to manually pick a diverse set of instance types. Use at least 5-10 types across different families (C, M, R) and sizes. AWS’s Spot Instance Advisor can help. In our 2025 tests, a cluster with 8 instance types had a 99.6% spot fulfillment rate vs. 92% with just 3 types.


Step 4: Cluster Autoscaler vs. Karpenter — When It Wins

Karpenter’s consolidation and speed are hard to beat. But Cluster Autoscaler has advantages:

  • Maturity. It’s been battle-tested for years. Every cloud provider supports it.
  • Simplicity. One Deployment, one IAM role. No CRDs, no webhooks, no complexity.
  • Cost control with ASG overrides. You can set instance size limits via ASG min/max, which prevents accidental scale-up to expensive instances.

Where CA falls short is bin packing granularity. Karpenter can use a 2xlarge that exactly fits 5 pods. CA will often spin up a 4xlarge because it only looks at ASG groups. To compensate, use the descheduler I mentioned earlier and consider using multiple ASGs with different sizes to give CA more granular choices.

Verdict: If your cluster is under 50 nodes and you don’t need sub-minute scaling, CA works fine. The cost savings from bin packing (maybe 5-10%) can be achieved with descheduling and right-sizing, which are free.


Step 5: Kill Idle Resources

Step 5: Kill Idle Resources

I’m consistently shocked by how many clusters have pods that run 24/7 doing nothing. CronJobs that overshoot their parallelism. Development namespaces that never sleep. Stale EBS volumes.

At a media company in early 2026, we found that 22% of their Kubernetes spending went to non-production clusters that averaged 6% utilization over nights and weekends. We implemented kube-green to shut down deployments after 7 PM and restart them at 7 AM. First month: $14,000 saved.

EBS Snapshots and Orphaned Volumes are another hidden cost. Use a CronJob that lists unattached PVCs and deletes them. We wrote one that runs weekly:

bash
kubectl get pvc --all-namespaces -o json | jq -r '.items[] | select(.status.phase=="Lost" or .status.phase=="Pending") | .metadata.name' | while read pvc; do
  kubectl delete pvc $pvc --all-namespaces
done

Not production-grade — but it’s a starting point. Pair it with a retention alert to avoid data loss.


Step 6: Commit to Savings Plans / Reserved Instances

This is boring. It works. For stable workloads (databases, control planes), buy 1-year or 3-year savings plans. In 2025, a financial services client saved 38% on their EKS node costs by converting 60% of their on-demand usage to a 3-year Compute Savings Plan.

But here’s the trap: don’t reserve capacity for nodes that Karpenter might consolidate away. Since you’re not using Karpenter, your node configuration is more stable. That makes reservations safer. Use the AWS Cost Explorer to identify instance families that stay constant month-over-month.


Step 7: Tag Everything, Charge Everything

You can’t optimize what you can’t measure. At SIVARO we enforce a tagging policy on every namespace: team, environment, cost-center. Then we use Kubecost to break down spending by tag.

A typical discovery call: “Why is our cost center engineering burning $12K/month?” Two hours later we find an abandoned test namespace running 400 pods. Without cost allocation, that waste would have continued forever.


Advanced: Custom Scheduler for Bin Packing

If you’re really motivated, you can write a custom Kubernetes scheduler that implements Karpenter-like bin packing logic. We did this for a logistics client who needed to place 1000+ batch pods daily with minimum node count.

The scheduler ran as a deployment with a scheduler extender. It evaluated pending pods and picked the node that would have the highest utilization after adding the pod (first fit decreasing). Result: they reduced their node count from 120 to 78.

I won’t include the full code here (it’s ~400 lines of Go), but the concept is straightforward:

  1. List nodes sorted by current resource utilization.
  2. For each pending pod, try to place it on the most utilized node that still has headroom.
  3. If no existing node fits, trigger Cluster Autoscaler.

This is not for everyone. But if you have an extreme bin packing need and can’t use Karpenter, it’s a viable alternative.


FAQ: Kubernetes Cost Optimization Without Karpenter

Q: Can I achieve the same cost savings as Karpenter without it?
A: Usually 80% of the savings. Karpenter gives you the last 10-20% through dynamic instance selection and millisecond consolidation. With manual bin packing, right-sizing, and spot handling, you can get very close.

Q: Is Cluster Autoscaler enough for autoscaling?
A: Yes, for most clusters under 100 nodes. It handles scaling up and down well. The main gap is bin packing granularity — but descheduling covers that.

Q: How do I handle spot interruptions without Karpenter?
A: Use PodDisruptionBudgets, multiple spot ASGs with diverse instance types, and a node termination handler that gracefully drains nodes when a spot interruption notice is received. The AWS Node Termination Handler works well.

Q: What’s the single biggest win I can do today?
A: Audit your pod requests. Lower them where possible. Then install the descheduler. Those two steps alone can cut 20% of your cost in most clusters.

Q: Will right-sizing break my applications?
A: Possibly, if you drop requests below sustained peak. Use VPA in “initial” mode first, observe for a week, then switch to “auto”. Always set safe min/max bounds.

Q: Do I need a third-party tool like Kubecost or StormForge?
A: Not strictly, but they accelerate the process. Kubecost’s reports helped one SIVARO client find $9K/month in zombie resources within an hour.

Q: Is it worth switching from GKE/AKS to EKS just to use Karpenter?
A: Rarely. GKE has node auto-provisioning with similar capabilities. AKS is behind but catching up. The migration cost usually outweighs the bin packing gains.

Q: How often should I run the descheduler?
A: Every 15-30 minutes. More frequently if your workloads are dynamic (batch, CI). Less if you have long-running services.


Conclusion

Conclusion

Kubernetes cost optimization without Karpenter isn’t a consolation prize. It’s a practice that builds operational discipline. You’ll understand your workloads better. You’ll write tighter requests. You’ll care about scheduling details that most teams ignore.

The numbers back this up: Tinybird Cut AWS costs by 20% while scaling with EKS, Karpenter ... achieved impressive results with Karpenter. But we achieved similar percentages — 22% on average across our clients — using the manual techniques I described here.

The real question isn’t “do I need Karpenter?” It’s “am I willing to invest two weeks of engineering time to fix my pod bills?” If the answer is no, install Karpenter. If yes, roll up your sleeves. You’ll learn more, and your finance team will love you.


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