Skip to main content
rightsizing · aws

EKS node groups running more nodes than their load needs, trimmed one at a time

resource types
1
rule IDs covered
1
severity
medium

What does ZopNight detect here?

A node group averaging 40% CPU or less with desired above its minimum can absorb one drained node's pods across the survivors, so ZopNight recommends shrinking DesiredSize by exactly 1 (priced at that node's on-demand rate × 730 hours) and re-measures before ever claiming a second.

Signal and threshold

How ZopNight evaluates EKS node groups running more nodes than their load needs, trimmed one at a time.
Field Value
Rule IDsRC-042
Categoryrightsizing
Severitymedium
Metricnone — pure configuration read
Sourceeks_underutilized_nodes.go

One node per pass, on purpose

A 10-node group at 10% CPU could arguably lose several nodes at once. The rule still only ever claims one: under-claiming is the safe direction for an action that drains live pods, and successive passes converge on the right size with a fresh measurement between each step. The gate is desired count above the scaling-config floor plus a 14-day CPU average at or under 40%: headroom enough for the remaining nodes to absorb the drained node’s pods without tipping into pressure.

Three vetoes stand between the average and the recommendation

Memory first: a CPU-idle group holding JVM heaps or in-memory caches must not shed a node, so the memory rollup is required and must average at or under 50%. Peaks second: a group that spikes toward saturation needs its headroom at exactly those moments, so a trusted peak near the ceiling suppresses regardless of the average. Capacity type third: Spot groups abstain entirely, because only the on-demand instance rate is visible here and pricing a removed Spot node at on-demand would overstate the saving two to three times.

Evidence requirements, stated

Both rollups come from the Container Insights agent EKS documents; a cluster without the agent produces no series and the rule stays silent. A blind scale-down on an unmeasured group is the failure mode it refuses. Unhealthy or transitioning node groups are skipped, and a missing on-demand rate abstains rather than emitting an unpriced recommendation.

Check a group’s headroom

Terminal window
aws eks describe-nodegroup --cluster-name my-cluster --nodegroup-name my-ng \
--query 'nodegroup.[scalingConfig,capacityType,instanceTypes]'
aws cloudwatch get-metric-statistics \
--namespace ContainerInsights --metric-name node_cpu_utilization \
--dimensions Name=ClusterName,Value=my-cluster Name=NodegroupName,Value=my-ng \
--start-time "$(date -u -v-14d +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --period 86400 --statistics Average

The PDB is your half of the contract

Applying the change sets DesiredSize down by one; AWS cordons and drains the surplus node, honouring PodDisruptionBudgets as it evicts. That guarantee is only as good as the PDBs you have defined, and a workload without one can be evicted abruptly. The pre-flight check is therefore that every workload on the group carries a PDB, and the post-flight check is a day of latency and error metrics on the smaller fleet.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·