Key Takeaways
  • Kubernetes clusters typically run at 35 to 45 percent average utilization, meaning over half of provisioned EKS compute often goes unused.
  • Rightsizing workload requests and limits, not adding more nodes, is the highest-leverage EKS cost optimization step available to most teams.
  • Karpenter’s bin-packing and consolidation features reduce node count, but still require ongoing manual tuning to stay effective as workloads change.
  • Buying a Savings Plan against a fixed, once-a-year estimate locks in whatever waste existed at signing time; continuous, daily-adjusting commitment automation avoids that lock-in entirely.
  • Zesty automates rightsizing, bin-packing, and Savings Plan allocation continuously, cutting Kubernetes compute costs by 50 to 80 percent without manual tuning.

Introduction

What drives EKS costs, and how do you actually control them? Compute, specifically the EC2 instances backing your pods, is the largest line item in most Kubernetes bills, and EKS cost optimization comes down to one fact: continuous adjustment controls cost, not a one-time sizing estimate made at deploy time. Clusters drift out of their original sizing within weeks as traffic patterns, deployments, and team habits change, and a cluster sized correctly in January is rarely still correctly sized by March.

This guide follows a three-tier priority order, in the sequence that actually saves the most money: rightsize workloads first, reduce unused node capacity second, and optimize how compute commitments are purchased third. Skipping ahead, adding autoscaling before fixing request sizes, or buying a commitment before either, wastes effort on the wrong layer. Kubernetes compute costs respond to this order specifically because each tier compounds on the one before it.

The temptation is always to start with the most visible lever: adding nodes when a cluster feels slow, or locking in a large annual commitment to chase a headline discount. Both moves can make sense eventually, but applied before the underlying request and limit values are accurate, they just mean paying discounted prices for capacity that was oversized in the first place. The order matters as much as the actions themselves.

Rightsize Workloads Before You Touch Infrastructure

EKS rightsizing starts with a simple, uncomfortable fact: most workloads request far more CPU and memory than they use, and that gap, not node count or instance pricing, is the root cause of most Kubernetes waste. When a workload requests 2 CPU cores but consistently uses 400 millicores, the cluster reserves the full 2 cores whether or not the pod ever touches them. Multiply that gap across every service, sidecar, and init container in a cluster and the waste compounds fast.

Resource requests and limits are usually set once, at whatever number a developer guessed felt safe, and rarely adjusted again as real usage data accumulates. This applies just as much to sidecars, service meshes, logging agents, and monitoring containers as it does to the main application container. A cluster with 50 services each running an oversized sidecar can lose more aggregate capacity to sidecars than to the applications themselves, simply because nobody audits the small containers with the same scrutiny as the primary one.

A handful of tools exist specifically to estimate correct request and limit values from real usage data:

  • Goldilocks (built on the Vertical Pod Autoscaler, or VPA) visualizes recommended requests against actual usage in a dashboard, making it easy to spot the worst offenders across a namespace.
  • KRR (Robusta KRR) is an open-source, agentless recommender that analyzes historical Prometheus data and outputs suggested CPU and memory values without deploying anything into the cluster.
  • Kubecost combines cost allocation with rightsizing recommendations, useful when the goal is connecting wasted spend back to a specific team or product.

Each of these tools is a strong starting point for building visibility into where requests and actual usage diverge, and running more than one in parallel is common practice, since Goldilocks’ dashboard view and KRR’s point-in-time report answer slightly different questions for a platform team.

The safest rollout path is running VPA in audit-only mode first, letting it observe and recommend without enforcing anything, then validating those recommended values in a non-production environment before pushing changes to a running production workload. Skipping the non-prod validation step is where most rightsizing efforts go wrong, since a request set too tight in production causes throttling or Out of Memory (OOM) kills instead of savings, and the cost of an incident caused by an overly aggressive rightsizing change usually outweighs several months of the waste it was meant to fix. The core principle worth remembering: rightsizing is a continuous process that has to run on an ongoing basis, not a one-time task completed during a migration or a quarterly cost review. Zesty’s approach to multi-dimensional autoscaling treats it exactly that way, adjusting pod-level requests continuously based on real usage instead of a snapshot taken once and left unchanged for months.

