AWS EKS Cluster Setup: Provisioning Kubernetes with eksctl and Terraform (Part 1)
EKS-KloudGov-Cluster
ByOlaniyi Oladimeji
AWS EKS Cluster Setup: Provisioning Kubernetes with eksctl and Terraform (Part 1)

KloudGov has run on EC2 with Ansible automation, and on ECS with Fargate. This new series adds a third architecture: Kubernetes, via Amazon EKS (Elastic Kubernetes Service), fronted by an ALB Ingress Controller, a custom Route 53 domain, and SSL through AWS Certificate Manager.

This is a four-part series:

Part 1 (this post) provisions a managed EKS cluster and the CLI tooling needed to operate it.
Part 2 configures an Ingress Controller backed by an Application Load Balancer.
Part 3 deploys the KloudGov application onto the Cluster as Kubernetes workloads.
Part 4 exposes it securely: Ingress rules, a custom domain per state, and SSL via ACM.

Why Add Kubernetes to This Project at All

It’s worth being honest about why this matters, given KloudGov already runs successfully on two other architectures. ECS/Fargate is AWS-specific; it only runs on AWS, using AWS’s own orchestration. Kubernetes is the industry-standard orchestration layer, portable across AWS, GCP, Azure, or on-premises, and it’s what a huge share of real-world platform teams actually run in production. Learning to provision and operate EKS specifically not Kubernetes in the abstract, but the exact managed AWS flavor of it is a genuinely different skill set from either of the first two KloudGov architectures, even though the application being deployed is identical across all three.

Tools This Series Depends On

Three CLI tools need to be installed on the EC2 IDE instance before touching Kubernetes at all:

eksctl, the official CLI for creating and managing EKS clusters. Without it, cluster creation means manually assembling a VPC, subnets, IAM roles, and a control plane through CloudFormation or the console; eksctl collapses that into a single command.
kubectl is the standard Kubernetes CLI, used to interact with any Kubernetes cluster, not just EKS. This is what you’ll use in every subsequent part of this series to deploy, inspect, and manage workloads.
Helm is a package manager for Kubernetes, conceptually similar to apt or yum. Part 2 uses it to install the AWS Load Balancer Controller as a pre-packaged “chart” rather than hand-writing its full set of Kubernetes manifests.

Step 1: Validating the AWS CLI Version

aws --version

Confirm this reports AWS CLI version 2 or higher; version 1 doesn’t support all the commands this series relies on, and most current EC2 AMIs ship with v2 by default, but it’s worth confirming rather than assuming.

Step 2: Installing eksctl

ls /tmp/

# Download and extract the latest version of eksctl to the /tmp directory
curl --silent --location "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp

ls /tmp/

sudo mv /tmp/eksctl /usr/bin

eksctl version

Worth knowing directly: eksctl was originally maintained by Weaveworks, which shut down in 2024. The project is now community-maintained under a new GitHub organization, eksctl-io.

Step 3: Installing kubectl

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x ./kubectl
sudo mv kubectl /usr/local/bin/
kubectl version --client

This installs the current stable release of kubectl when you run it, rather than a version pinned to a specific date. That distinction matters more than it looks: a kubectl binary tied to a hardcoded version and URL pointing at Kubernetes 1.18 from late 2020, for instance, quietly becomes incompatible with any EKS cluster running a modern Kubernetes version, since kubectl only officially supports interacting with clusters within one minor version above or below its own. Fetching the current stable release dynamically avoids that entire class of problem and keeps this command usable indefinitely, rather than requiring a rewrite every time a new Kubernetes version ships.

Step 4: Installing Helm

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

chmod 700 get_helm.sh

ls /usr/local/bin
./get_helm.sh
ls /usr/local/bin

helm version

rm get_helm.sh

This script detects your OS and architecture automatically and installs the latest stable Helm 3 release, with no version-pinning concerns, since the script always points to Helm’s current release channel.

Step 5: Provisioning the Supporting Infrastructure with Terraform

EKS still needs the same underlying storage layer; every other KloudGov architecture has needed an S3 bucket and a DynamoDB table per state. If you’ve worked through the ECS series, the Terraform module is already adapted for a container-based architecture (EC2 instance, security group, and per-instance IAM role all commented out; DynamoDB, S3, and the random string suffix left active); nothing further needs to change here.

cd /home/ec2-user/kloud-gov-infrastructure/terraform
terraform show          # The state file is empty. No resources are represented.
terraform plan
terraform apply -auto-approve

Record the resulting DynamoDB table name and S3 bucket name; these get referenced directly in the Kubernetes deployment manifests in Part 3, the same role they’ve played in every architecture so far.

Step 6: Creating an EKS Cluster via eksctl

