Amazon EKS now runs Kubernetes 1.36 in production, and the gap between “I have an AWS account” and “I have a cluster taking real traffic” is smaller than most engineers expect. This guide walks through the entire process: provisioning a production-ready EKS cluster, wiring up node groups, deploying a working application, and handling the operational details that tutorials usually skip. By the end you’ll have a live cluster running Kubernetes 1.36.3 on AWS, a deployed app behind a load balancer, and a checklist for keeping the whole thing patched and cost-controlled.
This is not a toy walkthrough. It covers IAM roles, VPC networking, node group sizing, Helm-based add-ons, autoscaling, and the troubleshooting steps you’ll actually need when something breaks at 2 a.m. Budget about 100 minutes for the full build if you’re following along on a fresh AWS account.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why Run Kubernetes on AWS in 2026
Kubernetes on AWS mostly means Amazon EKS (Elastic Kubernetes Service), the managed control plane that removes the burden of running etcd and the API server yourself. As of August 2026, EKS supports Kubernetes 1.36 as its newest available version, with the upstream project having shipped 1.36.3 as the latest patch release on July 22, 2026, per the official Kubernetes release page. Active support for 1.36 runs through April 2027, with maintenance support extending to June 2027, according to endoflife.date’s Kubernetes tracker.
The three realistic paths for running Kubernetes on AWS are: a fully managed EKS cluster, a self-managed cluster on raw EC2 instances, or an Infrastructure-as-Code deployment using Terraform or eksctl on top of EKS. Self-managing your own control plane on EC2 means patching etcd, rotating certificates, and handling API server high availability by hand — a maintenance burden most teams no longer accept when EKS handles it for a per-cluster fee. That’s why this tutorial focuses on EKS with eksctl, the CLI tool that wraps CloudFormation to provision clusters in a fraction of the clicks a manual VPC-and-IAM setup would take.
EKS platform versions matter here too. Each Kubernetes minor version on EKS ships multiple platform versions that bundle security patches independent of the Kubernetes version number itself. For Kubernetes 1.31, for example, AWS shipped platform version eks.64 in June 2026 with security fixes, while an earlier eks.63 build was discarded internally and never released, according to AWS’s EKS platform versions documentation. The same pattern applies going forward for 1.36 — you’ll want to track platform versions, not just the Kubernetes minor version, when auditing what’s actually running in your cluster.
There’s also a practical reason EKS wins out over self-hosted control planes for most teams: certificate rotation, etcd defragmentation, and API server patching are exactly the kind of undifferentiated operational work that doesn’t move a product forward. AWS handles all three automatically on EKS, and the service now spans multi-AZ control plane redundancy by default, meaning a single availability zone outage no longer takes down cluster API access the way early self-hosted deployments sometimes did. That reliability baseline, combined with tight IAM integration, is the main reason EKS adoption has kept climbing even as competing managed Kubernetes offerings on Azure (AKS) and Google Cloud (GKE) have closed feature gaps.
Prerequisites and Required Versions
Get these tools installed and versions confirmed before starting. Mismatched versions are the number one cause of cluster creation failures reported in AWS support tickets and community forums.
| Tool | Minimum Version | Recommended Version | Install Command |
|---|---|---|---|
| AWS CLI | v2.15 | v2.27 or newer | curl / installer package |
| eksctl | v0.190 | latest stable | brew install eksctl or GitHub release binary |
| kubectl | v1.34 | v1.36 (match cluster minor version ±1) | curl -LO from Kubernetes release URL |
| Helm | v3.14 | v3.16 or newer | curl get_helm.sh | bash |
| IAM permissions | AdministratorAccess (initial setup) | Scoped EKS/EC2/IAM policy post-setup | IAM console or CLI |
You also need an AWS account with billing enabled, a service quota that allows at least one VPC and one Elastic IP in your target region, and roughly $350-450/month in budget if you leave the cluster running (more on cost breakdown later). Working knowledge of YAML and basic container concepts helps but isn’t strictly required — every manifest in this guide is provided in full.
Kubernetes itself follows a policy of supporting the three most recent minor versions. With 1.36 as current, that means 1.34 and 1.35 remain in active support, while 1.31 reached its documented end-of-life status in the AWS EKS lifecycle. Do not build new clusters on end-of-life versions — you lose security patches and, on EKS specifically, AWS auto-upgrades control planes past their support window whether you’re ready or not.
One more prerequisite worth calling out: region selection. Not every AWS region supports every EKS feature on day one — newer capabilities like certain add-on versions or Karpenter integrations sometimes roll out to us-east-1 and us-west-2 first before reaching smaller regions. If you’re building in a less-common region, check the EKS console for feature parity before committing your architecture to capabilities that might not be available there yet. This tutorial uses us-east-1 throughout, both because it has the broadest feature coverage and because most cost examples and quota defaults are calibrated against it.
Step 1: Configure the AWS CLI and Verify Account Access
Start by confirming your AWS CLI is authenticated and pointed at the right account. This catches credential and region mistakes before you’ve committed to a 20-minute cluster provisioning job.
aws configure
# AWS Access Key ID: [your key]
# AWS Secret Access Key: [your secret]
# Default region name: us-east-1
# Default output format: json
aws sts get-caller-identity
# {
# "UserId": "AIDAEXAMPLE123456",
# "Account": "123456789012",
# "Arn": "arn:aws:iam::123456789012:user/your-username"
# }
If get-caller-identity fails, your credentials aren’t configured correctly — fix that before moving on. Also confirm kubectl and eksctl are installed and reporting sane version numbers:
eksctl version
kubectl version --client
helm version
Step 2: Plan Your VPC and Networking Layout
EKS clusters need a VPC spanning at least two availability zones, with both public and private subnets if you want proper isolation between your load balancers and your worker nodes. eksctl can create this VPC for you automatically, which is the fastest path and the one this tutorial uses. If your organization requires clusters inside an existing VPC (common in enterprises with centralized networking teams), you’ll instead pass --vpc-private-subnets and --vpc-public-subnets flags pointing at pre-existing subnet IDs.
Decide your CIDR range now — changing it later means rebuilding the cluster. A /16 block like 192.168.0.0/16 gives ample room for pod networking under the AWS VPC CNI plugin, which assigns each pod a real VPC IP address rather than using an overlay network. This is one of the more distinctive design choices in EKS compared to self-managed Kubernetes: pods consume actual VPC address space, so undersized CIDR blocks are a common cause of “no IP addresses available” errors once node counts grow.
Step 3: Create the EKS Cluster with eksctl
This is the step that used to take an afternoon of manual IAM role creation, VPC wiring, and CloudFormation debugging. eksctl collapses it into a single YAML config file and one command. Create a file named cluster.yaml:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: ti-production-cluster
region: us-east-1
version: "1.36"
vpc:
cidr: "192.168.0.0/16"
nat:
gateway: HighlyAvailable
managedNodeGroups:
- name: ng-general-1
instanceType: m6i.large
desiredCapacity: 3
minSize: 2
maxSize: 6
volumeSize: 40
privateNetworking: true
labels:
role: general
tags:
environment: production
addons:
- name: vpc-cni
- name: coredns
- name: kube-proxy
- name: aws-ebs-csi-driver
cloudWatch:
clusterLogging:
enableTypes: ["api", "audit", "authenticator"]
Then create the cluster:
eksctl create cluster -f cluster.yaml
# Expected output (abbreviated):
# 2026-08-20 09:14:02 [ℹ] eksctl version 0.203.0
# 2026-08-20 09:14:02 [ℹ] using region us-east-1
# 2026-08-20 09:14:03 [ℹ] setting availability zones to [us-east-1a us-east-1b us-east-1c]
# 2026-08-20 09:14:05 [ℹ] building cluster stack "eksctl-ti-production-cluster-cluster"
# 2026-08-20 09:24:41 [ℹ] deploying stack "eksctl-ti-production-cluster-cluster"
# 2026-08-20 09:31:18 [ℹ] waiting for CloudFormation stack "eksctl-ti-production-cluster-nodegroup-ng-general-1"
# 2026-08-20 09:38:52 [✔] EKS cluster "ti-production-cluster" in "us-east-1" region is ready
This takes 15-25 minutes on average. eksctl provisions the VPC, subnets, NAT gateways, IAM roles for the control plane and node groups, the EKS control plane itself, and a managed node group with the instance count and type you specified. Grab coffee — there’s no faster path here, and repeatedly re-running the command won’t speed it up.
Step 4: Update kubeconfig and Confirm Cluster Access
eksctl updates your local kubeconfig automatically after cluster creation, but it’s worth running the command explicitly, especially if you’re switching between multiple clusters or team members.
aws eks update-kubeconfig --region us-east-1 --name ti-production-cluster
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# ip-192-168-45-12.ec2.internal Ready 4m v1.36.3-eks-abc1234
# ip-192-168-78-90.ec2.internal Ready 4m v1.36.3-eks-abc1234
# ip-192-168-91-34.ec2.internal Ready 4m v1.36.3-eks-abc1234
kubectl get pods -A
# Confirms coredns, kube-proxy, aws-node (VPC CNI), and ebs-csi pods are Running
If nodes show NotReady for more than a few minutes, jump to the troubleshooting section below — this is almost always an IAM or networking misconfiguration, not a Kubernetes problem.
Step 5: Set Up IAM Roles for Service Accounts (IRSA)
IAM Roles for Service Accounts is how pods get scoped AWS permissions without embedding long-lived access keys in your cluster — a pattern every production EKS deployment should use. First, associate an OIDC provider with your cluster (a one-time step per cluster):
eksctl utils associate-iam-oidc-provider \
--region us-east-1 \
--cluster ti-production-cluster \
--approve
# Example: create a scoped role for a pod that needs S3 read access
eksctl create iamserviceaccount \
--name s3-reader \
--namespace default \
--cluster ti-production-cluster \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--approve
Reference the resulting service account name in any pod spec that needs those permissions, and AWS handles the credential exchange transparently via the pod’s projected token. This eliminates the older, riskier pattern of mounting static AWS keys as Kubernetes secrets.
Step 6: Install the AWS Load Balancer Controller
Without this controller, Kubernetes Ingress and Service resources of type LoadBalancer won’t provision real AWS Application Load Balancers or Network Load Balancers. Install it via Helm, which is the officially recommended method:
eksctl create iamserviceaccount \
--cluster=ti-production-cluster \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::123456789012:policy/AWSLoadBalancerControllerIAMPolicy \
--approve
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=ti-production-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
kubectl get deployment -n kube-system aws-load-balancer-controller
# NAME READY UP-TO-DATE AVAILABLE AGE
# aws-load-balancer-controller 2/2 2 2 45s
Check Helm’s official documentation if you hit chart repository issues — Helm 3 removed Tiller entirely, so all operations run client-side against your kubeconfig context.
Step 7: Deploy a Working Application
Now deploy something real. Below is a complete, working three-tier setup: a deployment, a service, and an ingress that provisions an AWS ALB automatically. Save this as app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: public.ecr.aws/nginx/nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
---
apiVersion: v1
kind: Service
metadata:
name: demo-app-svc
spec:
selector:
app: demo-app
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app-svc
port:
number: 80
kubectl apply -f app.yaml
kubectl get ingress demo-app-ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# demo-app-ingress alb * k8s-default-demoapp-a1b2c3d4e5.us-east-1.elb.amazonaws.com 80 90s
Give the ALB a couple of minutes to finish provisioning, then hit the address in a browser or with curl. A 200 response with the default nginx welcome page confirms the whole chain — ALB, target groups, service, and pods — is wired correctly.
Step 8: Configure the Cluster Autoscaler or Karpenter
Fixed node counts waste money during quiet periods and throttle you during traffic spikes. You have two realistic options in 2026: the traditional Kubernetes Cluster Autoscaler, or Karpenter, AWS’s own node provisioning engine, which has become the more commonly recommended default for new EKS deployments because it provisions nodes faster and can bin-pack more efficiently across instance types.
# Karpenter install via Helm (values require your cluster endpoint and IAM role ARN)
helm install karpenter oci://public.ecr.aws/karpenter/karpenter \
--version "1.1.0" \
--namespace kube-system \
--set settings.clusterName=ti-production-cluster \
--set settings.interruptionQueue=ti-production-cluster \
--wait
After installing Karpenter, define a NodePool and EC2NodeClass resource specifying which instance families and availability zones it’s allowed to draw from. Karpenter then watches for unschedulable pods and provisions the cheapest instance type that satisfies their resource requests — a meaningful cost lever compared to statically sized managed node groups.
Step 9: Set Up Monitoring and Logging
The cloudWatch.clusterLogging block in your original cluster.yaml already streams control plane logs (API server, audit, authenticator) to CloudWatch Logs. For workload-level metrics, install the Kubernetes metrics server and, ideally, a fuller observability stack:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl top nodes
# NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
# ip-192-168-45-12.ec2.internal 312m 15% 1840Mi 28%
# ip-192-168-78-90.ec2.internal 298m 14% 1795Mi 27%
# ip-192-168-91-34.ec2.internal 340m 17% 1912Mi 29%
Beyond metrics-server, teams typically layer on Prometheus and Grafana for dashboards, or forward metrics into Amazon Managed Service for Prometheus / Amazon Managed Grafana if you’d rather not run and patch that stack yourself.
Step 10: Apply Network Policies and Pod Security
By default, every pod in a Kubernetes cluster can talk to every other pod. That’s rarely what you want in production. Apply a default-deny network policy, then explicitly allow the traffic your application needs:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-demo-app-ingress
namespace: default
spec:
podSelector:
matchLabels:
app: demo-app
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
ports:
- protocol: TCP
port: 80
Note that the AWS VPC CNI plugin needs network policy enforcement enabled explicitly (it’s off by default on older EKS clusters, though newer 1.36 clusters ship it enabled by default in most add-on versions). Confirm with kubectl describe daemonset aws-node -n kube-system and check for ENABLE_NETWORK_POLICY=true in the environment variables if policies don’t seem to be taking effect.
Step 11: Plan Your Upgrade and Version Strategy
Kubernetes minor versions move fast, and EKS auto-upgrades clusters that fall out of support whether or not you’ve tested the new version. Track where your target version sits in the support lifecycle before you commit to it.
| Kubernetes Version | Released | Latest Patch | Active Support Ends | Status |
|---|---|---|---|---|
| 1.36 | April 22, 2026 | 1.36.3 (July 22, 2026) | April 28, 2027 | Current — recommended for new clusters |
| 1.35 | Late 2025 | 1.35.7 (July 22, 2026) | Supported | Stable — safe fallback |
| 1.34 | Mid-2025 | Ongoing patches | Supported | Approaching upgrade window |
| 1.31 | September 18, 2024 | 1.31.14 (final) | April 22, 2026 (reached) | End of life — do not deploy new clusters |
Data compiled from endoflife.date and the official Kubernetes releases page. When you do upgrade, move one minor version at a time — EKS does not support skipping versions, and neither does upstream Kubernetes in any way that’s officially tested.
Step 12: Cost Breakdown and Right-Sizing
EKS charges a flat $0.10/hour per cluster for the control plane — about $73/month — on top of whatever EC2 instances, EBS volumes, load balancers, and NAT gateways your workloads consume, per AWS’s published EKS pricing page. Here’s a realistic breakdown for the cluster built in this tutorial:
| Component | Configuration | Approx. Monthly Cost |
|---|---|---|
| EKS control plane | Flat rate | $73 |
| Worker nodes (3x m6i.large) | On-Demand, us-east-1 | $210-230 |
| NAT Gateway (HA, 3 AZs) | Data processing + hourly | $95-140 |
| Application Load Balancer | 1x internet-facing ALB | $16-25 |
| EBS volumes (3x 40GB gp3) | Node root volumes | $10 |
| CloudWatch Logs | Control plane logging | $5-15 |
Total realistic cost for this exact setup lands around $410-500/month running continuously. Switching worker nodes to Spot Instances (via Karpenter or a Spot-enabled managed node group) commonly cuts that compute line by 60-70%, though you take on interruption risk that requires your app to tolerate node churn. Single-NAT-gateway configurations (versus the HighlyAvailable setting used above) also trim $60-90/month at the cost of cross-AZ resilience — a fair trade for dev/staging clusters, a bad one for production.
Common Pitfalls When Setting Up Kubernetes on AWS
These mistakes account for the overwhelming majority of failed or stalled EKS deployments reported in community forums and support channels.
- Undersized VPC CIDR blocks. Because the AWS VPC CNI assigns pods real VPC IPs, a small CIDR range runs out of addresses as node and pod counts grow. Start with at least a /16.
- Skipping IRSA and using static IAM keys instead. This works initially but becomes a security liability and a compliance blocker the moment anyone audits the cluster.
- Ignoring EKS platform versions. Teams track the Kubernetes minor version but forget platform versions carry independent security patches — a cluster can be “on 1.36” and still be missing critical fixes.
- Running everything in the default namespace. This makes RBAC, network policies, and resource quotas far harder to reason about once more than one team shares a cluster.
- No resource requests or limits on pods. Without them, the scheduler can’t bin-pack effectively, and a single runaway pod can starve its neighbors on the same node.
- Forgetting to delete the cluster after testing. NAT gateways and idle EC2 instances bill by the hour regardless of whether they’re doing anything — a common source of surprise AWS invoices.
- Mixing Karpenter and Cluster Autoscaler on the same node pools. The two systems fight over scaling decisions; pick one per node pool, not both.
Troubleshooting Guide
Here are the specific errors you’re most likely to hit, and what actually fixes them.
- Nodes stuck in
NotReadystatus: Usually an IAM role or security group issue. Check that the node IAM role hasAmazonEKSWorkerNodePolicy,AmazonEKS_CNI_Policy, andAmazonEC2ContainerRegistryReadOnlyattached, and that node security groups allow control plane communication on port 443. - “no IP addresses available in subnet” error: Your CIDR block is exhausted. Either add secondary CIDR blocks to the VPC or switch to a CNI custom networking mode that uses a separate, larger IP pool for pods.
- Ingress stuck with no
ADDRESSassigned: The AWS Load Balancer Controller isn’t running or lacks IAM permissions. Checkkubectl logs -n kube-system deployment/aws-load-balancer-controllerfor explicit IAM denial errors. ImagePullBackOffon private ECR images: The node IAM role is missing ECR pull permissions, or you’re pulling cross-region without replicating the image first.- eksctl create cluster hangs on CloudFormation: Check the CloudFormation console directly — eksctl’s CLI output can lag behind actual stack events by a minute or more. Rollback failures usually point to a service quota limit (VPCs, Elastic IPs, or EC2 instances per region).
- kubectl “Unable to connect to the server”: Your kubeconfig is stale or pointing at the wrong cluster context. Re-run
aws eks update-kubeconfigand confirm withkubectl config current-context. - Pods pending with “Insufficient cpu/memory”: Node capacity is exhausted. Check
kubectl describe nodefor allocatable resources versus what’s already requested, and either scale the node group or right-size pod requests. - Cluster autoscaler not scaling up: Confirm the autoscaler’s IAM role has
autoscaling:DescribeAutoScalingGroupsand related permissions, and that your ASG tags include the requiredk8s.io/cluster-autoscaler/enabledtag. - DNS resolution failing inside pods: CoreDNS pods may be unhealthy or undersized for cluster scale. Check
kubectl get pods -n kube-system -l k8s-app=kube-dnsand review CoreDNS resource limits if you’re seeing timeouts under load. - Helm install fails with “context deadline exceeded”: Usually a networking path issue between your local machine and the API server, or an overloaded control plane during simultaneous mass deployments. Retry with a longer
--timeoutflag.
Advanced Tips for Production EKS Clusters
Once the basics are working, a few practices separate a demo cluster from one that survives a real production incident. First, enable EKS’s managed add-on auto-update where possible — it keeps CoreDNS, kube-proxy, and the VPC CNI patched without manual Helm upgrades, though you should still test upgrades in staging first. Second, use separate node groups per workload type (general compute, GPU, memory-optimized) rather than one large uniform pool — this lets you apply different taints, tolerations, and autoscaling policies per workload class.
Third, adopt GitOps for cluster state instead of manual kubectl apply runs once you’re past the learning phase — tools like Argo CD or Flux turn your Git repository into the single source of truth and make rollbacks a git revert instead of a manual manifest hunt. Fourth, set Pod Disruption Budgets on anything customer-facing so that node upgrades, Spot interruptions, and autoscaler scale-downs don’t take out every replica of a service simultaneously. Finally, budget real time for chaos testing — deliberately terminating nodes and pods in a staging cluster before you trust the same setup in production surfaces gaps that documentation alone won’t reveal.
Connecting a CI/CD Pipeline to Your EKS Cluster
A cluster that only accepts manual kubectl apply commands isn’t production-ready in any organization with more than one contributor. The most common pattern in 2026 pairs a CI system (GitHub Actions, GitLab CI, or CodePipeline) for building and pushing container images with a GitOps controller inside the cluster for the actual deployment step. This separation matters: your CI system never needs direct cluster credentials, which shrinks your attack surface considerably compared to older pipelines that stored a kubeconfig as a CI secret.
Here’s a minimal GitHub Actions workflow that builds an image, pushes it to Amazon ECR, and updates a Kubernetes manifest in a separate GitOps repository that Argo CD or Flux then syncs automatically:
name: build-and-push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecr-push
aws-region: us-east-1
- name: Login to ECR
run: aws ecr get-login-password | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
- name: Build and push
run: |
docker build -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/demo-app:${{ github.sha }} .
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/demo-app:${{ github.sha }}
Note the use of configure-aws-credentials with an assumed IAM role via OIDC federation rather than long-lived access keys stored as GitHub secrets — this is the current recommended pattern and avoids the credential-leak risk that static AWS keys in CI systems have historically created. Set up the trust relationship on the IAM role to accept tokens from your specific GitHub repository and branch, not a blanket GitHub Actions trust policy.
On the deployment side, installing Argo CD takes one Helm command and gives you a reconciliation loop that continuously compares your Git repository’s declared state against the live cluster state, auto-correcting drift:
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace
kubectl get pods -n argocd
# NAME READY STATUS RESTARTS AGE
# argocd-application-controller-0 1/1 Running 0 62s
# argocd-repo-server-7d9f8c9b6-x2k4p 1/1 Running 0 62s
# argocd-server-6c5d8f7b9d-m9p2q 1/1 Running 0 62s
Once Argo CD is running, define an Application resource pointing at your GitOps repository, and every merge to your main branch automatically syncs into the cluster within seconds — no manual deploy step, and a full audit trail in Git history for every change that ever hit the cluster.
Backup, Disaster Recovery, and Multi-Region Considerations
EKS handles control plane availability across multiple AZs automatically, but it does not back up your workload state, persistent volumes, or cluster configuration for you. That’s your responsibility, and it’s frequently skipped until the first real incident forces the issue. Two things need protecting: etcd-managed Kubernetes objects (deployments, services, config maps, secrets) and any persistent data your pods write to EBS-backed volumes.
For cluster object backups, Velero is the most widely used open-source tool, and it integrates directly with the AWS EBS CSI driver you already installed in Step 3 to snapshot both Kubernetes resource state and volume data together:
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.10.0 \
--bucket ti-eks-backups \
--backup-location-config region=us-east-1 \
--snapshot-location-config region=us-east-1 \
--secret-file ./credentials-velero
velero backup create daily-backup --include-namespaces default,kube-system
velero schedule create daily --schedule="0 2 * * *" --include-namespaces default
Schedule backups to run outside peak traffic hours, and periodically test restores into a scratch cluster — a backup you’ve never restored from is a backup you don’t actually have. For multi-region disaster recovery, the realistic pattern is running a second, smaller standby EKS cluster in a different region with the same Velero backup bucket replicated cross-region via S3 replication rules, so a full regional AWS outage doesn’t leave you without a recovery path. This is meaningfully more infrastructure to maintain, so weigh it against your actual uptime requirements rather than defaulting to it for every workload.
Cleaning Up: How to Delete Your Cluster
If this was a test build, tear it down completely to avoid ongoing charges. eksctl removes the cluster and every resource it provisioned, including the VPC, NAT gateways, and IAM roles created specifically for the cluster:
helm uninstall aws-load-balancer-controller -n kube-system
kubectl delete -f app.yaml
eksctl delete cluster --name ti-production-cluster --region us-east-1
# 2026-08-20 11:02:14 [ℹ] deleting EKS cluster "ti-production-cluster"
# 2026-08-20 11:14:47 [✔] all cluster resources were deleted
Double-check the EC2, VPC, and Elastic Load Balancing consoles afterward — orphaned resources (particularly load balancers created outside of eksctl’s own tracking, like ones provisioned by the AWS Load Balancer Controller for Ingress objects you didn’t delete first) are the most common source of lingering charges after a cluster teardown.
RBAC and Access Control Setup
By default, whoever created the EKS cluster has full administrative access, and nobody else has any. This surprises teams moving from self-managed clusters where cluster-admin access was often shared more loosely. EKS uses an aws-auth ConfigMap (or, in newer configurations, EKS Access Entries — the more current, API-driven approach that’s gradually replacing the ConfigMap pattern) to map IAM principals to Kubernetes RBAC roles.
Access Entries are the recommended path for new clusters since they let you grant and audit access through the AWS API and CLI instead of hand-editing a ConfigMap, which was a notoriously easy way to accidentally lock yourself out of a cluster if the YAML got malformed:
aws eks create-access-entry \
--cluster-name ti-production-cluster \
--principal-arn arn:aws:iam::123456789012:role/platform-team \
--type STANDARD
aws eks associate-access-policy \
--cluster-name ti-production-cluster \
--principal-arn arn:aws:iam::123456789012:role/platform-team \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=cluster
For anyone who doesn’t need cluster-wide admin — most application developers, for instance — scope access to a specific namespace using type=namespace and a narrower managed policy like AmazonEKSEditPolicy. Combine this with standard Kubernetes Role and RoleBinding objects for even finer-grained control over specific resource types within a namespace. Treat this exactly like production IAM policy work: least privilege by default, and access reviewed on a schedule, not granted once and forgotten.
EKS vs. Self-Managed Kubernetes vs. Alternatives
It’s worth being clear-eyed about when EKS is the right call versus other approaches on AWS.
| Approach | Control Plane Management | Best For | Tradeoff |
|---|---|---|---|
| Amazon EKS | Fully managed by AWS | Production workloads, teams without dedicated platform engineers | $73/mo flat fee plus infrastructure |
| Self-managed on EC2 (kubeadm) | You manage etcd, API server, upgrades | Air-gapped environments, deep customization needs | Significant ongoing operational burden |
| K3s / lightweight distros on EC2 | You manage a lighter control plane | Edge, dev/test, resource-constrained environments | Reduced feature parity with full Kubernetes |
| ECS (non-Kubernetes) | Fully managed, AWS-proprietary | Teams that don’t need Kubernetes portability | No Kubernetes API compatibility |
For teams already committed to Kubernetes as their orchestration standard — whether for portability, existing tooling investment, or multi-cloud strategy — EKS remains the default choice on AWS. If your workloads don’t specifically need Kubernetes semantics, ECS is worth evaluating as a simpler, more tightly AWS-integrated alternative with less operational surface area.
Frequently Asked Questions
How long does it take to set up a Kubernetes cluster on AWS?
Using eksctl with a config file, cluster provisioning itself takes 15-25 minutes. Add another 20-30 minutes for installing add-ons like the Load Balancer Controller, deploying a test application, and confirming everything works end-to-end. Budget roughly 90-100 minutes total if this is your first time through the process.
What’s the difference between EKS and running Kubernetes manually on EC2?
EKS manages the control plane (API server, etcd, scheduler) for you, including patching and high availability across availability zones. Running Kubernetes manually on EC2 via kubeadm means you own every part of that stack, which gives more customization control but adds significant ongoing operational work, especially around etcd backups and API server certificate rotation.
How much does an EKS cluster cost per month?
The control plane itself is a flat $0.10/hour (about $73/month) per AWS’s published pricing. Total cost depends heavily on worker node instance types, NAT gateway configuration, and load balancer usage — a modest 3-node production setup like the one in this tutorial typically runs $400-500/month, with Spot Instances and single-NAT configurations able to cut that substantially for non-critical environments.
Which Kubernetes version should I use on EKS right now?
Kubernetes 1.36 is the current stable release as of August 2026, with active support running through April 2027. New clusters should target 1.36 or the immediately preceding 1.35 release if you need extra stability margin before adopting the newest minor version. Avoid 1.31 and earlier — that version line has reached end-of-life status.
Do I need Terraform, or is eksctl enough?
eksctl is faster for getting a cluster running and is purpose-built for EKS specifically. Terraform is the better choice if you’re managing the cluster as part of a broader multi-service infrastructure stack, need fine-grained state management, or want a single tool across AWS, other clouds, and on-prem resources. Many teams start with eksctl for speed and migrate cluster definitions into Terraform once the setup stabilizes.
Can I run Kubernetes on AWS without using EKS at all?
Yes — you can install any Kubernetes distribution (vanilla kubeadm, K3s, RKE2, and others) directly on EC2 instances you manage yourself. This avoids the EKS control plane fee but shifts all control plane operations, upgrades, and high-availability engineering onto your team.
What happens if my EKS cluster falls out of Kubernetes version support?
AWS auto-upgrades EKS clusters that reach end-of-standard-support, typically after a grace window, whether or not you’ve tested the target version against your workloads. This is a strong incentive to stay proactively one or two minor versions behind current rather than letting AWS force an upgrade on its own schedule.
Is Karpenter better than the Kubernetes Cluster Autoscaler on AWS?
Karpenter generally provisions nodes faster and can select from a broader range of instance types to fit pending pod requirements more efficiently, which tends to reduce both scaling latency and idle capacity cost. The traditional Cluster Autoscaler remains a solid, well-understood choice, particularly for teams already standardized on fixed node group instance types who don’t need Karpenter’s more dynamic provisioning behavior.