Reduce Unused Capacity With Autoscaling and Bin-Packing

Fixing request sizes only helps if the nodes underneath those pods are also packed efficiently. Kubernetes bin packing is the practice of scheduling pods onto the fewest nodes that can safely hold them, and it’s the second tier of cost control after rightsizing, because a cluster full of correctly-sized pods can still waste enormous capacity if those pods are scattered thinly across too many nodes.

Karpenter and Cluster Autoscaler take different approaches to node provisioning. Cluster Autoscaler works from predefined node groups and scales within their limits, while Karpenter provisions nodes directly based on the exact resource shape pending pods need, without being tied to fixed instance-type groups.

FactorKarpenterCluster Autoscaler
Provisioning modelDirect, just-in-time node provisioning matched to pending pod shapeScales predefined node groups within configured limits
Config overheadLower ongoing tuning, but requires defining provisioning constraints upfrontHigher ongoing tuning as node group definitions age
Bin-packing supportNative consolidation, actively repacks and terminates underused nodesLimited, primarily scales up and down, doesn’t actively repack

Karpenter consolidation actively identifies underused nodes and either moves their pods elsewhere or terminates them outright, which is where much of its cost advantage comes from. That advantage carries a real trade-off: aggressive consolidation can briefly slow down pod scheduling during scale events, since pods may need to be rescheduled onto a different node as consolidation runs. Overly restrictive Pod Disruption Budgets (PDBs) compound this problem by blocking the very scale-down actions consolidation depends on, leaving nodes running well under capacity because the scheduler isn’t permitted to safely move their pods.

Even a well-configured autoscaler misses waste that happens after initial scheduling. A pod that lands correctly on a node can still leave that node underused once neighboring pods finish and scale down, and this is where a descheduler plays its role, periodically re-evaluating placement and evicting pods that could be consolidated onto fewer nodes. Descheduling and Karpenter consolidation solve overlapping but distinct problems: consolidation acts at the node level, deciding whether a node can be safely terminated, while a descheduler acts at the pod level, deciding whether an individual pod’s current placement is still the most efficient option available. Running both together closes more of the gap than either does alone.

Consider a concrete example: a 10-node cluster running at 60 percent average utilization is still wasting 40 percent of its provisioned capacity, every hour, regardless of how well the workloads inside it are individually rightsized. That waste sits entirely at the node-packing layer, invisible to any tool that only looks at pod-level requests, which is exactly why rightsizing and bin-packing have to be treated as two separate problems rather than one combined fix.

The pattern across all of this is consistent: the autoscaler and descheduler provide the mechanism, but the thresholds, consolidation policies, and PDB configurations that make that mechanism effective require ongoing manual tuning as workloads change. Zesty’s adaptive pod placement removes that tuning burden by continuously repacking workloads onto the most efficient node configuration in real time, without a platform engineer adjusting consolidation thresholds by hand every time traffic patterns shift.

Optimize Cloud Commitments Instead of a Once-a-Year Guess

Once workloads are rightsized and nodes are efficiently packed, the third tier of EKS cost optimization is how the remaining, now-accurate compute footprint actually gets paid for. Most teams answer that question once a year: estimate the stable baseline, commit to a Savings Plan against that estimate, and reassess it at renewal.

That approach has a built-in expiration date. Savings Plans offer up to 66 percent savings for predictable, steady-state baseline usage, in exchange for a compute spend commitment, and that discount is real. The problem isn’t the discount, it’s the estimate underneath it. A baseline sized correctly in January is rarely still correct by June: teams grow, workloads shift, services get decommissioned, and the commitment purchased against last year’s usage pattern increasingly diverges from what the cluster actually needs. Overcommit and you’re paying for reserved capacity that’s no longer there to use. Undercommit as usage grows and the savings opportunity on the new baseline goes uncaptured until the next renewal cycle rolls around, potentially a full year later.

The fix isn’t a better one-time estimate. It’s not treating the commitment as one-time at all. Instead of locking in a single annual figure, a micro-Savings Plan approach adjusts the committed amount in smaller increments on a rolling basis, closer to daily than annually, so the commitment tracks the cluster’s actual baseline as it changes rather than the baseline the team happened to have on the day they signed.

