DEV Community

Cover image for My Kubernetes App Moved to EKS Unchanged. Everything Around It Didn't.
Lalit Bagga
Lalit Bagga

Posted on Originally published at blog.lalitbagga.com

My Kubernetes App Moved to EKS Unchanged. Everything Around It Didn't.

My Kubernetes application moved from my home server to Amazon EKS without an application code change.

The platform around it did not.

The EKS control plane took 5 minutes 51 seconds to create. Once the delivery path was working, Argo CD deployed two healthy application replicas and corrected a manual scaling change in 5 seconds.

But reaching that result exposed three assumptions about networking, worker size and AWS access that did not exist in my home lab.

That was the test I wanted.

Why I Returned to EKS

I started this series because I wanted an always on Kubernetes environment without an always on EKS bill.

My Debian server and k3s gave me an inexpensive place to learn every day. I then built a pull based delivery path: CI tested and published an immutable image, Git recorded the approved image digest, and Argo CD made the cluster follow Git.

The home lab proved that the design worked on k3s. It did not prove that I could move the same workload to a managed cloud cluster.

So I created a temporary EKS environment for one question:

Could I keep the application and delivery contract while changing the Kubernetes platform underneath them?

This was a portability test, not a production EKS design.

What I Kept the Same

I reused the important parts of the existing project:

  • the same Node.js application
  • the same container image, selected by an immutable digest
  • the same Helm chart
  • the same Git repository as the deployment record
  • the same pull-based Argo CD model
  • the same health, readiness and version endpoints.

The application CI still had no Kubernetes credentials. It could test code, build an image and propose a digest change in Git, but it could not deploy directly to either cluster.

The delivery path remained:

CI tests and publishes the image
  -> Git records the approved digest
  -> Argo CD reads Git from inside the cluster
  -> Kubernetes runs the declared release
Enter fullscreen mode Exit fullscreen mode

I made one portable extension to the Helm chart. The home environment could keep a private ClusterIP Service, while the EKS values could request an AWS load balancer. The chart stayed shared, the environment-specific choice stayed in a small values file.

The Smallest Useful AWS Test

I used Terraform to create only what the experiment needed:

  • one EKS 1.36 cluster
  • one managed AMD64 worker node
  • two public subnets in separate Availability Zones
  • private control plane access for the worker
  • a public control plane endpoint restricted to my current IP
  • the standard EKS networking add-ons
  • AWS Load Balancer Controller
  • Argo CD Core with no public user interface; and
  • one temporary Network Load Balancer for the application.

One worker reduced the cost, but it did not provide high availability. If that node failed, the workload had nowhere else to run. That limitation was acceptable for a short portability experiment.

The Worker Could Not Join

The EKS control plane became active, but the worker did not join the cluster.

I had restricted the public API to my computer and disabled private access. That protected the public endpoint, but it also left the worker without a path to the control plane.

I enabled the private endpoint for communication inside AWS while keeping public administrative access restricted.

The application had moved unchanged. Its network paths still required an AWS specific decision.

AWS Rejected My Planned Worker

I planned to use a t3.medium worker. AWS rejected that instance type for the account's eligible configuration.

Instead of choosing a larger machine at random, I queried the eligible AMD64 choices and selected one c7i-flex.large worker with 2 vCPUs and 4 GiB of memory.

This changed the infrastructure, not the application. The same container and Helm release still ran after the worker choice changed.

That is a useful portability boundary: Kubernetes can preserve the workload contract, but the cloud provider still controls which underlying compute choices are available.

The Access Problem Was Operational, Not Kubernetes

The long-running AWS operation also exposed an authentication problem. My interactive login stopped refreshing reliably while I was creating and checking the cluster.

For this temporary lab, I created a temporary role for the remaining setup and teardown work, then removed that access during cleanup.

The important lesson was not to weaken authentication. It was to treat operator access as part of the platform design. A repeatable EKS workflow needs a stable, non-personal AWS role with only the permissions the workflow requires.

Git Deployed the Workload

After the cluster components were ready, I merged the EKS configuration through a pull request. The CI checks completed successfully in 13 seconds.

Argo CD, running privately inside EKS, then read the merged Git state and applied the Helm release. CI never received a kubeconfig and never called the cluster.

The final checks showed:

  • Argo CD was Synced and Healthy
  • both application replicas were ready
  • the running image matched the immutable digest in Git
  • /health returned a healthy response
  • /version returned the expected application version and
  • Argo CD had no public Service or user interface.

The same application and delivery decision had crossed from k3s to EKS. Git remained the durable record on both platforms.

I Changed the Cluster Behind Git's Back

I then repeated the simplest self-healing test from my home lab.

Git declared two application replicas. I manually scaled the live EKS Deployment down to one.

Argo CD detected that the cluster no longer matched Git and restored the second replica in 5 seconds.

On k3s, the same type of test had taken 42 seconds. This single observation does not prove that EKS is generally faster. Reconciliation timing depends on polling, refresh timing and cluster conditions. It proves only that this EKS run corrected this specific drift in 5 seconds.

The Cost Test

I also treated cost as an engineering result rather than an afterthought.

  • EKS control-plane creation: 5 minutes 51 seconds
  • Estimated environment cost: approximately US$0.24-US$0.27 per hour
  • Final charge: to be replaced with the AWS billing result when it becomes available

The estimate covered the temporary control plane, worker, storage, public IPv4 use and load balancer. I kept it as an estimate because billing data had not appeared when I prepared the first draft.

Teardown Was Part of the Test

A temporary cloud environment is not finished when the application works. It is finished when its chargeable resources are gone.

I removed the application load balancer first, destroyed the Terraform-managed infrastructure and removed the temporary access used for the experiment. The teardown script completed successfully.

I deliberately kept identifiers, cluster endpoints, credentials, IP addresses and kubeconfig data out of the repository and article evidence.

What Was Actually Portable

The test gave me a more precise answer than “Kubernetes is portable.”

The application code, container contract, immutable image, Helm release structure and GitOps delivery model moved cleanly.

The cloud platform still required AWS-specific engineering:

  • worker-to-control-plane networking
  • available instance selection
  • operator and controller identity
  • load balancer integration and
  • cost-aware teardown.

Kubernetes made the workload portable. It did not make every platform decision portable.

That is the boundary I wanted to find. My home k3s server remains the practical always on learning environment. EKS becomes a short-lived validation target when I need to prove that the platform contract still works in AWS.

Top comments (1)

Collapse
 
raknaos profile image
Baptiste Le Bouquin

That contrast is the honest version of an EKS write-up: 5m51s for the control plane, 5s for Argo to correct a manual scale. The automation is the easy part — the assumptions around it are the actual migration.

Curious which of your three burned hardest. Ours was networking: the flat home-lab address space vs. VPC/subnet reality quietly breaking things the app code never touched. Which assumption broke first for you?