Skip to main content
Back to All Whitepapers/FinOps & Cost Optimization
FinOps & Cost OptimizationAugust 28, 20266 min read

AWS EKS Cost Optimization: 15 Practical Ways to Reduce Kubernetes Spend

A field-tested playbook for cutting AWS EKS bills by 40% to 70%: Karpenter spot bin-packing, Graviton3 migrations, VPC endpoint data transfer pruning, and right-sizing memory requests.

SA

Sikander Ali

Principal DevOps Architect & CTO

Executive Engineering Summary & Takeaways

  • Migrating from legacy Cluster Autoscaler to Karpenter with EC2 Spot diversification cuts compute costs by 45% to 65% with sub-45-second node provisioning.
  • Transitioning x86 workloads to AWS Graviton3/Graviton4 (ARM64) delivers an immediate 20% to 40% price-performance gain.
  • Deploying Gateway VPC Endpoints for S3 and DynamoDB eliminates NAT Gateway data processing surcharges ($0.045/GB) entirely.
  • Enforcing strict Horizontal Pod Autoscaler (HPA) targets and Vertical Pod Autoscaler (VPA) profiling prevents over-provisioned CPU and memory buffer waste.
  • Consolidating Application Load Balancers via AWS Load Balancer Controller IngressGroups eliminates dozens of redundant $16/month ALB fees.
  • Automated nightly pod hibernation for staging and non-production clusters reduces dev cluster compute hours by over 60%.

1. Replace Cluster Autoscaler with Karpenter v1.x Dynamic Spot Bin-Packing

Legacy Kubernetes Cluster Autoscaler operates by reacting to pending pods and scaling pre-configured EC2 Auto Scaling Groups (ASGs). This creates instance fragmentation, slow spin-up latencies (3 to 6 minutes), and inflexible sizing boundaries.

Karpenter bypasses ASG abstractions entirely. It evaluates pending pod specifications directly against the AWS EC2 Fleet API, choosing the optimal mix of instance families, architectures, and sizes in sub-45 seconds.

By leveraging a diversified Spot instance pool across multiple availability zones with automated fallback to On-Demand, Karpenter prevents capacity exhaustion while maximizing node density.