This matters more than it sounds like it should, because the commitment layer is exactly where rightsizing gains can get undone without anyone noticing. A team that rightsizes its workloads and shrinks its baseline usage, but is still locked into a Savings Plan sized for the old, larger baseline, is paying for savings it isn’t actually receiving. The commitment has to track the rightsizing work, not sit frozen against whatever the baseline looked like a year ago. Zesty’s AWS commitment optimization automates exactly that: adjusting the Savings Plan commitment daily against real, current usage, so the discount always applies to the cluster’s actual baseline rather than a stale annual guess.

Use Cheaper, More Efficient Compute (Graviton and Accelerators)

Graviton processors run up to 20 percent cheaper and up to 60 percent more energy-efficient than comparable x86 instances, making them one of the simplest cost levers available once rightsizing and bin-packing are already handled. The catch is that Graviton runs on ARM architecture, which means container images built only for amd64 won’t run on it without changes.

Adopting Graviton means building multi-arch container images, images that include both amd64 and arm64 variants, so the same deployment can schedule onto either architecture. Karpenter supports running mixed amd64/arm64 clusters directly, letting workloads with multi-arch images schedule onto whichever architecture is available and most cost-effective at that moment, while workloads still tied to a single architecture continue scheduling normally.

For machine learning workloads, GPU and accelerator costs deserve the same scrutiny as CPU and memory. GPUs are frequently the least utilized resource in a cluster, since a workload is often assigned an entire accelerator even when it only needs a fraction of its capacity. GPU sharing approaches like time-slicing and Multi-Instance GPU (MIG) let multiple workloads share a single physical accelerator instead of each reserving one outright, which matters because idle GPU capacity is dramatically more expensive to waste than idle CPU or memory capacity of the same proportion.

Conclusion: Manual Optimization vs. Automated Optimization

The three-tier framework in this guide works. Rightsizing requests and limits, reducing unused node capacity through bin-packing, and continuously adjusting cloud commitments to match the real baseline is a complete, effective approach to EKS cost optimization when executed well. The catch is what “executed well” actually requires, and it’s a longer list than it first appears:

  • Quarterly (at minimum) review of request and limit accuracy across every service and sidecar, repeated indefinitely as new services ship and old ones drift out of their original sizing
  • Ongoing coordination between DevOps and application developers to validate and apply rightsizing changes safely, since neither side has full visibility into the other’s constraints on their own
  • Continuous tuning of Karpenter consolidation thresholds and PDB configurations as workloads and traffic patterns evolve, work that has no natural stopping point
  • Regular reassessment of the Savings Plan commitment as the baseline shifts, since a commitment sized correctly at renewal can be badly out of date within a quarter

That’s a lot of standing operational work for a framework that has to run continuously to stay effective, and it’s exactly the kind of ongoing effort most platform teams don’t have the headcount to sustain quarter after quarter, especially alongside everything else on a platform team’s roadmap. Zesty automates Kubernetes cost optimization by applying all three tiers in real time instead of on a review cycle: rightsizing requests and limits continuously as usage changes, repacking workloads through automated bin-packing and consolidation, and adjusting Savings Plan commitments dynamically, all without a team manually re-tuning thresholds every time traffic shifts. Result: 50 to 80 percent reductions in Kubernetes compute costs, sustained automatically rather than re-earned every quarter through manual review.

The manual framework and the automated one solve the same problem. The difference is whether a person has to keep solving it every quarter, or whether the system keeps solving it continuously on its own. Book a demo with Zesty to see which end of that trade-off fits your team.

FAQs

What is the biggest driver of EKS compute costs?

Oversized resource requests and limits, not node count or instance pricing, are typically the biggest driver. A cluster running at 35 to 45 percent average utilization is paying for compute that’s provisioned but never used, and that gap traces back to requests set too high at deploy time and never adjusted afterward. Node-level bin-packing and commitment sizing matter too, but they’re optimizing on top of whatever baseline the request values already set.

Should I use Karpenter or Cluster Autoscaler for EKS cost optimization?

Why does a Savings Plan bought once a year stop matching actual usage?

Does Fargate save money compared to EC2 for EKS workloads?

How does Zesty automate EKS cost optimization compared to manual tuning?