AWS Builder Center

Building a Highly Available Amazon EKS Cluster with Terraform: A Step-by-Step Guide

Learn how to provision an Amazon EKS environment using Terraform, including the VPC, public and private subnets, IAM roles, managed node groups, and Kubernetes workloads. This hands-on guide also covers deployment validation, high availability, and application rollback.

DevOps Engineer | AWS | Kubernetes | React | Cloud & Kubernetes
I wanted to understand what it actually takes to run a containerized application on Amazon EKS, starting from the infrastructure layer instead of creating resources manually from the AWS Console.
For this project, I used Terraform to provision the AWS infrastructure and then used Kubernetes to deploy a Python FastAPI application on the EKS cluster.
The main objective was not just to create an EKS cluster, but to understand how the different pieces fit together — networking, IAM, EKS, worker nodes, containers, Kubernetes workloads, high availability, and application rollback.

The Approach

I divided the work into two parts.
The first part was the AWS infrastructure. Terraform was used to create the VPC, subnets, networking components, IAM roles, EKS cluster, and managed node group.
The second part was the application layer. After the cluster was ready, I connected to it using kubectl and deployed a containerized FastAPI application using Kubernetes manifests.
The flow was:
1
2
3
4
5
6
7
8
9
10
11
12
13
Terraform
↓
AWS VPC
↓
Subnets + Networking
↓
Amazon EKS
↓
Managed Node Group
↓
Kubernetes
↓
FastAPI Application
This separation helped me understand an important distinction:
Terraform provisions the infrastructure, while Kubernetes manages the applications running on that infrastructure.

Starting with the AWS Network

Before creating EKS, I started with the VPC.
An EKS environment depends heavily on networking, so I wanted to understand this part instead of treating the VPC as something that is automatically created in the background.
The VPC was designed across multiple Availability Zones with public and private subnets.
The private subnets are used for the worker nodes, while the public subnets provide the networking components required for internet-facing connectivity.
The basic layout was:
1
2
3
4
5
6
7
8
9
10
11
VPC
|
+------------+------------+
| |
Availability Zone A Availability Zone B
| |
Public Subnet Public Subnet
| |
Private Subnet Private Subnet
| |
EKS Nodes EKS Nodes
This also helped me understand why worker nodes don't necessarily need to be directly exposed to the public internet.

Creating the Infrastructure with Terraform

Once the networking design was clear, I moved the infrastructure into Terraform.
I kept the Terraform configuration separated into multiple files rather than putting everything into a single file.
1
2
3
4
5
6
7
8
9
terraform/
├── provider.tf
├── versions.tf
├── variables.tf
├── vpc.tf
├── iam.tf
├── eks.tf
├── outputs.tf
└── terraform.tfvars
Before creating anything, I used Terraform's validation and planning steps:
1
2
3
4
terraform init
terraform fmt
terraform validate
terraform plan
terraform plan was particularly useful because it allowed me to review what Terraform intended to create before actually provisioning the resources.
After reviewing the plan:
1
terraform apply
Once the deployment completed, the AWS resources were available for the next stage.

Creating and Accessing the EKS Cluster

After the EKS cluster was created, I configured my local kubectl client to communicate with it.
1
2
3
aws eks update-kubeconfig \
--region <region> \
--name <cluster-name>
I then checked the worker nodes:
1
kubectl get nodes
Seeing the nodes in the Ready state was an important checkpoint because it confirmed that the Kubernetes cluster was ready to schedule workloads.

Deploying the Application

For the application, I used Python with FastAPI.
The application was packaged as a Docker image and then deployed to Kubernetes.
The Kubernetes Deployment was configured with multiple replicas rather than running a single pod.
For example:
1
2
spec:
replicas: 3
This gave me an opportunity to test how Kubernetes behaves when multiple instances of the same application are running.
I could check the pods with:
1
kubectl get pods -o wide
and see where the application instances were scheduled.

Testing Kubernetes Self-Healing

One of the things I wanted to test was what happens when an application pod disappears.
Instead of assuming that Kubernetes provides self-healing, I tested it.
After deleting one of the running pods:
1
kubectl delete pod <pod-name>
I checked the pods again:
1
kubectl get pods
Kubernetes created a replacement pod because the Deployment's desired state was still three replicas.
This was a simple experiment, but it made the concept of Kubernetes reconciliation much clearer.

Rolling Updates and Rollback

The next part was testing application updates.
A Kubernetes Deployment allows us to update the application without immediately taking all existing replicas offline.
I monitored the update using:
1
kubectl rollout status deployment/fastapi-app
I also checked the deployment history:
1
kubectl rollout history deployment/fastapi-app
To test the rollback capability, I intentionally moved to a newer application version and then reverted the Deployment:
1
kubectl rollout undo deployment/fastapi-app
This was one of the more useful parts of the exercise because it demonstrated how Kubernetes can help manage application releases rather than simply running containers.

What I Learned

Working through the deployment from the infrastructure layer to the application layer helped connect several concepts that can otherwise feel separate when learning AWS and Kubernetes.
A few key takeaways were:
  • EKS is not just an EC2 replacement; it provides a managed Kubernetes control plane.
  • Networking needs to be designed before thinking about the application workload.
  • Terraform makes the infrastructure repeatable and easier to track through code.
  • Kubernetes Deployments maintain the desired number of application replicas.
  • Multiple replicas improve application availability, but the overall architecture still needs to consider Availability Zones and node distribution.
  • Kubernetes can automatically replace failed pods.
  • Deployment history and rollback provide a safer way to handle application updates.

Final Thoughts

This project started as an exercise to understand EKS and Terraform, but working through the complete flow made the relationship between AWS infrastructure and Kubernetes much clearer.
The most useful part was not simply getting the application to run. It was being able to trace the complete path:
1
2
3
4
5
6
7
8
9
10
11
12
13
Infrastructure
↓
Networking
↓
EKS
↓
Worker Nodes
↓
Containers
↓
Kubernetes Deployment
↓
Application
There is still a lot more to explore, including autoscaling, observability, ingress, load balancing, secrets management, and CI/CD. But this provided a solid foundation for understanding how a containerized application can be deployed and managed on Amazon EKS using infrastructure as code.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article