Cloud  /  AWS

AWS Amazon Web Services 61 guides · updated 2026

Hands-on guides to compute, storage, databases, networking, and serverless on the world's most widely adopted cloud platform.

Amazon EKS: Managed Kubernetes on AWS Without the Control Plane Work

Kubernetes has become the standard for running containerised workloads at scale. The problem is that operating a production Kubernetes cluster — maintaining etcd, the API server, the scheduler, and controller manager across three control plane nodes — is a full-time job. Amazon EKS removes that burden by managing the control plane for you.

With EKS, AWS runs the Kubernetes control plane components in a multi-AZ configuration, handles control plane upgrades, patches the OS, and backs up etcd automatically. You focus on deploying workloads.

EKS Architecture

┌──────────────────────────────────────────────────────────────────┐
│ EKS Cluster Architecture │
│ │
│ AWS-Managed Control Plane (invisible to you) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Kubernetes API Server │ etcd │ Scheduler │ Controllers │ │
│ │ HA across 3 AZs — AWS manages this entirely │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ Your VPC │ kubectl / AWS SDK │
│ ┌────────────────────────┴──────────────────────────────┐ │
│ │ Worker Nodes (EC2 Managed Node Group) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │
│ │ │ Node (AZ1) │ │ Node (AZ2) │ │ Node(AZ3) │ │ │
│ │ │ Pod Pod │ │ Pod Pod │ │ Pod Pod │ │ │
│ │ └──────────────┘ └──────────────┘ └────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

Creating an EKS Cluster

The easiest way to create an EKS cluster is with eksctl, the official CLI:

Terminal window
# Install eksctl
brew install eksctl # macOS
# or download from github.com/eksctl-io/eksctl
# Create cluster with a managed node group
eksctl create cluster \
--name production \
--region us-east-1 \
--version 1.29 \
--nodegroup-name general-workers \
--instance-types m6i.large \
--nodes 3 \
--nodes-min 2 \
--nodes-max 10 \
--managed

This creates the control plane, a VPC, subnets, security groups, and a managed node group. It takes about 15 minutes.

Alternatively, with the AWS CLI:

Terminal window
aws eks create-cluster \
--name production \
--kubernetes-version 1.29 \
--role-arn arn:aws:iam::123456789012:role/EKSClusterRole \
--resources-vpc-config \
subnetIds=subnet-0a1b2c,subnet-0d4e5f,subnet-0g6h7i,\
securityGroupIds=sg-eks-control-plane,\
endpointPublicAccess=true,\
endpointPrivateAccess=true

After the cluster is ready, configure kubectl:

Terminal window
aws eks update-kubeconfig \
--region us-east-1 \
--name production

Managed Node Groups

Managed node groups are EC2 Auto Scaling Groups where AWS handles node provisioning, OS updates, and Kubernetes version upgrades. The nodes use an EKS-optimised Amazon Linux 2 or Bottlerocket AMI.

Terminal window
aws eks create-nodegroup \
--cluster-name production \
--nodegroup-name api-workers \
--scaling-config minSize=2,maxSize=10,desiredSize=3 \
--instance-types m6i.large m6i.xlarge \
--subnets subnet-0a1b2c subnet-0d4e5f subnet-0g6h7i \
--ami-type AL2_x86_64 \
--node-role arn:aws:iam::123456789012:role/EKSNodeRole \
--labels tier=api \
--taints key=dedicated,value=api,effect=NO_SCHEDULE

The --taints flag marks nodes so only pods that tolerate the taint are scheduled there. Combined with node selectors, this lets you dedicate specific nodes to specific workloads.

EKS with Fargate

For fully serverless Kubernetes, EKS Fargate profiles let pods run without managing EC2 nodes. You define namespace and label selectors; matching pods run on Fargate.

Terminal window
aws eks create-fargate-profile \
--cluster-name production \
--fargate-profile-name api-namespace \
--pod-execution-role-arn arn:aws:iam::123456789012:role/EKSFargatePodRole \
--subnets subnet-private-0a1b2c subnet-private-0d4e5f \
--selectors '[
{"namespace": "api", "labels": {"workload": "serverless"}},
{"namespace": "default"}
]'

Pods in the api namespace with label workload: serverless, and all pods in the default namespace, run on Fargate.

Fargate pods each run in their own isolated compute environment. There are no shared nodes to worry about. The tradeoff: DaemonSets do not run on Fargate (no access to the node), persistent volumes need EFS rather than EBS, and pod startup is slower.

VPC CNI and Networking

