🚀 Complete Guide to EKS with Terraform: Add-ons, Nodes, VPCs, and More

Amazon EKS is a managed Kubernetes service that lets you run Kubernetes clusters at scale on AWS. But doing it manually through the console? Tedious. That's where Terraform becomes your best friend.
In this blog, we’ll cover:
✅ Setting up EKS with Terraform
✅ Installing cluster add-ons
✅ Managing EKS nodes (with autoscaling)
✅ Configuring your VPC via Terraform
📁 Project Structure
Here’s the structure we’ll be working with:
├── provider.tf
├── locals.tf
├── vpc.tf
├── eks.tf ✅
├── outputs.tf
└── variables.tf
Let’s now go deep into eks.tf
.
🧩 Complete eks.tf
File
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.31"
cluster_name = local.name
cluster_version = "1.31"
cluster_endpoint_public_access = true
public_access_cidrs = ["203.0.113.0/24", "0.0.0.0/0"]
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
control_plane_subnet_ids = module.vpc.intra_subnets
###########################
# Cluster Add-ons Section
###########################
cluster_addons = {
vpc-cni = {
most_recent = true
}
kube-proxy = {
most_recent = true
}
core-dns = {
most_recent = true
}
}
##########################################################
# Managed Node Groups - Default & Specific Configuration
##########################################################
eks_managed_node_group_defaults = {
instance_types = ["t2.medium"]
attach_cluster_primary_security_group = true
}
eks_managed_node_groups = {
cluster-ng = {
instance_types = ["t2.medium"]
min_size = 2
max_size = 3
desired_size = 2
capacity_type = "SPOT"
tags = {
Environment = local.env
Terraform = "true"
}
}
}
tags = {
Terraform = "true"
Environment = local.env
}
}
✅ Explanation:
module "eks"
: This creates a reusable module namedeks
.source
: Points to the Terraform registry module for EKS.version
: Ensures compatibility by locking the version to~> 20.31
.cluster_name
: Takes its value from thelocals.tf
file (e.g.,eks-cluster
).cluster_version
: Explicitly sets the Kubernetes version. If not set, the latest version will be used.cluster_endpoint_public_access = true
: This allows you to access the Kubernetes API from outside the VPC (e.g., your laptop or Jenkins).public_access_cidrs
: Limits who can access the public endpoint by IP range.0.0.0.0/0
opens it to everyone (not recommended for production).
vpc_id
: Passes the VPC ID from yourvpc.tf
module.subnet_ids
: These are the private subnets where your worker nodes (EC2 instances) will live.control_plane_subnet_ids
: These are infra subnets, used only for the EKS control plane (for ENIs, not pods).Enables essential Kubernetes components:
vpc-cni
: Handles pod networking via AWS VPC.kube-proxy
: Enables networking between services and pods.core-dns
: Allows DNS-based service discovery inside the cluster.
most_recent = true
: Ensures you're always using the latest stable version of each addon.eks_managed_node_group_defaults
: Applies defaults to all node groups.instance_types
: Defines EC2 type for nodes.attach_cluster_primary_security_group
: Ensures all nodes join the same security group as the cluster.eks_managed_node_groups
: Defines one or more autoscaling worker node groups.cluster-ng
: The name of this node group.instance_types
: Nodes will be launched usingt2.medium
instances.min_size
,max_size
,desired_size
: This node group will auto-scale between 2 and 3 nodes, maintaining 2 by default.capacity_type = "SPOT"
: Uses spot instances to save cost.tags
: Helps label and filter resources (e.g., by environment).These are global tags applied to all resources created by the EKS module.
Terraform = "true"
: Marks the resource as managed by Terraform.Environment = local.env
: Takes value like"dev"
or"prod"
fromlocals.tf
.
🧠 Reference: locals.tf
locals {
region = "us-east-2"
name = "eks-cluster"
vpc_cidr = "10.0.0.0/16"
azs = ["us-east-2a", "us-east-2b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
intra_subnets = ["10.0.5.0/24", "10.0.6.0/24"]
env = "dev"
}
🌐 Reference: vpc.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "${local.name}-vpc"
cidr = local.vpc_cidr
azs = local.azs
private_subnets = local.private_subnets
public_subnets = local.public_subnets
intra_subnets = local.intra_subnets
enable_nat_gateway = true
enable_vpn_gateway = true
tags = {
Terraform = "true"
Environment = local.env
}
}
✅ NAT Gateway allows private subnets to reach the internet (e.g., for pulling Docker images).
✅ VPN Gateway enables secure connections to on-prem infrastructure.
🔐 Why Use Private Subnets for EKS?
EKS worker nodes and control plane components should live in private subnets for better security. They don’t need public IPs — they just require outbound access via a NAT Gateway to fetch container images.
🧠 Public subnets are best for:
Load Balancers (ALB, NLB)
Bastion Hosts
Public-facing services
🔄 What are Infra Subnets?
Infra subnets are dedicated private subnets used only by the EKS control plane's ENIs (Elastic Network Interfaces).
🛡 Why separate them?
Network Isolation: Separate management traffic from application traffic.
Compliance: Some industries (finance, healthcare) demand network-level isolation.
IP Management: Avoid subnet IP exhaustion in large clusters.
If you're building an enterprise-scale or regulated environment, use infra subnets.
🤖 eksctl
vs Terraform: VPC Creation
If you run:
eksctl create cluster
...from a Jenkins server in a default VPC, eksctl will create a new VPC by default unless you specify --vpc-id
.
Even if Jenkins and EKS live in different VPCs, Jenkins can still communicate with the EKS API if:
The EKS endpoint is public.
Jenkins has internet access.
You’ve set
aws eks update-kubeconfig
.
🌐 CoreDNS — The Invisible Glue in Kubernetes
CoreDNS is Kubernetes’ internal DNS service. It lets pods and services resolve names like:
my-service.default.svc.cluster.local
Without CoreDNS:
Pods couldn’t find each other by name.
Services wouldn’t be discoverable.
Autoscaling and service meshes would break.
Even if you define services (e.g., LoadBalancer or ClusterIP), CoreDNS is what makes name resolution work.
🌉 vpc.tf
— NAT & VPN Gateways
Two crucial flags:
enable_nat_gateway = true
→ Allows private subnets to reach the internet (e.g., pull images from GitHub).enable_vpn_gateway = true
→ Enables secure communication with on-prem environments over a VPN.
If your tools like ArgoCD are in private subnets, they need a NAT Gateway to access external services like GitHub.
🧩 Final Thoughts
Setting up EKS with Terraform is powerful but requires attention to networking, access control, and best practices like using private and infra subnets. Whether you're deploying for a startup or a regulated enterprise, these patterns will keep your infrastructure scalable, secure, and compliant.
💬 Got Questions?
Leave a comment or ping me on LinkedIn!
Let’s keep the DevOps conversation rolling. 🚀
My LinkedIn Profile - https://www.linkedin.com/in/binereet-singh-9a7685316/
Subscribe to my newsletter
Read articles from BinereetDevops directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