karpenter-nodepool.yaml
YAML
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: general-compute-spot
spec:
  template:
    spec:
      requirements:
        - key: kubernetes.io/arch
          operator: In
          values: ["arm64", "amd64"]
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
        - key: karpenter.k8s.aws/instance-generation
          operator: In
          values: ["6", "7", "8"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default-nodeclass
      expireAfter: 720h
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 1m
    budgets:
      - nodes: "15%"
Pro Tip

FinOps Rule: Diversifying across at least 8 to 12 instance types (e.g., c6g, c7g, m6g, m7g, r6g) virtually guarantees 99.9% Spot fulfillment without capacity-unavailable errors.

2. Migrate Containerized Microservices to AWS Graviton (ARM64)

AWS Graviton3 and Graviton4 custom silicon deliver an immediate 20% to 40% price-performance advantage over equivalent Intel and AMD x86 instances.

For runtime environments such as Node.js, Python, Go, Java 17+, and Rust, moving to ARM64 requires zero application code changes—only multi-architecture Docker image compilation via buildx.

Updating your Kubernetes Deployment nodeAffinity to prioritize ARM64 allows seamless scheduling onto Graviton instances as Karpenter provisions them.

graviton-affinity.yaml
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service
  namespace: production
spec:
  replicas: 10
  template:
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: kubernetes.io/arch
                    operator: In
                    values: ["arm64"]

3. Eliminate CPU and Memory Buffer Waste via VPA Profiling

Over 60% of idle cloud compute waste originates from developers setting arbitrary, defensive pod resource requests (e.g. requesting 4GiB memory for a service averaging 180MiB). Because the Kubernetes scheduler reserves capacity based strictly on requests, nodes fill up and trigger unnecessary scale-outs.

Running the Vertical Pod Autoscaler (VPA) in recommendation mode or deploying Goldilocks generates precise P99 memory and CPU targets based on historical Prometheus metrics.

By aligning container requests to real-world P95 utilization plus a 20% headroom buffer, node utilization density increases by 2.5x.

4. Bypass NAT Gateways for S3 & DynamoDB with Free Gateway VPC Endpoints

AWS charges $0.045/hour per NAT Gateway plus $0.045 per GB for all data processed. When container workloads pull training datasets, download media, or query DynamoDB, routing that internal traffic through NAT Gateways creates massive hidden surcharges.

Deploying Gateway VPC Endpoints for S3 and DynamoDB is 100% free of charge and routes traffic directly across the AWS private network backbone, bypassing NAT Gateways completely.

s3-gateway-endpoint.tf
Terraform
resource "aws_vpc_endpoint" "s3" {
  vpc_id            = module.vpc.vpc_id
  service_name      = "com.amazonaws.us-east-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = module.vpc.private_route_table_ids

  tags = {
    Name = "technofreaks-s3-gateway-endpoint"
  }
}

5. Upgrade Legacy EBS gp2 Storage Volumes to gp3 In-Place

Legacy General Purpose SSD (gp2) volumes couple baseline IOPS to provisioned disk size (3 IOPS/GB), forcing teams to provision huge 1TB disks just to get 3,000 IOPS.

Upgrading to gp3 provides 3,000 IOPS and 125 MB/s baseline throughput for free on any volume size, while cutting baseline per-GB storage cost by 20% ($0.08/GB vs $0.10/GB). This modification is online and 100% non-disruptive.

6. Automate Object Storage Transitions with S3 Intelligent-Tiering

Unstructured application data, user uploads, and build artifacts that remain in S3 Standard past 30 days accumulate unnecessary cost. S3 Intelligent-Tiering monitors access patterns and shifts dormant data to Infrequent and Archive Instant Access tiers with zero retrieval penalties.

7. Prune Inter-AZ Data Egress with Topology Spread Constraints

Cross-AZ traffic inside an AWS region is billed at $0.01/GB in and $0.01/GB out ($0.02/GB round-trip). High-throughput microservices communicating randomly across AZ boundaries generate significant inter-AZ network charges.

Using Kubernetes Topology Spread Constraints combined with Service Internal Traffic Policy (trafficDistribution: PreferClose) keeps client-to-service communication within the same AZ whenever possible.

zone-aware-service.yaml
YAML
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  trafficDistribution: PreferClose
  selector:
    app: order-service
  ports:
    - port: 80
      targetPort: 8080

8. Fine-Tune Horizontal Pod Autoscaling (HPA) with KEDA Custom Metrics

Default CPU/Memory-based HPAs scale reactively after a node is already saturated, causing laggy scale-outs and over-conservative minimum replica counts.

Using KEDA (Kubernetes Event-driven Autoscaling) to scale on active SQS queue depth, Kafka consumer lag, or Prometheus HTTP request rate allows pods to scale down to low baseline minimums during quiet hours and scale up immediately ahead of traffic bursts.

9. Schedule Nightly Pod Hibernation for Staging and Ephemeral Clusters

Development and QA environments typically sit completely idle for 12 to 14 hours every night and all weekend (representing ~65% of total hours per week).

Deploying an automated CronJob scales deployment replicas to 0 at 19:00 UTC and scales them back up at 07:00 UTC Monday through Friday, slashing non-production compute bills by over 60%.

cluster-sleep-cronjob.yaml
YAML
apiVersion: batch/v1
kind: CronJob
metadata:
  name: dev-environment-hibernate
  namespace: kube-system
spec:
  schedule: "0 19 * * 1-5"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: cluster-scaler-sa
          containers:
            - name: scaler
              image: bitnami/kubectl:latest
              command:
                - /bin/sh
                - -c
                - "kubectl scale deployment --all -n staging --replicas=0"
          restartPolicy: OnFailure

10. Prune Redundant DaemonSets and Deploy NodeLocal DNSCache

Every DaemonSet (log forwarders, APM agents, security monitors) consumes fixed CPU and memory on every single node. In clusters with 50+ nodes, heavy DaemonSets can waste 20% to 30% of aggregate cluster capacity.

Auditing DaemonSet resource requests and implementing NodeLocal DNSCache reduces CoreDNS queries and eliminates DNS resolution latency across high-density node fleets.

11. Prune Verbose EKS Control Plane Logging & CloudWatch Ingestion

Enabling all EKS Control Plane logs (API, Audit, Authenticator, ControllerManager, Scheduler) generates high CloudWatch Log ingestion charges ($0.50/GB).

Keep only api and authenticator logs enabled in production, and set strict 14-day CloudWatch log retention policies with automated S3 export for long-term audit storage.

12. Consolidate Application Load Balancers (ALBs) via IngressGroups

Creating an individual AWS ALB for every microservice generates $16.20/month per ALB plus LCU (Load Balancer Capacity Unit) charges. A cluster with 25 services incurs over $400/month just in idle load balancer fees.

Using the AWS Load Balancer Controller alb.ingress.kubernetes.io/group.name annotation consolidates dozens of Ingress resources onto a single shared, highly available Application Load Balancer.

ingress-group-shared-alb.yaml
YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: customer-api-ingress
  namespace: production
  annotations:
    alb.ingress.kubernetes.io/group.name: "technofreaks-shared-alb"
    alb.ingress.kubernetes.io/scheme: "internet-facing"
    alb.ingress.kubernetes.io/target-type: "ip"
spec:
  ingressClassName: alb
  rules:
    - host: api.technofreaks.online
      http:
        paths:
          - path: /customer
            pathType: Prefix
            backend:
              service:
                name: customer-service
                port:
                  number: 80

13. Blend 1-Year & 3-Year Compute Savings Plans with Spot Pools

Once steady-state cluster utilization is right-sized, calculate your minimum unyielding baseline compute demand over a 30-day window.

Cover 40% to 50% of aggregate baseline compute with a 3-Year Compute Savings Plan (securing up to 66% discount), and allow Karpenter to serve the dynamic remaining 50% to 60% using diverse Spot capacity.

14. Configure PodDisruptionBudgets (PDB) & Graceful Node Drain Buffers

Cost optimization must never compromise availability. Running workloads on Spot instances requires strict PodDisruptionBudgets (PDBs) ensuring a minimum percentage of healthy replicas remain online at all times during Spot node reclamation.

pdb-production.yaml
YAML
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: checkout-pdb
  namespace: production
spec:
  minAvailable: 75%
  selector:
    matchLabels:
      app: checkout-service

15. Deploy Kubecost / OpenCost for Continuous In-Cluster Cost Allocation

Traditional AWS Cost Explorer cannot see inside a Kubernetes node to reveal which namespace, deployment, or team is consuming compute resources.

Deploying Kubecost or CNCF OpenCost correlates AWS billing APIs with real-time cgroup CPU/memory usage, providing engineering leaders with exact per-namespace and per-pod financial telemetry.

Frequently Asked Questions

How much can Karpenter reduce AWS EKS compute costs?▼

In production environments, migrating from legacy Kubernetes Cluster Autoscaler to Karpenter with EC2 Spot diversification typically cuts compute bills by 45% to 65%. Karpenter provisions right-sized nodes in under 45 seconds and continuously consolidates underutilized nodes.

What is the cost benefit of switching EKS worker nodes to AWS Graviton?▼

AWS Graviton3 and Graviton4 ARM-based EC2 instances offer up to 40% better price-performance compared to comparable x86 instances. Most modern containerized workloads (Node.js, Python, Go, Java) run on ARM64 with simple multi-arch Docker image builds.

How do Gateway VPC Endpoints eliminate AWS NAT Gateway charges?▼

AWS charges $0.045 per GB for data processed through NAT Gateways. Gateway VPC Endpoints for Amazon S3 and DynamoDB are completely free and route traffic over the private AWS internal network, eliminating NAT processing fees for backup and data pipeline traffic.

What tools provide pod-level cost allocation in Kubernetes?▼

CNCF OpenCost and Kubecost provide real-time per-pod, per-namespace, and per-label cloud billing allocation by binding Linux cgroup metrics directly to AWS pricing APIs.

SA
Sikander AliFounder & CEO • CKA

Founder & CEO | Principal Cloud Architect, TechnoFreaks

Commercial FinOps PracticeTypical ROI achieved within 30 days • 35%–75% Spend Cut

Overpaying for AWS EKS Compute, NAT Gateways, or Idle Nodes?

Our senior cloud architects perform a comprehensive FinOps audit of your AWS estate to eliminate unneeded provisioned IOPS, configure automated Karpenter Spot consolidation, and enforce right-sized pod limits. We routinely reduce client AWS invoices by 35% to 75% without compromising production reliability.

Implement This in Production

Ready to Upgrade Your Cloud Infrastructure?

Book a 30-minute technical architecture review with our senior DevOps leads to assess your migration roadmap and infrastructure optimization.