Skip to main content
idle · aws

EKS node groups holding live nodes at essentially zero utilization

resource types
1
rule IDs covered
1
severity
high

What does ZopNight detect here?

Worker nodes bill full instance-hours whatever their pods do, so a node group whose CPU averages and peaks under 3% for 30 straight days is capacity bought for nothing. ZopNight also vetoes on memory, since a CPU-quiet group holding large caches must not be torn down, and prices the entire run-rate as recoverable.

Signal and threshold

How ZopNight evaluates EKS node groups holding live nodes at essentially zero utilization.
Field Value
Rule IDsRC-043b
Categoryidle
Severityhigh
Metricnone — pure configuration read
Sourceeks_running_idle.go

The 3% bar, and why it is not zero

Real nodes are never at literal zero: kubelet, system daemons, and the container runtime produce a permanent murmur of CPU. The idle floor sits at 3%, comfortably above that baseline and far below any workload, and both the 30-day average and the peak must stay under it. A group that bursts weekly above the floor is being used, however idle it looks between bursts, and abstains.

Memory can veto what CPU approves

A node group can be CPU-silent while holding memory that matters: in-memory caches, JVM heaps, stateful sets that do their work in RAM. Tearing that down evicts state the surviving capacity cannot absorb. So the memory rollup is required alongside CPU with the same 30-day coverage, and a memory-loaded group abstains even at 0% CPU. Both series come from the CloudWatch Observability EKS add-on; clusters without the agent produce no rollups, and the rule stays silent on them rather than tearing down capacity it cannot see.

How the dollar figure is built

The node group object itself is free (the money is in its EC2 workers), so the run-rate is priced directly: the node’s live on-demand hourly rate × 730 hours × the desired node count, fully recoverable on teardown. Two pricing abstentions follow. No resolvable rate means no recommendation. And Spot groups abstain entirely: only the on-demand rate is visible to the rule, and pricing a removed Spot node at on-demand would overstate the saving roughly 2–3×.

What this rule leaves to its siblings

Groups already scaled to zero, and degraded groups, belong to the abandoned-workloads rule; unhealthy or transitioning groups are skipped so a teardown is never recommended mid-repair. Only healthy, active groups with at least one desired node land here.

See the rollup yourself

Terminal window
aws cloudwatch get-metric-statistics \
--namespace ContainerInsights --metric-name node_cpu_utilization \
--dimensions Name=ClusterName,Value=my-cluster Name=NodegroupName,Value=my-nodegroup \
--start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 86400 --statistics Average Maximum

Before acting, check for seasonal or intermittent jobs that schedule onto this group, drain with PodDisruptionBudgets in place, and sweep for load balancers and EBS volumes the group leaves behind. Those keep billing after the nodes are gone.

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·