EKS Node Groups Oversized: The $15,000 Monthly Bill Nobody Could Explain
"Why are we still paying so much for EKS compute, even when our apps are barely busy?"
The Persistent Cloud Bill Headache
That was the question echoing through our Slack channels and budget meetings every month. As a platform engineer, I've seen my share of unexpected cloud costs, but our Amazon EKS bill felt like a recurring nightmare. Our containerized microservices were supposed to be agile and efficient, yet the compute costs for our primary EKS cluster kept climbing, completely out of sync with our actual application usage.
We were running our production workloads on m5.2xlarge
instances – beefy machines with 8 vCPUs and 32GB of RAM. On paper, it seemed like a safe bet for a growing architecture. The reality, however, was stark: our monitoring showed consistent CPU and memory utilization well below 25% across most of these nodes. It was like buying an 18-wheeler to deliver a single pizza.
The problem wasn't a sudden spike; it was a slow, insidious bleed of money. A significant chunk of our AWS bill – easily $15,000 a month – was going towards compute capacity that simply wasn't being used. This wasn't just bad for the budget; it was a constant source of friction between the finance team, our developers who needed resources, and the platform team trying to keep things stable.
Chasing Shadows: Our Initial Attempts at Optimization
Our first instinct was to manually intervene. We'd try to scale down node groups during off-peak hours, only to be met with scheduling nightmares and occasional outages when an unexpected burst of traffic or a batch job would hit. It quickly became clear that manual scaling was not sustainable or reliable for our dynamic environment.
Next, we looked at Kubernetes resource requests and limits. We spent weeks trying to fine-tune these for hundreds of microservices. While essential, this was a massive, ongoing effort. Developers found it challenging to predict exact resource needs, often erring on the side of caution (over-requesting) to avoid OOMKills, or setting limits too low, leading to performance issues.
We also invested heavily in Cluster Autoscaler (CAS) and even experimented with Karpenter, thinking "automatic scaling" was the silver bullet. But even with these tools enabled, we continued to see those large m5.2xlarge
instances being provisioned, often with substantial unused capacity. It felt like we were throwing technology at the problem without truly understanding why it wasn't working as advertised. The blame started to shift: "Devs are requesting too much!", "Ops isn't scaling fast enough!".


The Bin-Packing Conundrum: Discovering the Root Cause
The 'Aha!' moment arrived when we stopped focusing solely on aggregate cluster utilization and started examining individual node group behavior and the instance types being chosen. We realized the problem wasn't just about scaling up or down, but what we were scaling with.
Our primary node groups were configured to use only m5.2xlarge
instances. While a few critical, high-resource pods genuinely needed the muscle, the vast majority of our microservices were lightweight, requiring perhaps 0.5-1 vCPU and 1-2GB of RAM. The Kubernetes scheduler, in its attempt to efficiently "bin pack" pods, would see that the smallest available node type was an m5.2xlarge
. Even if it only needed to schedule a few small pods, it would spin up or keep alive these large instances.
This led to a persistent mismatch: our cluster often had nodes running at <25% utilization because the pods running on them were tiny relative to the node's capacity. We were paying for 8 vCPUs and 32GB of RAM, but only actively using 1-2 vCPUs and 4-8GB. The rest was dead weight. The Cluster Autoscaler was doing its job, but with a limited palette of very large instance types, it couldn't be truly efficient.
EazyOps to the Rescue: Intelligent Right-Sizing and Auto-Scaling
This is where EazyOps stepped in. We needed a solution that could not only identify this exact mismatch but also provide prescriptive guidance and actionable steps. EazyOps' analysis went beyond surface-level metrics, digging deep into actual pod resource consumption patterns across our EKS cluster.
EazyOps identified that a significant portion of our workloads could comfortably run on much smaller instance types. Their recommendation was clear: right-size our general-purpose node groups from m5.2xlarge
(8 vCPU, 32GB) to m5.large
(2 vCPU, 8GB). This wasn't about simply scaling down, but about optimizing the shape of our compute.
- Granular Insights: EazyOps provided detailed reports showing which specific pods and deployments were contributing to the over-provisioning and which instance types would be a better fit.
- Prescriptive Recommendations: It wasn't just data; EazyOps offered a clear plan for adjusting our EKS node group configurations.
- Optimized Auto-Scaling: We reconfigured our Cluster Autoscaler (and later Karpenter) to include
m5.large
as the primary instance type, ensuring that smaller workloads would land on appropriately sized nodes. We maintained a separate, smaller node group withm5.2xlarge
for truly demanding services that genuinely required those resources, preventing the large nodes from becoming the default for everything.
By leveraging EazyOps, we moved from reactive firefighting to a proactive, data-driven optimization strategy. It helped us understand that autoscaling is only as effective as the instance types it has to choose from.


