← Projects Terraform · EKS · GitOps

2048 app on Amazon EKS

Built an Amazon EKS platform around a containerised 2048 application, with Terraform infrastructure, GitOps delivery, HTTPS, monitoring and security scanning.

TerraformAmazon EKSArgoCDGitHub ActionsECRPrometheusGrafana
01 — Overview

What I built.

The goal of the project was to deploy a containerised 2048 game application on Amazon EKS. The infrastructure was provisioned with Terraform and application deployments were managed through ArgoCD using a GitOps approach. CI/CD pipelines automated security scans, Docker image builds and application release updates, while ArgoCD handled deployment to EKS. Dynamic DNS and SSL/TLS certificate management were configured for the application endpoint. Prometheus and Grafana were also configured to provide real-time visibility into application performance and infrastructure health.

2Availability Zones
3EKS worker nodes
GitOpsArgoCD reconciliation
HTTPSCloudflare DNS + ACM
02 — Architecture

How the platform fits together.

Architecture & deployment flow

Application architecture

Switch between the live request path and the GitOps delivery path.

  1. 01A custom VPC spans two Availability Zones with public and private subnets. EKS worker nodes run in the private subnets while the internet-facing ALB sits in the public networking layer.
  2. 02Amazon ECR stores immutable application images. Each release is tagged with the Git commit SHA so the running version can be traced back to source.
  3. 03Cloudflare manages the DNS zone, ExternalDNS manages the application DNS record, and AWS Certificate Manager provides TLS for 2048.nabilenv.com.
  4. 04ArgoCD manages the application, AWS Load Balancer Controller, ExternalDNS and monitoring stack using Git as the source of truth.
  5. 05Prometheus and Grafana provide cluster and workload visibility, with Alertmanager, node metrics and Kubernetes state metrics included through the monitoring stack.
Architecture evolution

How cluster capacity changed.

Monitoring exposed a pod-density constraint on the original worker capacity, so the node group was scaled and the Terraform desired state was updated to match.

Initial state

2 × t3.small workers running the application and core platform components.

Constraint found

Adding kube-prometheus-stack produced Too many pods scheduling failures.

Final state

3 × t3.small workers, with Terraform aligned to desired_size = 3.

03 — Implementation

How it was implemented.

  1. 01Built reusable Terraform modules for the VPC, EKS cluster, ECR and IAM, then composed them through a development environment in eu-west-2.
  2. 02Containerised the 2048 application and created Kubernetes Deployment, ClusterIP Service and Ingress manifests with explicit CPU and memory requests and limits.
  3. 03Configured GitHub Actions to authenticate to AWS using OIDC. Terraform pull requests run validation, Checkov and planning; changes reaching main can apply the infrastructure.
  4. 04Created a separate application release workflow that builds the image, scans it with Trivy, pushes it to ECR, updates the Kubernetes image tag in Git and leaves deployment to ArgoCD.
  5. 05Enabled ArgoCD automated sync, pruning and self-healing so manual drift in the cluster is corrected back to the state stored in Git.
  6. 06Deployed kube-prometheus-stack through ArgoCD and used Grafana dashboards to inspect cluster CPU, memory, workloads, networking, kubelet and node metrics.
terraform · eks · argocd
Engineering decisions

Why the platform was designed this way.

Expand a decision to see the reasoning and trade-off behind it.

DecisionUse ArgoCD to deploy Kubernetes application changes.

ReasonGit stores the desired state while automated sync, pruning and self-healing reconcile the cluster.

Trade-offArgoCD becomes another platform component that must remain healthy and correctly configured.

DecisionAuthenticate GitHub Actions to AWS through OIDC.

ReasonThe workflows can assume scoped IAM roles without storing long-lived AWS credentials in GitHub.

Trade-offThe IAM trust relationship and permissions need to be defined carefully for each workflow.

DecisionRun EKS worker nodes in private subnets.

ReasonThe public ALB handles internet-facing traffic while cluster compute remains on the private networking layer.

Trade-offPrivate workers still need a controlled outbound path for dependencies and AWS service access.

DecisionSet region and VPC ID explicitly in the controller Helm values.

ReasonThe controller was restarting when automatic region/VPC discovery through instance metadata failed.

Trade-offThe deployment values now contain environment-specific settings that must stay aligned with Terraform.

Engineering evidence

Inspect the implementation.

The drawer stays closed by default and exposes the repository structure, core commands and release path when needed.

2048-eks-platform/
├── .github/workflows/
│   ├── app-release.yml
│   └── terraform.yml
├── terraform/
│   ├── bootstrap/
│   ├── environments/
│   └── modules/
├── kubernetes/
│   ├── app/
│   ├── argocd/
│   ├── aws-load-balancer-controller/
│   ├── external-dns/
│   └── monitoring/
└── app/
$ terraform plan
$ kubectl get nodes
$ kubectl get pods -A
$ argocd app list
Git push
  → GitHub Actions
  → Trivy scan + image build
  → Amazon ECR
  → Kubernetes image tag updated in Git
  → ArgoCD reconciliation
  → Amazon EKS
04 — Troubleshooting

Issues resolved.

01Node registration

Worker nodes failed to join the cluster

ProblemThe EKS control plane was running, but the managed node group kept failing and no healthy worker nodes appeared in Kubernetes. Checking the node group, kube-system pods, private-subnet routing and IAM showed that worker bootstrap depended on the VPC CNI being available early enough.

FixI made the core EKS add-ons explicit in Terraform and configured vpc-cni with before_compute = true. After applying the change, the aws-node pods came up and kubectl get nodes showed the workers in Ready state.

02VPC detection

Load Balancer Controller kept restarting

ProblemAfter installing the AWS Load Balancer Controller, the pod went into a crash/restart loop. Its logs showed that it was failing while trying to discover the AWS region and VPC through instance metadata, so it could not provision an Application Load Balancer for the Ingress.

FixI stopped relying on automatic discovery and set region: eu-west-2 and the VPC ID explicitly in the controller Helm values. I kept those values in the version-controlled values.yml used by ArgoCD. The controller then stayed running and provisioned the ALB for the 2048 Ingress.

03Cluster capacity

Monitoring pods were stuck Pending

ProblemAfter I added kube-prometheus-stack, several monitoring pods would not schedule. kubectl describe pod reported Too many pods. The existing t3.small workers had run out of available pod slots, even though CPU and memory were not the main limit.

FixI scaled the managed node group to three workers and aligned Terraform to desired_size = 3. Once the third node became Ready, the previously Pending monitoring pods scheduled successfully.

Outcome.

The outcome of the EKS project was a production-grade Kubernetes platform on AWS capable of running, exposing, updating and monitoring a containerised application. It gave me practical experience in managing deployment automation, platform reliability, security controls and observability within a more complex environment. It also helped me gain a deeper insight into the underlying issues around networking, IAM, cluster capacity and Kubernetes itself. Overall, the project strengthened my understanding of how Kubernetes environments are designed, operated and maintained.