Creating an EKS Cluster

Creating a cluster is a single API call followed by about ten minutes of waiting. The decisions around it matter more than the command: where it runs, who can reach it, and how nodes are provided. Make sure the prerequisites are in place first.

Three ways to create one

MethodBest forTrade-off
eksctlLearning, experiments, small teamsDrives CloudFormation; harder to integrate with existing infrastructure code
Terraform / OpenTofuAnything long-livedMore to write, but reviewable, repeatable and composable
ConsoleSeeing the options onceNot reproducible

Whichever you choose, the result should end up as code in a repository. A cluster built by hand cannot be rebuilt after a disaster or recreated for a blue-green upgrade.

With eksctl

The one-liner creates a new VPC, the cluster and compute:

eksctl create cluster --name my-cluster --region eu-west-1 --enable-auto-mode

A config file is better, because it can be reviewed and re-applied:

# cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: my-cluster
  region: eu-west-1
  version: "1.34"
  tags: { team: platform, environment: dev }
 
vpc:
  cidr: 10.0.0.0/16
  nat: { gateway: HighlyAvailable } # one NAT gateway per zone
  clusterEndpoints:
    publicAccess: true
    privateAccess: true
  publicAccessCIDRs: ["203.0.113.0/24"]
 
accessConfig:
  authenticationMode: API
  bootstrapClusterCreatorAdminPermissions: false
  accessEntries:
    - principalARN: arn:aws:iam::111122223333:role/platform-admin
      accessPolicies:
        - policyARN: arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy
          accessScope: { type: cluster }
 
secretsEncryption:
  keyARN: arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
 
cloudWatch:
  clusterLogging:
    enableTypes: ["audit", "authenticator", "api"]
 
addons:
  - name: vpc-cni
  - name: coredns
  - name: kube-proxy
  - name: eks-pod-identity-agent
  - name: aws-ebs-csi-driver
 
managedNodeGroups:
  - name: general
    amiFamily: AmazonLinux2023
    instanceTypes: ["m7g.large", "m6g.large"]
    minSize: 2
    desiredCapacity: 3
    maxSize: 6
    privateNetworking: true
    volumeSize: 80
eksctl create cluster -f cluster.yaml

What the important lines do:

SettingWhy
versionPin it. Otherwise you get whatever is the default that day.
clusterEndpoints and publicAccessCIDRsKeep the API reachable from your network only — endpoint access
authenticationMode: APIUse access entries, not the legacy ConfigMap
bootstrapClusterCreatorAdminPermissions: falseAdmin goes to a named role, not to whoever ran the command
secretsEncryptionEnvelope-encrypt Secrets with your own KMS key
clusterLoggingAudit logs exist before you need them — control plane logs
privateNetworking: trueNodes in private subnets with no public IPs
addonsManaged add-ons are versioned and upgradable through the EKS API

To use Auto Mode instead of node groups, replace managedNodeGroups and the compute and storage add-ons with autoModeConfig: { enabled: true } — EKS Auto Mode.

With Terraform

The community terraform-aws-modules/eks module covers the same ground:

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 21.0"
 
  name               = "my-cluster"
  kubernetes_version = "1.34"
 
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets
 
  endpoint_public_access       = true
  endpoint_public_access_cidrs = ["203.0.113.0/24"]
 
  enable_cluster_creator_admin_permissions = false
  access_entries = {
    platform = {
      principal_arn = "arn:aws:iam::111122223333:role/platform-admin"
      policy_associations = {
        admin = {
          policy_arn   = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
          access_scope = { type = "cluster" }
        }
      }
    }
  }
 
  compute_config = {
    enabled    = true
    node_pools = ["general-purpose", "system"]
  }
}

Keep the VPC, the cluster and the workloads in separate states, so a change to an application’s queue cannot plan a change to the cluster — Terraform state and collaboration. Install in-cluster software with GitOps rather than Terraform’s Helm provider where you can; mixing the two creates ordering and drift problems. See Terraform.

What exists afterwards

ResourceNotes
Control planeManaged by AWS, billed per hour
Cluster security groupAttached to the control plane ENIs and, by default, to nodes
ENIs in your subnetsHow the control plane reaches nodes and webhooks
OIDC issuer URLUsed by IRSA
Cluster and node IAM rolesCreated by eksctl; you supply or create them with Terraform
Nodes or Auto Mode node poolsBilled as EC2
With eksctl: a VPC, subnets, NAT gateways and CloudFormation stacksThe NAT gateways are a noticeable cost

Verify

aws eks describe-cluster --name my-cluster \
  --query 'cluster.{status:status,version:version,endpoint:endpoint,auth:accessConfig.authenticationMode}'
 
aws eks update-kubeconfig --name my-cluster --region eu-west-1
kubectl get nodes -o wide
kubectl get pods -n kube-system
aws eks list-addons --cluster-name my-cluster

All nodes Ready and all kube-system pods Running means the cluster is usable. If nodes never appear, see common EKS issues.

Hard to change later

DecisionChangeable?
IP family (IPv4 or IPv6)No
The VPCNo; subnets can be added
Cluster nameNo
Secrets encryption keyCan be enabled, not removed or swapped
Authentication modeForward only
Kubernetes versionUpward only, one minor at a time
Endpoint access, logging, add-ons, computeYes

Subnet sizing deserves the most thought, because every pod uses a VPC address — EKS networking.

Next

Deploy your first application.

Across the wiki