eksctl create cluster --name kloudgov-cluster --region us-east-1 --nodegroup-name standard-workers --node-type t3.medium --nodes 1

This single command does an enormous amount of work behind the scenes: provisioning a dedicated VPC (unless you specify an existing one), the EKS control plane itself, IAM roles for both the Cluster and its nodes, and a managed node group a set of EC2 instances that actually run your workloads. Expect this to take 15–20 minutes. Unlike terraform apply, there’s no faster path here; EKS control plane provisioning genuinely takes this long regardless of tooling.

A cost detail worth flagging before you run this: unlike EC2 (free-tier eligible) or Fargate (billed per task, no charge when nothing’s running), the EKS control plane itself has a flat hourly charge currently around $0.10/hour that accrues the entire time the Cluster exists, regardless of whether any workloads are actually running on it. On top of that, the t3.medium worker node from this command is a real, billed EC2 instance. Budget your working sessions accordingly, similar to the Fargate cost warning from the ECS series, and don’t leave this Cluster running between sessions unless you’re actively working through the rest of this series.

Once complete, confirm in the console: EKS → your Cluster → Compute tab → Node groups, and confirm standard-workers shows as active with one node.

Step 7: Connecting kubectl to the Cluster

# Display the current Kubernetes context kubectl is using
kubectl config current-context

# Configure kubectl to connect to the "kloudgov-cluster" EKS cluster
aws eks update-kubeconfig --name kloudgov-cluster

# Display the kubeconfig file kubectl reads
cat /home/ec2-user/.kube/config

# Confirm the context switched correctly
kubectl config current-context

Step 8: Validating Connectivity

kubectl get svc

kubectl get nodes

Expected output for get nodes looks something like:

NAME                            STATUS   ROLES    AGE     VERSION
ip-192-168-0-188.ec2.internal   Ready    <none>   7m27s   v1.32.3-eks-473151a

Then confirm in the EC2 console that an actual EC2 instance now exists, backing this node. This is the worker node eksctl provisioned as part of the managed node group in Step 6.

Worth understanding explicitly: notice there’s no separate “control plane” EC2 instance anywhere in your account. That’s deliberate, and it’s the entire value proposition of a managed Kubernetes service: EKS operates the control plane (the API server, scheduler, etcd, and everything else that makes Kubernetes function) as infrastructure AWS owns and maintains entirely outside your account. You only ever see and pay for the worker nodes running your actual workloads; the control plane exists as a managed service you interact with through the API, never something you SSH into or patch yourself.

Tearing Down (Reference Only; Don’t Run This Yet)

Part 2 depends entirely on the cluster and infrastructure created in this post still existing. For reference, when you eventually finish this whole series:

# Delete the EKS cluster
eksctl delete cluster --name kloudgov-cluster --region us-east-1

# Destroy the Terraform-managed resources
terraform destroy -auto-approve

Key Takeaways

eksctl, kubectl, and helm are three distinct tools with three distinct jobs: cluster lifecycle management, general Kubernetes interaction, and application packaging, respectively, and this series uses all three for different reasons.
Installation commands that hardcode a specific tool version and download URL age poorly; preferring a source’s dynamic “latest stable” release channel keeps setup instructions usable long after they were written.
eksctl create cluster provisions far more than just Kubernetes itself: VPC, IAM roles, control plane, and a managed node group all come from one command, which is exactly why it takes 15–20 minutes.
EKS’s control plane has a flat hourly cost regardless of usage, layered on top of the EC2 cost of whatever worker nodes you run; it’s a genuinely different cost model than either EC2 or Fargate, worth planning sessions around.
The absence of a visible “control plane” EC2 instance in your account is the actual point of a managed Kubernetes service AWS operates that layer entirely outside resources you can see or touch directly.

With the cluster provisioned and kubectl correctly connected, Part 2 installs and configures the AWS Load Balancer Controller, which lets Kubernetes Ingress resources provision and manage a real Application Load Balancer on your behalf.

{{ reviewsTotal }}{{ options.labels.singularReviewCountLabel }}
{{ reviewsTotal }}{{ options.labels.pluralReviewCountLabel }}
{{ options.labels.noReviewsLabel }}
{{ options.labels.newReviewButton }}
{{ userData.canReview.message }}

Related Posts

Docker-and-ECR
Terraform ECS Automation: Fully Automated, HTTPS-Secured Fargate Services (Part 4)
Everything built by hand in Part 3 the task definition, the target group, the ALB,...
Docker-and-ECR
ECS Task Definitions and Fargate Services: Deploying KloudGov (Part 3)
creating the task definition through the console, launching the service, and watching it come up...
Docker-and-ECR
Terraform for ECS: Provisioning S3, DynamoDB, and Fargate Cluster (Part 2)
The interesting part of this post isn't creating new resources from scratch; it's adapting the...