EKS uses the AWS VPC CNI plugin by default. Each pod gets a real VPC IP address from one of the node’s ENI secondary IP addresses.

Node 10.0.1.50 (m6i.large, can have ~27 secondary IPs):
ENI 1 (primary):
Node IP: 10.0.1.50
Pod IPs: 10.0.1.60, 10.0.1.61, 10.0.1.62 ...
ENI 2 (secondary):
Pod IPs: 10.0.1.80, 10.0.1.81, 10.0.1.82 ...

Because pods have VPC IPs, they can communicate directly with other AWS resources (RDS, Lambda, other EC2) without NAT. Security groups on pods allow fine-grained network access control at the pod level (Security Groups for Pods feature).

For clusters with many pods, the default VPC CIDR may not have enough IP space. Consider enabling custom networking or IPv6 for large clusters.

Managed Add-Ons

EKS manages several critical add-ons with version lifecycle tied to Kubernetes versions:

Terminal window
# List available add-ons
aws eks describe-addon-versions --kubernetes-version 1.29
# Install CoreDNS
aws eks create-addon \
--cluster-name production \
--addon-name coredns \
--addon-version v1.11.1-eksbuild.4
# Install EBS CSI driver (required for PersistentVolumes backed by EBS)
aws eks create-addon \
--cluster-name production \
--addon-name aws-ebs-csi-driver \
--service-account-role-arn arn:aws:iam::123456789012:role/EBSCSIRole

Common managed add-ons:

IAM for Service Accounts (IRSA)

In Kubernetes, pods need AWS permissions to call services like S3 or DynamoDB. IRSA lets you bind an IAM role to a Kubernetes service account using OIDC federation — no need for instance profiles or long-lived credentials on nodes.

Terminal window
# Enable OIDC provider for the cluster
eksctl utils associate-iam-oidc-provider \
--cluster production \
--approve
# Create IAM role for the service account
eksctl create iamserviceaccount \
--cluster production \
--namespace api \
--name api-service-account \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--approve

In your deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: api
spec:
template:
spec:
serviceAccountName: api-service-account
containers:
- name: api
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/api:v1

The pod’s service account token is automatically projected and the SDK uses it to assume the IAM role. No credentials to manage.

Deploying an Application

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-api
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web-api
template:
metadata:
labels:
app: web-api
spec:
containers:
- name: api
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/web-api:v2.1
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web-api-svc
namespace: production
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
spec:
type: LoadBalancer
selector:
app: web-api
ports:
- port: 80
targetPort: 8080
Terminal window
kubectl apply -f deployment.yaml
kubectl rollout status deployment/web-api -n production

The AWS Load Balancer Controller (installed separately) reads the annotation and provisions an NLB pointing to the pod IPs.

Cluster Autoscaler vs Karpenter

Cluster Autoscaler adjusts the number of nodes in your node groups based on unscheduled pods and underutilised nodes. It is the traditional approach and works well with managed node groups.

Karpenter (AWS-developed, open source) is the modern alternative. Instead of scaling node groups, Karpenter provisions individual nodes on demand when pods cannot be scheduled. It selects the optimal instance type for each batch of unscheduled pods, supports Spot Instances natively, and consolidates underutilised nodes aggressively. For new EKS clusters, Karpenter is the recommended autoscaler.

Common Interview Questions

Q: What does EKS manage vs what do you manage? AWS manages: Kubernetes API server, etcd, scheduler, controller manager, control plane HA across AZs, control plane OS patches, control plane Kubernetes upgrades. You manage: worker node OS updates (or delegate to managed node groups), Kubernetes application deployments, network policy, add-on versions.

Q: What is the difference between a managed node group and a self-managed node group? Managed node groups are EC2 ASGs where AWS manages the AMI, OS patching, and node-level Kubernetes upgrades (with controlled drain and replacement). Self-managed node groups require you to handle all of this manually. Use managed node groups unless you have a specific customisation requirement.

Q: How does IRSA work? EKS exposes an OIDC identity provider. A Kubernetes service account has an annotation linking it to an IAM role that trusts the OIDC provider. When a pod uses that service account, a projected token is mounted. The AWS SDK exchanges that token with STS for temporary IAM credentials via the AssumeRoleWithWebIdentity API.

Q: Can EKS pods use EBS volumes? Yes, with the EBS CSI driver add-on. Create a PersistentVolumeClaim with the gp3 StorageClass; EKS provisions an EBS volume and attaches it to the node where the pod is scheduled. Note that EBS volumes are AZ-specific, so the pod must be scheduled in the same AZ as the volume.