Kubernetes on AWS EKS — Complete Setup Guide 2026

Sanjeev SharmaSanjeev Sharma
6 min read

Advertisement

Introduction

Why This Matters

AWS Elastic Kubernetes Service (EKS) is the most widely used managed Kubernetes platform in 2026. It offloads the control plane — API server, etcd, scheduler, and controller manager — to AWS, letting your team focus on workloads instead of cluster infrastructure. EKS integrates natively with IAM, VPC, ALB, ECR, CloudWatch, and Secrets Manager, making it the natural choice for teams already on AWS.

This guide walks through creating a production-ready EKS cluster from scratch using eksctl and the AWS CLI.

Prerequisites and Tooling

Install the required CLI tools before starting:

# Install AWS CLI v2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip && sudo ./aws/install
 
# Configure AWS credentials
aws configure
# Enter: Access Key ID, Secret Access Key, region (us-east-1), output format (json)
 
# Install eksctl (the official EKS CLI)
curl --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_Linux_amd64.tar.gz" | tar xz
sudo mv eksctl /usr/local/bin
 
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
 
# Verify installations
aws --version
eksctl version
kubectl version --client

Creating an EKS Cluster With eksctl

The fastest way to create a production-ready cluster is with an eksctl config file:

# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
 
metadata:
  name: my-production-cluster
  region: us-east-1
  version: "1.29"
 
vpc:
  cidr: "10.0.0.0/16"
  nat:
    gateway: HighlyAvailable  # NAT gateway per AZ
 
managedNodeGroups:
  - name: ng-general
    instanceType: t3.medium
    minSize: 2
    maxSize: 10
    desiredCapacity: 3
    volumeSize: 50
    privateNetworking: true
    labels:
      role: worker
    tags:
      Environment: production
    iam:
      withAddonPolicies:
        imageBuilder: true
        autoScaler: true
        albIngress: true
        cloudWatch: true
 
addons:
  - name: vpc-cni
    version: latest
  - name: coredns
    version: latest
  - name: kube-proxy
    version: latest
  - name: aws-ebs-csi-driver
    version: latest
    wellKnownPolicies:
      ebsCSIController: true
 
cloudWatch:
  clusterLogging:
    enableTypes: ["api", "audit", "authenticator", "controllerManager", "scheduler"]
# Create the cluster (takes ~15 minutes)
eksctl create cluster -f cluster.yaml
 
# Update kubeconfig to connect kubectl to the new cluster
aws eks update-kubeconfig --name my-production-cluster --region us-east-1
 
# Verify nodes are ready
kubectl get nodes -o wide

IAM Roles for Service Accounts (IRSA)

IRSA is the AWS-native way to give Kubernetes pods fine-grained AWS permissions without sharing node-level IAM roles.

# Enable OIDC provider for the cluster
eksctl utils associate-iam-oidc-provider \
  --cluster my-production-cluster \
  --region us-east-1 \
  --approve
 
# Create a service account with an S3 read policy
eksctl create iamserviceaccount \
  --cluster my-production-cluster \
  --namespace my-app \
  --name s3-reader \
  --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
  --approve
 
# Use the service account in a Pod
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  namespace: my-app
spec:
  serviceAccountName: s3-reader
  containers:
    - name: app
      image: my-app:latest
      # AWS SDK automatically picks up credentials via projected token

AWS Load Balancer Controller

The AWS Load Balancer Controller provisions Application Load Balancers (ALB) for Kubernetes Ingress resources.

# Add EKS Helm repo
helm repo add eks https://aws.github.io/eks-charts
helm repo update
 
# Install the controller
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=my-production-cluster \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller

Sample Ingress that provisions an ALB:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  namespace: my-app
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:123456789:certificate/abc
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

Cluster Autoscaler

The Cluster Autoscaler adds or removes EC2 nodes based on pending pods.

helm repo add autoscaler https://kubernetes.github.io/autoscaler
helm install cluster-autoscaler autoscaler/cluster-autoscaler \
  --namespace kube-system \
  --set autoDiscovery.clusterName=my-production-cluster \
  --set awsRegion=us-east-1 \
  --set rbac.serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::123456789:role/cluster-autoscaler

Useful EKS Operations

# List all EKS clusters in a region
aws eks list-clusters --region us-east-1
 
# Describe a cluster
aws eks describe-cluster --name my-production-cluster --region us-east-1
 
# Update cluster Kubernetes version
eksctl upgrade cluster --name my-production-cluster --region us-east-1 --approve
 
# Add a new managed node group
eksctl create nodegroup \
  --cluster my-production-cluster \
  --name ng-spot \
  --spot \
  --instance-types t3.medium,t3.large \
  --nodes-min 1 \
  --nodes-max 5
 
# Delete the cluster (removes all resources)
eksctl delete cluster --name my-production-cluster --region us-east-1

Common Mistakes

  • Using the default VPC — always create a dedicated VPC with private subnets for worker nodes; the default VPC puts everything on public subnets.
  • Not enabling IRSA — using node-level IAM roles grants all pods on a node the same AWS permissions; IRSA provides pod-level isolation.
  • Skipping control plane logs — enable API, audit, and authenticator logs to CloudWatch; without them, debugging auth issues is nearly impossible.
  • Under-provisioning node groups — set minSize and maxSize correctly and enable the Cluster Autoscaler from day one; manual scaling causes outages.
  • Not tagging subnets — EKS requires specific tags on subnets for the load balancer controller and Cluster Autoscaler to discover them automatically.

Best Practices

  • Use managed node groups — they handle node draining, OS patching, and rolling updates automatically.
  • Enable spot instances for non-critical workloads — mix on-demand and spot instance types in node groups to reduce cost by 60-80%.
  • Use private endpoints — set cluster endpoint access to private-only and use VPN or AWS Systems Manager for kubectl access.
  • Rotate node groups on version upgrades — create a new node group with the updated AMI and drain the old one instead of in-place upgrades.
  • Implement Pod Disruption Budgets — ensure rolling updates and autoscaler operations don't take down all replicas of a service simultaneously.

Key Takeaways

  • AWS EKS manages the Kubernetes control plane (API server, etcd, scheduler) on your behalf; you only manage worker node groups.
  • Use eksctl with a declarative YAML config file for reproducible, version-controlled cluster creation.
  • IRSA (IAM Roles for Service Accounts) provides pod-level AWS permissions using OIDC federation — safer than node-level IAM roles.
  • The AWS Load Balancer Controller provisions ALBs from Kubernetes Ingress resources using annotations.
  • The Cluster Autoscaler automatically adjusts node group size based on pending pod resource requests.
  • EKS managed node groups handle OS patching and node replacement automatically, reducing operational burden.
  • Always use private subnets for worker nodes and enable control plane logging to CloudWatch from cluster creation.
  • Subnet tagging (kubernetes.io/role/internal-elb and kubernetes.io/cluster/name) is required for AWS add-ons to discover VPC resources.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading