# From docker compose up -d to AWS EKS: Building a Repeatable Spring Petclinic Deployment with Terraform, Helm, ArgoCD and Claude

> Source: <https://dev.to/angela_bfc238/from-docker-compose-up-d-to-aws-eks-building-a-repeatable-spring-petclinic-deployment-with-424c>
> Published: 2026-10-15 09:06:04+00:00

What started with a simple command—

`docker compose up -d`

—grew into a project involving eight microservices, observability, Infrastructure as Code, Kubernetes, AWS EKS, Helm, ArgoCD, CI/CD, and AI-assisted engineering.

I started this project as part of my DMI capstone. It became one of the central projects in my DevOps portfolio because it gave me the opportunity to work through the journey from a local microservices deployment to an automated cloud deployment on AWS.

More importantly, I learned that getting an application running is only part of the job. Understanding its behavior, troubleshooting failures, automating deployments, and verifying the results are equally important.

**From Local Microservices to Observability;**

Spring Petclinic is a microservices-based application made up of independent services that work together. For this deployment, I worked with eight application services:

I also deployed the following observability components:

My goal wasn't simply to get the application running. I wanted to understand what was happening inside it.

*Gateway communicating with the application services, with Config and Discovery Server supporting them and Prometheus, Grafana, and Zipkin providing observability.*

**Running Spring Petclinic Locally**

I started locally because Docker Compose provided a controlled environment for understanding the application before moving to AWS.

`docker compose up -d`

docker compose ps

Once the containers were running, I accessed Petclinic through the API Gateway and checked Eureka to confirm that the services were registering correctly.

*All eight microservices running with their port mappings.*

*Eureka showing the registered services*

**Observability: Metrics Collection and Distributed Tracing**

Prometheus collected application metrics while Grafana provided dashboards for visualizing and interpreting them.

The initial observability setup was relatively straightforward, but I encountered an issue with Zipkin;

The Zipkin container was running, yet no traces were appearing. After checking the logs and configuration, I discovered that the services were not properly configured to send tracing data to Zipkin.

I corrected the relevant configuration, restarted the services, and generated new requests to verify the result.

*A trace showing a request flowing across multiple Petclinic services.*

That taught me an important lesson:

Having an observability tool running is not the same as having observability. The application must actually send useful data to it.

The experience also helped me understand three basic concepts:

**TAKING THE APPLICATION TO AWS**

Once the local deployment was working, I wanted to go beyond Docker Compose and deploy the application to Amazon EKS.

The cloud deployment involved:

**Terraform · EKS · ECR · Kubernetes · Helm · ArgoCD · AWS Load Balancer Controller · IAM · GitOps**

I didn't want to rely on a long sequence of commands that I would have to remember and execute manually every time. I wanted a deployment process that was easier to manage, repeat, and tear down.

One Script to Set Up the Environment

With help from Claude, I developed a deployment script, `deploy.sh`, to coordinate the major setup steps.

The process looks like this:

deploy.sh

    ↓

AWS credential check

    ↓

Terraform infrastructure provisioning

    ↓

EKS and kubectl configuration

    ↓

ArgoCD installation

    ↓

AWS Load Balancer Controller installation

    ↓

ArgoCD project and application manifests

    ↓

Trigger initial CI pipeline for image build

    ↓

ArgoCD synchronizes deployment configuration

The important distinction is that the script sets up the environment and triggers the deployment workflow. The application build, image publishing, and Helm image-tag updates are handled by GitHub Actions, while ArgoCD manages deployment synchronization.

**DEPLOYMENT STAGES;**

**1. Terraform + EKS + ECR**

Terraform handled infrastructure provisioning:

`terraform init -input=false`

terraform apply -auto-approve

Terraform uses configuration files to provision the infrastructure required for the EKS environment, including supporting AWS resources and ECR repositories.

*Terraform provisioning the infrastructure and supporting resources*

Amazon ECR provided the container image registry. The deployment script obtained the AWS account ID dynamically and replaced the `YOUR_ACCOUNT_ID` placeholder in the relevant Helm values files.

This allowed the configuration to reference the ECR image locations for the AWS account being used.

*AWS Console showing the petclinic-prod EKS cluster running in us-east-1*.

**GitHub Actions: Building and Publishing the Images**

The GitHub Actions workflow, `ci.yml`, automates testing and image publishing.

Here is how it works:

**1. Pull requests to main:** The workflow runs the tests. It does not build and publish the application images for a pull request.

**2. Pushes to main:** The workflow runs the tests and, if they pass, builds eight service images in parallel.

**3. Publish to ECR:** The workflow pushes each image using both the Git commit SHA and the latest tag.

**4. Update Helm values:** After all eight image builds and pushes succeed, another job updates the image tag in the eight Helm values files to the commit SHA.

**5. Commit the updates:** GitHub Actions commits and pushes the updated Helm files. The commit includes [skip ci] to prevent the image-tag update from triggering another CI run.

The commit SHA is useful because it identifies the specific version of the code associated with each image. It also makes it easier to see which version the Helm configuration is requesting.

The workflow uses GitHub Actions OIDC to obtain AWS credentials through an IAM role rather than storing long-lived AWS access keys in GitHub Secrets. This reduces the need to manage permanent AWS credentials in the repository's CI/CD settings.

**How the Deployment Pieces Work Together**

The overall workflow can be understood in two parts.

**Part 1: Environment setup**

deploy.sh → Terraform → EKS and supporting AWS resources → ArgoCD and AWS Load Balancer Controller → ArgoCD application manifests → CI trigger.

**Part 2: Application delivery**

Git push to main → tests → build eight images → push images to ECR → update Helm image tags → ArgoCD syncs the desired configuration → Kubernetes runs the application.

This distinction is important because the deployment script does not itself build all eight images or directly deploy every service. It coordinates the setup and starts the workflow that handles those steps.

**2. Helm, ArgoCD and GitOps**

With EKS ready, I used Helm to package and configure the Kubernetes deployments instead of maintaining completely separate manifests for every service or environment.

ArgoCD provided the GitOps layer, managing the applications from Git and reconciling the cluster with the desired configuration.In simple terms, Git stores the desired deployment configuration, and ArgoCD checks that configuration and works to keep the Kubernetes cluster aligned with it.

The relationship looks like this:

Git repository → ArgoCD → Helm configuration → Kubernetes resources → running application.

*ArgoCD showing the Petclinic applications as Synced and Healthy*

This helped me understand how GitOps can reduce manual deployment work by allowing the cluster to reconcile its state with the configuration stored in Git.

**3. Making the Application Accessible**

The final part was exposing the application outside the Kubernetes cluster.

I installed the AWS Load Balancer Controller. This component watches Kubernetes resources such as Ingress and manages the AWS load balancers required by their configuration.

The controller is not a step that application traffic passes through. Instead, it manages the load balancer and its routing configuration.

The application request path is:

Internet / Browser → AWS Application Load Balancer → Kubernetes Ingress routing → API Gateway Service → Petclinic microservice

*AWS Console showing the provisioned Application Load Balancer*

I used the deployment script to coordinate the setup:

`bash scripts/deploy.sh`

The script provisions the infrastructure, installs the required deployment components, applies the ArgoCD application configuration, and triggers the CI workflow. After the workflow and application deployment completed, I verified access through the load balancer URL.

*Spring Petclinic loading through the Application Load Balancer*

This was an important milestone: I had moved from running the application locally to accessing it through an AWS-hosted Kubernetes environment.

The deployment script also included a teardown process. With:

`bash teardown.sh`

I could run the teardown workflow to remove the environment and destroy the resources managed by the relevant configuration. This helped reduce the time spent on manual cleanup and avoid unnecessary cloud costs. After teardown, I still needed to verify that the intended resources had been removed.

**Testing Idempotency and Repeatability**

Automation should not only work once. It should also behave predictably when run again. One important part of this project was testing whether the deployment process could be safely repeated.

I tested the deployment workflow in two ways.

*Terraform showing `No changes`. Infrastructure matches the configuration*

These tests addressed two related but different goals:

**Idempotency:** Repeating an operation against an existing environment should not introduce unintended changes or duplicate resources.

**Repeatability:** The deployment process should be capable of recreating the intended environment under the required conditions/process.

I also tested how repeated runs behaved because part of the deployment script creates an empty Git commit to trigger the CI/CD workflow. Since this can start another pipeline run, I needed to consider not only whether Terraform reported infrastructure changes2, but also whether that run produces unintended changes or safely maintains the desired application state. My repeated tests helped me evaluate that behavior.

The experience reinforced the importance of validating automation through observable results rather than assuming that a successful script execution guarantees the desired outcome.

**Claude as Part of the Engineering Workflow**

One of the most interesting parts of this project was incorporating Claude into the engineering process.

I used Claude as an AI engineering assistant for understanding requirements, reasoning through architecture, working through configuration, troubleshooting errors, and refining the deployment workflow.

Claude helped me explore how the different layers connected, but I remained responsible for the decisions, execution, testing, and verification.

*A Claude conversation showing assistance with the Helm configuration*

Another advantage was iteration speed. Instead of manually piecing together every configuration across Terraform, EKS, ECR, Helm, ArgoCD, the Load Balancer Controller, and CI/CD, I could provide the relevant error or configuration, ask Claude to explain the underlying issue, evaluate the proposed solution, and test it.

This helped me turn a collection of individual setup steps into a more repeatable deployment process. However, Claude's suggestions were not treated as proof that something worked. The configurations and changes still had to be tested in the actual environment.

**Things That Didn't Go According to Plan**

The deployment wasn't completely smooth, and some of the most valuable learning came from troubleshooting.

**Docker:** Existing containers caused name conflicts when restarting Compose.

**Zipkin:** The container was running, but traces weren't appearing because the services were not correctly configured to send tracing data.

**Kubernetes:** Some services initially used `localhost` for communication instead of Kubernetes service names, such as discovery-server.

**AWS/EKS:** Moving from Compose to EKS introduced more components to understand and coordinate, including Terraform, ECR, Helm, ArgoCD, and the AWS Load Balancer Controller.Understanding how they worked together took time.

Working through these issues taught me to investigate the cause rather than simply rerun commands and hope the problem disappeared. Seeing the application become accessible after troubleshooting made the learning process especially rewarding.

**What This Project Taught Me**

The biggest lesson wasn't learning another list of DevOps tools. It was understanding how the different layers work/fit together.

**- Docker Compose** helped me develop and validate the microservices locally.

**- Prometheus, Grafana, and Zipkin** helped me investigate application behavior through metrics, dashboards, and distributed traces.

**- Terraform** enabled infrastructure provisioning through code.

**- EKS and Kubernetes** provided the orchestration platform.

**- ECR** stored the container images.

**- Helm** helped manage Kubernetes application configurations.

**- GitHub Actions** automated testing, image building, publishing, and image-tag updates.

**- ArgoCD** managed GitOps-based deployment synchronization.

**- Deployment and teardown scripts** made the process easier to repeat and clean up.

**- Claude** helped me investigate errors, explore implementation options, and refine the automation, while I remained responsible for testing and validating the results.

The most valuable lessons came when things didn't work as expected. Missing traces, service-discovery issues, and cloud deployment challenges pushed me to investigate beyond the immediate error and understand the underlying system.

What began as a local Spring Petclinic deployment became a practical exercise in cloud-native deployment, observability, Infrastructure as Code, CI/CD, GitOps, and AI-assisted engineering.

**A deployment that works teaches you how to deploy. A deployment that breaks teaches you how to think like an engineer.**

I'm grateful to Pravin Mishra for providing the DMI learning platform, my co-mentors, and my teammates for their support throughout DMI Cohort 2.

🔗 Project Resources

Project repository: [https://github.com/Crystal-Angie/spring-petclinic-ai-devops.git](https://github.com/Crystal-Angie/spring-petclinic-ai-devops.git) 

Interested in DMI Cohort 4? Join the waiting list:

[https://forms.gle/t8AnbtieRmGAiFHw5](https://forms.gle/t8AnbtieRmGAiFHw5)
