DEV Community

Rita Gitamo
Rita Gitamo

Posted on

Deploying Spring PetClinic Microservices Locally with Docker Compose

There is something different about deploying an application yourself, from scratch, on your own machine. Our team had already deployed Spring PetClinic on AWS EKS as part of our DMI capstone project. But when I sat down to run the same application locally using Docker Compose, I learned things the cloud deployment never taught me.
What Is Spring PetClinic?
Spring PetClinic is a veterinary clinic management application built as 8 independent Spring Boot microservices. It covers everything from managing pet owners and their animals to scheduling vet visits and browsing veterinarian profiles. What makes it interesting as a learning project is not what it does, but how it is structured — 8 services that each own a piece of the system, all needing to work together seamlessly.

Prerequisites
Before running anything, make sure you have the following installed:

Docker
Git
A terminal — I used Ubuntu on WSL2

Getting Started
bash

git clone https://github.com/gitamoo/spring-petclinic-microservices.git
cd spring-petclinic-microservices
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

That single command spins up the entire stack:

Config Server and Eureka Discovery Server — the foundation
API Gateway, Customers, Visits, Vets, and GenAI services — the application layer
Spring Boot Admin, Prometheus, Grafana, and Zipkin — the full observability stack

The Startup Order
The first thing that stood out was watching the startup sequence play out. Config Server boots first and has to reach a healthy state before anything else moves. Discovery Server follows. Only once both are healthy do the remaining services start.
This is enforced in docker-compose.yml using depends_on with health checks. It is not just a convention — it is a hard requirement. Every service fetches its configuration from Config Server the moment it starts. Without it, they fail immediately. Then each service registers itself in Eureka so other services can find it by name. Without Discovery Server, the whole routing layer breaks.
Watching this happen in real time made the concept of distributed service dependencies feel very concrete.

The Zipkin Problem
Once everything was running I opened Zipkin at http://localhost:9411, ran a query, and got nothing. No traces, no errors — just an empty screen.
The container was healthy. The UI loaded fine. But nothing was showing up.
After investigating I found the root cause: the individual services had no configuration telling them where to send their trace data. Zipkin was running but completely isolated — none of the application services knew it existed.
The fix was adding two environment variables to each service in docker-compose.yml:
yamlenvironment:

- MANAGEMENT_ZIPKIN_TRACING_ENDPOINT=http://tracing-server:9411/api/v2/spans
  - MANAGEMENT_TRACING_SAMPLING_PROBABILITY=1.0
Enter fullscreen mode Exit fullscreen mode

Once I brought the stack back down and up again with those variables in place, the traces started flowing immediately. I could see requests moving through api-gateway → customers-service in real time.
That experience taught me something I will carry into every deployment going forward: a container being healthy and a container being correctly wired up to the rest of the system are two completely different things. Health checks tell you the process is running. They do not tell you it is connected.

Verifying Everything Works
Once the stack was up I verified each service:

Service                    URL
Spring PetClinic App       http://localhost:8080
Eureka Dashboard           http://localhost:8761
Spring Boot Admin          http://localhost:9090
Zipkin                     http://localhost:9411
Prometheus                 http://localhost:9091
Grafana                    http://localhost:3030
Enter fullscreen mode Exit fullscreen mode

Everything worked as expected. The only service that did not function fully was the GenAI chatbot, which requires a paid OpenAI API key. The service starts and registers with Eureka cleanly — it just cannot respond to AI requests without the key configured.
Cleaning Up
bash

docker compose down
Enter fullscreen mode Exit fullscreen mode

This stops and removes all containers and the network, freeing up all ports and memory.
Final Thoughts
Running this application locally gave me a ground-level understanding of how microservices behave that the cloud deployment, with all its automation, had abstracted away. The startup order, the service wiring, the observability configuration — these are things you only truly understand when you have to get them right yourself.
This deployment was part of the DMI DevOps Micro-Internship. If you want hands-on experience like this, DMI Cohort 3 starts 27 June 👇

https://docs.google.com/forms/d/e/1FAIpQLSel7ai7nyb0P1qLW4vEyfB_nEsD4lUF1XG88vmAaFGBOb6hPA/viewform

Top comments (0)