Transformative Impact: 50% Cost Reduction, Enhanced Efficiency
The results were not just impressive; they were transformative. Within a few weeks of implementing EazyOps' recommendations and adjusting our node group strategies, we saw an immediate and sustained impact:
- 50% Reduction in Compute Spend: Our monthly EKS compute bill dropped by a staggering 50%, saving us approximately $15,000 every single month. This freed up significant budget for other critical initiatives.
- Improved Resource Utilization: Node CPU and memory utilization across the cluster increased from an abysmal <25% to a healthy 60-70%, indicating truly efficient resource allocation.
- Faster Scaling & Stability: With a more diverse pool of instance types, the Cluster Autoscaler became far more responsive and intelligent, spinning up
m5.large
instances precisely when needed, leading to faster pod scheduling and a more stable cluster environment. - Enhanced Developer Experience: Developers no longer faced "pending" pods for extended periods due to lack of resources, and the platform felt more responsive overall.
This wasn't just about saving money; it was about building a more resilient, cost-aware, and efficient cloud infrastructure. We finally had transparency into our EKS costs and the confidence to scale without fear of uncontrolled expenditure.
Key Takeaways from Our EKS Optimization Journey
- Don't Trust Default Instance Choices: Simply enabling auto-scaling isn't enough. The chosen instance types for your node groups critically impact efficiency.
- Monitor Actual Usage, Not Just Requests: Focus on what your applications are actually consuming, not just what they're requesting. This is where hidden waste lies.
- Bin Packing is King: Provide your Kubernetes scheduler with a diverse pool of instance types to optimize how pods are packed onto nodes. Smaller, varied instance types lead to better utilization.
- Intelligent Tools are Essential: Manually identifying and acting on these inefficiencies is nearly impossible at scale. Solutions like EazyOps provide the deep insights and prescriptive guidance needed to make a real difference.
- Cost Optimization is an Ongoing Process: Workloads evolve. Continuous monitoring and adaptation are crucial to maintain efficiency.
The Future of EKS Cost Efficiency with EazyOps
Our journey with EKS node group optimization taught us invaluable lessons about the importance of granular visibility and intelligent automation. As cloud environments grow in complexity, the need for sophisticated tools to manage costs will only increase.
EazyOps continues to evolve, offering even more advanced capabilities like predictive cost modeling, anomaly detection, and automated right-sizing recommendations across various cloud resources. For organizations wrestling with rising cloud bills and underutilized resources, particularly within dynamic EKS clusters, the ability to effortlessly identify and rectify these inefficiencies is paramount.
The goal isn't just to cut costs, but to optimize spend in a way that fuels innovation, ensures reliability, and allows engineering teams to focus on building, not bill shock. With EazyOps, we're building a future where cloud costs are predictable, transparent, and always optimized.
About Shujat
Shujat is a Senior Backend Engineer at EazyOps, working at the intersection of performance engineering, cloud cost optimization, and AI infrastructure. He writes to share practical strategies for building efficient, intelligent systems.