Amazon EKS Getting Started: Running Kubernetes on AWS in 2026
AWS re:Invent 2023 had a stat that keeps coming up in job postings: EKS adoption grew 88% year-over-year among enterprise AWS customers. That number isn’t surprising if you’ve been watching the container orchestration space. What was ECS-only workloads five years ago has shifted heavily toward Kubernetes, and EKS is where most of that shift lands.
If you’ve used Kubernetes before, EKS will feel familiar with some AWS-specific wrinkles. If you haven’t, EKS is actually one of the more approachable entry points — you don’t manage the control plane, AWS handles the etcd cluster and API server availability, and you’re mostly dealing with worker nodes and workload configuration. This guide covers the parts that trip people up: node setup, IAM integration, and networking decisions you need to make before your first cluster is useful.
What EKS Actually Manages
EKS is a managed Kubernetes control plane. That means AWS runs the API server, scheduler, controller manager, and etcd cluster across multiple availability zones. You don’t SSH into control plane nodes, you don’t worry about etcd backups, and you don’t manage Kubernetes version upgrades on the control plane side. You pay $0.10 per cluster per hour ($72/month) for that management.
What AWS doesn’t manage: your worker nodes. Those are EC2 instances you pay for separately. You’re also responsible for the kubelet running on those nodes, the CNI plugin handling pod networking, and keeping your workloads healthy. The control plane is managed; everything below it is yours.
Three compute options exist for running workloads on EKS:
Managed node groups are EC2 instances that AWS provisions and manages within an Auto Scaling group. You pick the instance type, the AMI gets chosen automatically, and AWS handles node draining during updates. This is the right default for most workloads — you control instance type and scaling bounds, AWS handles the lifecycle.
Self-managed nodes give you full control over EC2 configuration. You’re running your own ASG, you choose the AMI, you handle node upgrades. Use this only if you have specific requirements that managed node groups can’t satisfy — custom kernel parameters, specialized instance configurations, on-premises hardware integration.
Fargate runs pods on AWS-managed infrastructure. No nodes to manage, no capacity planning. You define a Fargate profile specifying which namespaces and label selectors run on Fargate, and matching pods get scheduled automatically. Fargate works well for batch jobs and variable workloads, but it doesn’t support DaemonSets, stateful storage beyond EFS, and GPU workloads. The EKS Fargate deep dive covers where Fargate fits and where it doesn’t.
Setting Up Your First Cluster
The two paths to a cluster: AWS console and eksctl. The console is fine for understanding the options; eksctl is what you actually use for repeatable setups.
Install eksctl and configure AWS credentials first:
# Install eksctl on Linux
curl --silent --location "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin
# Verify
eksctl version
# Ensure AWS CLI is configured
aws sts get-caller-identity
A basic cluster with managed node groups:
eksctl create cluster \
--name my-cluster \
--region us-east-1 \
--nodegroup-name workers \
--node-type t3.medium \
--nodes 2 \
--nodes-min 1 \
--nodes-max 4 \
--managed
This creates a VPC, subnets across three AZs, an EKS control plane, and a managed node group. It takes 15-20 minutes. When it finishes, eksctl updates your kubeconfig automatically.
Better approach for anything beyond a test cluster: a YAML config file. This is what you’d commit to version control.
# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: production-cluster
region: us-east-1
version: "1.32"
vpc:
cidr: 10.0.0.0/16
nat:
gateway: HighlyAvailable
managedNodeGroups:
- name: workers-general
instanceType: t3.large
minSize: 2
maxSize: 10
desiredCapacity: 2
availabilityZones: ["us-east-1a", "us-east-1b", "us-east-1c"]
labels:
role: worker
tags:
Environment: production
iam:
withAddonPolicies:
albIngress: true
cloudWatch: true
autoScaler: true
eksctl create cluster -f cluster.yaml
The YAML approach lets you add node groups later, configure specific IAM addon policies, and version-control your cluster definition. Start with it even for non-production clusters — the habits carry over.
IAM: The Part That Breaks First
EKS IAM has two layers that people frequently confuse.
The first layer is the node IAM role. Every EC2 node in your cluster needs an IAM role with permissions to join the cluster and pull from ECR. The minimum required policies are AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, and AmazonEC2ContainerRegistryReadOnly. eksctl creates this role automatically when you create a node group. Without it, nodes can’t register with the control plane.
The second layer is IAM Roles for Service Accounts (IRSA). This is how Kubernetes pods get AWS permissions without storing credentials in the pod. The mechanism works through OpenID Connect: EKS clusters expose an OIDC endpoint, you create an IAM role that trusts that endpoint, and pods annotated with that service account get temporary credentials via the AWS SDK’s credential chain automatically.
Setting up IRSA for a pod that needs S3 access:
# Enable OIDC provider for your cluster (one-time setup)
eksctl utils associate-iam-oidc-provider \
--cluster my-cluster \
--approve
# Create a service account with an attached IAM role
eksctl create iamserviceaccount \
--name s3-reader \
--namespace default \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--approve
# Verify the service account annotation
kubectl get serviceaccount s3-reader -o yaml
The service account gets an annotation pointing at the IAM role ARN. Pods using that service account can call the S3 API without any credentials in their environment variables or secrets. The AWS IAM roles and policies guide covers the trust policy mechanics — worth reading before you start building IAM roles manually.
When IRSA breaks, the error is usually NoCredentialProviders or AccessDenied. Check the OIDC association first (aws eks describe-cluster --name my-cluster --query cluster.identity.oidc), then check the IAM role’s trust policy to confirm it lists your cluster’s OIDC provider URL.
For the full cluster access control model — Access Entries replacing the deprecated aws-auth ConfigMap, Kubernetes RBAC roles and bindings, and Pod Security Standards — the EKS RBAC and security guide covers all three layers in depth.
Networking Basics
EKS uses the VPC CNI plugin by default. Each pod gets an IP address from your VPC’s CIDR range, pulled from the node’s network interfaces. This is different from how networking works in non-AWS Kubernetes: pods aren’t on an overlay network, they have real VPC IP addresses that can be targeted directly by security groups and route tables.
The upside: pods can communicate with other VPC resources without NAT. Your pod can connect directly to an RDS instance using the database’s private IP. The downside: you can run out of IP addresses. Each EC2 instance has a limited number of network interfaces, and each interface has a limited number of secondary IPs. A t3.medium supports 3 interfaces with 6 IPs each — 18 pod IP slots minus the primary interface IP. Plan your subnets with this in mind. For clusters that hit IP limits, prefix delegation and custom networking are the two main solutions — the EKS VPC CNI deep dive covers both.
For production clusters, put your nodes in private subnets and expose workloads through a load balancer. Public node groups work for development but aren’t what you want for anything user-facing. eksctl’s vpc.nat: HighlyAvailable config sets up one NAT gateway per AZ, which costs money but avoids a single-AZ failure killing outbound traffic from all your pods.
The AWS Load Balancer Controller replaces the deprecated in-tree load balancer integration. Install it with Helm and configure it through Kubernetes Ingress or Service resources — it provisions ALBs and NLBs automatically. The controller needs IRSA with specific IAM permissions to create and manage load balancer resources.
Core Kubernetes Concepts You’ll Need Day One
A cluster without running workloads is just an expense. Getting your first service running requires understanding four Kubernetes objects.
A Deployment describes what container to run, how many replicas, and the rollout strategy. It’s not running instances directly — it manages a ReplicaSet that manages Pods. You update a Deployment, and it rolling-updates the ReplicaSet.
A Service exposes your Deployment to network traffic. ClusterIP gives you an internal DNS name and IP within the cluster. NodePort exposes a port on every node. LoadBalancer provisions an AWS load balancer. For most HTTP workloads, you want ClusterIP Services exposed through an Ingress, not LoadBalancer Services (each LoadBalancer Service creates a separate NLB).
An Ingress defines routing rules at the application layer. The AWS Load Balancer Controller watches Ingress resources and creates ALB rules. One ALB, multiple path-based routes, multiple backend Services.
A ConfigMap holds non-sensitive configuration that pods can consume as environment variables or mounted files. Secrets hold sensitive data — but understand that Kubernetes Secrets are base64-encoded, not encrypted, by default. If you need real encryption at rest, enable envelope encryption with a KMS key at the cluster level, or fetch secrets from AWS Secrets Manager via the external secrets operator rather than storing them in Kubernetes.
Cluster Upgrades
EKS supports the three most recent Kubernetes minor versions. You need to upgrade your cluster before the version you’re running reaches end of support. AWS announces EOL dates when versions hit that window.
The upgrade process: control plane first, then node groups. eksctl handles both:
# Upgrade control plane to next minor version
eksctl upgrade cluster --name my-cluster --version 1.32 --approve
# Upgrade managed node group (creates new nodes, drains old ones)
eksctl upgrade nodegroup \
--name workers-general \
--cluster my-cluster \
--kubernetes-version 1.32
One version at a time — you can’t skip minor versions. Test in a non-production cluster first. Check your workloads against the Kubernetes changelog for removed or deprecated APIs before upgrading. Things break most often at API boundaries: a resource that was apps/v1beta1 gets removed, and any Helm chart or manifest using the old API version fails to apply after the upgrade.
Monitoring Your Cluster
CloudWatch Container Insights gives you cluster-level metrics — node CPU and memory, pod counts, network traffic — without running your own Prometheus stack. It requires the CloudWatch agent deployed as a DaemonSet. For a full setup walkthrough including alarm configuration, the AWS CloudWatch deep dive covers the agent configuration and how to set meaningful thresholds.
For application-level observability, OpenTelemetry is the right direction. The OpenTelemetry + CloudWatch observability post walks through instrumenting services running on EKS with ADOT, which is the AWS-maintained distribution of the OpenTelemetry collector.
EKS vs ECS: When to Use Which
ECS is simpler. It has no control plane cost, fewer IAM layers to understand, and native integration with all AWS services. For teams running straightforward containerized services where Kubernetes abstractions don’t add value, ECS is the right choice.
EKS makes sense when you need Kubernetes-specific capabilities: CRDs, Helm charts, operators, multi-cluster federation, or portability to other clouds. If your organization is Kubernetes-first — existing tooling, existing Helm charts, engineers who already know kubectl — EKS is the obvious fit. If you’re starting from scratch and none of that applies, benchmark ECS against EKS for your use case before committing.
Mixing is valid. Many organizations run ECS for simple services and EKS for workloads that genuinely need orchestration features. The two can share a VPC and security groups. There’s no requirement to standardize on one.
Cost Awareness
The $0.10/hour control plane cost is fixed per cluster regardless of workload. Don’t create development clusters you leave running overnight — eksctl delete cluster when you’re done. One idle dev cluster is $72/month for nothing.
Node costs are EC2 costs at standard rates. Spot instances work well for stateless, interruptible workloads and can cut node costs 60-70%. Karpenter — the cluster autoscaler replacement — handles Spot provisioning better than the Cluster Autoscaler by selecting optimal instance types dynamically. If you’re running variable workloads, look at Karpenter autoscaling on EKS before settling on a scaling strategy.
For workloads that don’t need to run continuously, cluster shutdown scripts and scheduled scaling rules can reduce costs substantially. The control plane runs whether or not pods are scheduled on it, so that $72/month is your floor.
Getting started is straightforward. Running EKS well in production takes more: IAM right-sizing, network planning, upgrade discipline, and monitoring. Start with a managed node group, get a workload running, then layer in the more advanced configuration from there.
Comments