Amazon EKS — VPC Design for EKS

3 minute read

A well-designed VPC is the foundation of your EKS cluster. Poor VPC design leads to IP exhaustion, security gaps, and operational headaches. Let’s get it right from the start.


Reference Architecture

The recommended VPC architecture for EKS:

VPC (10.0.0.0/16 — 65,536 IPs)
├── Public Subnets (3 AZs)
│   ├── 10.0.101.0/24 (AZ-a) — NAT GW, ALB, bastion
│   ├── 10.0.102.0/24 (AZ-b) — NAT GW, ALB
│   └── 10.0.103.0/24 (AZ-c) — NAT GW, ALB
├── Private Subnets (3 AZs)
│   ├── 10.0.1.0/24 (AZ-a) — Worker nodes, pods
│   ├── 10.0.2.0/24 (AZ-b) — Worker nodes, pods
│   └── 10.0.3.0/24 (AZ-c) — Worker nodes, pods
└── Intra Subnets (optional, 3 AZs)
    ├── 10.0.51.0/24 (AZ-a) — EKS control plane ENIs
    ├── 10.0.52.0/24 (AZ-b) — EKS control plane ENIs
    └── 10.0.53.0/24 (AZ-c) — EKS control plane ENIs

Subnet Types

Subnet Type Route to Internet Use Case
Public Direct (via Internet Gateway) NAT Gateways, internet-facing ALBs, bastion hosts
Private Via NAT Gateway Worker nodes, pods, internal ALBs
Intra No internet route EKS control plane ENIs (maximum isolation)

Subnet Sizing

VPC CNI assigns a VPC IP to every pod. This means you need more IPs than a typical VPC setup.

How Many IPs Do You Need?

Total IPs needed = (Number of nodes) + (Max pods per node × Number of nodes)

For example, 10 nodes with 58 pods each:

10 + (58 × 10) = 590 IPs minimum
Subnet Size IPs Available Good For
/24 251 usable Small clusters (dev/test)
/22 1,019 usable Medium clusters
/20 4,091 usable Large clusters
/19 8,187 usable Very large clusters

Tip: Use /19 or /20 for private subnets in production. IP exhaustion is the most common VPC issue in EKS.


Subnet Tagging

EKS and the AWS Load Balancer Controller require specific subnet tags for auto-discovery:

All EKS Subnets

kubernetes.io/cluster/<cluster-name> = shared

Public Subnets (Internet-Facing Load Balancers)

kubernetes.io/role/elb = 1

Private Subnets (Internal Load Balancers and Nodes)

kubernetes.io/role/internal-elb = 1

Terraform Example

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = "eks-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19"]
  public_subnets  = ["10.0.96.0/24", "10.0.97.0/24", "10.0.98.0/24"]

  enable_nat_gateway   = true
  single_nat_gateway   = false  # HA: one per AZ
  enable_dns_hostnames = true

  public_subnet_tags = {
    "kubernetes.io/role/elb"                    = 1
    "kubernetes.io/cluster/my-cluster"          = "shared"
  }

  private_subnet_tags = {
    "kubernetes.io/role/internal-elb"           = 1
    "kubernetes.io/cluster/my-cluster"          = "shared"
  }
}

NAT Gateway Patterns

Pattern Cost HA Use Case
Single NAT Gateway ~$33/mo No Dev/test only
NAT Gateway per AZ ~$99/mo (3 AZs) Yes Production
No NAT Gateway $0 — Fully private (VPC endpoints only)

When Nodes Need Outbound Internet

Nodes in private subnets need a NAT Gateway to:

  • Pull container images from Docker Hub or other public registries
  • Call AWS APIs (unless VPC endpoints are configured)
  • Access external services

Reducing NAT Gateway Costs

  • Use VPC endpoints for ECR, S3, STS, CloudWatch (avoid routing AWS traffic through NAT)
  • Pull images from ECR (free data transfer via VPC endpoint)
  • Use a single NAT Gateway for non-production environments

Multi-AZ Design

Always deploy across at least 3 Availability Zones for production:

  • Worker nodes should be spread across 3 AZs
  • Each AZ gets its own private and public subnet
  • Pod Disruption Budgets (PDBs) ensure pods survive AZ failures
  • Use topology spread constraints for even distribution
topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: my-app

VPC Endpoints for EKS

Reduce NAT Gateway dependency by creating VPC endpoints:

Service Endpoint Type Purpose
com.amazonaws.region.ec2 Interface EC2 API calls
com.amazonaws.region.ecr.api Interface ECR API
com.amazonaws.region.ecr.dkr Interface ECR Docker pull
com.amazonaws.region.s3 Gateway S3 (ECR image layers)
com.amazonaws.region.sts Interface STS (for IRSA tokens)
com.amazonaws.region.logs Interface CloudWatch Logs
com.amazonaws.region.elasticloadbalancing Interface ELB API

What’s Next?

Let’s understand how the VPC CNI plugin assigns IP addresses to pods.

Next: Amazon VPC CNI →

Updated: