Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer
Kubernetes 2025-09-15 18 min read

The Complete Guide to Amazon EKS in 2026: Setup, Costs, and Best Practices

Saddique Khan

DevOps / SRE Engineer & CEO, Cloud DevOps Solutions

Amazon EKS is the managed Kubernetes control plane on AWS. AWS runs the API server, etcd, and controller manager; you manage worker nodes, networking, and workloads. In 2026, EKS runs Kubernetes 1.29-1.31 and charges $0.10/hour per cluster for the control plane, regardless of node count.

Architecture overview

An EKS cluster consists of:

  • Control plane, AWS-managed, multi-AZ, automatically patched
  • Data plane, EC2 node groups, Fargate profiles, or EKS Auto Mode
  • Networking, VPC CNI (one IP per pod), CoreDNS, kube-proxy
  • IAM integration, IRSA maps K8s service accounts to IAM roles without static credentials
┌─────────────────────────────────────────────────┐
│  AWS-managed control plane (across 3 AZs)       │
│  API Server │ etcd │ Scheduler │ Controllers     │
└──────────────────────┬──────────────────────────┘
                       │ kubelet
        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
   Node Group A   Node Group B   Fargate Profile
   (On-Demand)     (Spot)        (serverless pods)

Creating a cluster

With eksctl (fastest)

# Install eksctl
brew tap weaveworks/tap && brew install weaveworks/tap/eksctl

# Create cluster with managed node group
eksctl create cluster \
  --name prod-cluster \
  --region us-east-1 \
  --nodegroup-name workers \
  --node-type m5.xlarge \
  --nodes 3 \
  --nodes-min 2 \
  --nodes-max 10 \
  --managed \
  --with-oidc \
  --ssh-access \
  --ssh-public-key my-key
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "prod-cluster"
  cluster_version = "1.31"

  vpc_id                   = module.vpc.vpc_id
  subnet_ids               = module.vpc.private_subnets
  control_plane_subnet_ids = module.vpc.intra_subnets

  cluster_endpoint_public_access = true

  eks_managed_node_groups = {
    workers = {
      min_size       = 2
      max_size       = 10
      desired_size   = 3
      instance_types = ["m5.xlarge"]
      capacity_type  = "ON_DEMAND"
    }
    spot = {
      min_size       = 0
      max_size       = 20
      desired_size   = 0
      instance_types = ["m5.xlarge", "m5a.xlarge", "m4.xlarge"]
      capacity_type  = "SPOT"
    }
  }
}

Node groups vs Fargate

FeatureManaged Node GroupsFargate
BillingPer EC2 instancePer vCPU/GB per second
AMI patchingSemi-automated (you trigger)Fully automatic
DaemonSetsSupportedNot supported
GPUs / large memoryAvailableLimited
Startup latencyFast (pre-provisioned)Slow (15-90s cold start)
Best forGeneral workloads, stateful appsBatch jobs, burst workloads

Recommendation: Use managed node groups for your base capacity and Fargate for ephemeral jobs (CI runners, data pipelines).

Networking: VPC CNI

EKS uses the AWS VPC CNI plugin, which assigns real VPC IPs to pods. This simplifies security groups and routing but consumes IP space fast.

# Check available IPs per node
kubectl describe node ip-10-0-1-100.ec2.internal | grep Allocatable -A5

Key configuration:

# Increase ENI prefix delegation (more IPs per node)
kubectl set env daemonset aws-node \
  -n kube-system \
  ENABLE_PREFIX_DELEGATION=true \
  WARM_PREFIX_TARGET=1

For large clusters, use prefix delegation to get 16 IPs per ENI slot instead of 1.

IAM for Service Accounts (IRSA)

IRSA lets pods assume IAM roles without static credentials. This is the correct way to grant AWS API access to workloads.

# Enable OIDC provider (do this once per cluster)
eksctl utils associate-iam-oidc-provider \
  --cluster prod-cluster \
  --approve

# Create IAM role for a service account
eksctl create iamserviceaccount \
  --cluster prod-cluster \
  --namespace my-app \
  --name my-app-sa \
  --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
  --approve
# Annotate the service account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: my-app
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role

Critical add-ons

Install these on every production EKS cluster:

# EBS CSI driver (for PersistentVolumes)
eksctl create addon --name aws-ebs-csi-driver --cluster prod-cluster

# AWS Load Balancer Controller
helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=prod-cluster \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller

# Cluster Autoscaler
helm install cluster-autoscaler autoscaler/cluster-autoscaler \
  -n kube-system \
  --set autoDiscovery.clusterName=prod-cluster \
  --set awsRegion=us-east-1

Cost breakdown (us-east-1, 2026)

ComponentCost
Control plane$0.10/hr = ~$73/month
m5.xlarge On-Demand$0.192/hr each
m5.xlarge Spot~$0.06/hr each (saves ~70%)
EBS gp3 100GB~$8/month
NAT Gateway$0.045/hr + $0.045/GB processed
Data transfer outFirst 100GB free, then $0.09/GB

A typical 3-node cluster (m5.xlarge On-Demand) costs ~$140-200/month plus data transfer.

Security hardening checklist

# 1. Enable audit logging
aws eks update-cluster-config \
  --name prod-cluster \
  --logging '{"clusterLogging":[{"types":["audit","api","authenticator"],"enabled":true}]}'

# 2. Restrict public API access to your IPs
aws eks update-cluster-config \
  --name prod-cluster \
  --resources-vpc-config endpointPublicAccess=true,publicAccessCidrs="203.0.113.0/32"

# 3. Enable secrets encryption
aws eks create-cluster ... \
  --encryption-config resources=secrets,provider.keyArn=arn:aws:kms:...
# 4. Network policies, deny all by default, allow explicitly
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

Upgrade strategy

EKS supports N-2 Kubernetes versions. AWS drops support for older versions with ~14 months notice.

# 1. Update control plane (zero-downtime)
aws eks update-cluster-version \
  --name prod-cluster \
  --kubernetes-version 1.31

# 2. Update managed node group (rolling)
aws eks update-nodegroup-version \
  --cluster-name prod-cluster \
  --nodegroup-name workers

# 3. Update add-ons
eksctl update addon --name aws-ebs-csi-driver --cluster prod-cluster

Always test in a staging cluster first. The biggest breakages come from deprecated API versions, run kubent before upgrading.

# Check for deprecated APIs
kubectl kube-no-trouble  # or: kubent

Key takeaways

  • EKS charges $0.10/hr for the control plane; your real cost is worker nodes and data transfer.
  • Use Terraform with the terraform-aws-modules/eks module for reproducible clusters.
  • Enable IRSA immediately, never use static IAM credentials in pods.
  • Prefix delegation lets you run more pods per node without adding nodes.
  • Install the EBS CSI driver, ALB controller, and Cluster Autoscaler on every production cluster.
  • Test upgrades in staging with kubent to catch deprecated API usage before it breaks production.
#eks #kubernetes #aws #production
Found this useful? Share it with your team or link to it from your documentation.

Need help implementing this?

Our certified engineers can handle the setup, migration, or ongoing operations for you.