EKS Cost Optimization

An EKS bill is rarely about EKS. The control plane is a small fixed fee; nearly everything else is the EC2, storage and network that workloads consume. Reducing it is mostly a matter of closing three gaps:

what you pay for  ≥  what nodes provide  ≥  what pods request  ≥  what pods actually use
        └── purchase options ──┘└──── bin-packing ────┘└──── right-sizing ────┘

Work from right to left. Buying discounted capacity for pods that request three times what they use locks in the waste.

Where the money goes

ItemTypical shareDriven by
EC2 computeLargestPod requests, bin-packing, purchase option
Data transferOften surprisingCross-zone traffic, NAT gateway processing, egress
EBS and other storageModerateOver-provisioned and orphaned volumes, snapshots
Load balancersModerateOne per Service or Ingress, unless shared
ObservabilityCan be largeLog volume and metric cardinality
Control planeSmall, fixedNumber of clusters; much higher in extended support

1. See it

You cannot optimise what you cannot attribute.

  • Split cost allocation data for EKS adds pod-level cost to the Cost and Usage Report, by cluster, namespace and workload — query it with Athena or view it in Cost Explorer. See cost and usage report and Cost Explorer.
  • Kubecost or OpenCost show cost per namespace, label and deployment in near real time, with idle and efficiency figures.
  • Labels and tags. Require team and cost-centre labels on workloads with admission policy, and propagate tags to nodes, volumes and load balancers — cost allocation tags.

Report cost per team and per unit of work (per request, per tenant). Showing teams their own number changes behaviour more than any central optimisation.

2. Right-size requests

Requests decide how much capacity is reserved. Usage far below requests is paid-for idle.

  • Compare requested with actual over two weeks:

    sum by (namespace) (kube_pod_container_resource_requests{resource="cpu"})
      /
    sum by (namespace) (rate(container_cpu_usage_seconds_total[5m]))
  • Run VPA in recommendation mode, or use Kubecost’s suggestions, and apply them through normal deployments.

  • Set CPU requests near typical usage and memory requests near peak. Memory is not compressible; CPU is.

  • Scale replicas with HPA or KEDA instead of provisioning for peak all day, and scale idle workloads to zero — autoscaling.

  • Turn off non-production environments outside working hours. That alone removes about two thirds of their compute hours.

Details in requests and limits.

3. Pack nodes tightly

  • Consolidation. Karpenter and Auto Mode remove underused nodes and replace them with cheaper ones. This is usually the largest single saving after right-sizing.
  • Instance flexibility. Allow many families and sizes so the autoscaler can pick the cheapest shape for the pending pods.
  • Avoid fragmentation. Many small node groups with narrow selectors, strict anti-affinity and zone pinning all strand capacity.
  • Watch DaemonSet overhead. Agents on every node take a larger share of small nodes; fewer, larger nodes dilute it.
  • Raise pod density where IP limits, not CPU, cap a node — prefix delegation.

Protect workloads first with PodDisruptionBudgets: tight packing means pods move more often.

4. Pay less per unit

LeverTypical savingSuits
GravitonAbout 20% and moreAnything with multi-architecture images
SpotUp to 90%Stateless, replicated, interruption-tolerant workloads
Compute Savings PlansUp to about 66%The steady baseline that is always running
Current-generation instancesBetter price-performanceEverything

A common shape: Savings Plans cover the On-Demand baseline, Spot carries everything above it, and Graviton applies to both. Buy commitments after right-sizing and consolidation, and size them to the baseline, not the peak. See EC2 cost optimization and pricing models.

5. Network

Data transfer is the cost most often overlooked.

CostReduce it by
Cross-zone traffic between podsTopology-aware routing; keeping chatty services in the same zone where resilience allows
NAT gateway data processingVPC endpoints for S3, ECR, CloudWatch and STS; an S3 gateway endpoint is free
Image pulls through NATECR endpoints; pull-through cache for public images
Many load balancersShare one ALB across Ingresses with IngressGroup, or use one Gateway
Cross-zone load balancer trafficIP targets and zone-aware settings

More in network cost optimization.

6. Storage and observability

  • Move gp2 volumes to gp3: cheaper per GB with better baseline performance — EBS CSI.
  • Find orphaned volumes from deleted claims with a Retain policy, and old snapshots — EBS cost optimization.
  • Set retention on every log group, drop noisy logs at the agent, and control metric cardinality — pod logging and Prometheus.

7. Clusters themselves

  • Stay out of extended support. The control plane price is several times higher — cluster upgrades.
  • Consolidate small clusters. Each has a control plane fee, its own system pods and its own half-empty nodes. Namespaces with multi-tenancy controls are often enough.
  • Delete idle clusters, including their NAT gateways and load balancers.

An ordered plan

  1. Turn on cost allocation; give each team its number.
  2. Right-size requests for the top ten workloads by cost.
  3. Enable consolidation and widen instance choice.
  4. Move to Graviton where images allow.
  5. Put tolerant workloads on Spot.
  6. Add VPC endpoints; share load balancers.
  7. Buy Savings Plans for what is left as steady baseline.
  8. Review monthly; make cost a standing item beside reliability.

Quick wins checklist

  • Every container has resource requests
  • Non-production scales down out of hours
  • Consolidation enabled, with PodDisruptionBudgets in place
  • gp3 is the default StorageClass
  • Log groups have retention set
  • S3 gateway and ECR endpoints exist
  • No cluster in extended support
  • Orphaned volumes, snapshots and load balancers cleaned up

Across the wiki