# spring-petclinic-microservices: taking the Petclinic sample apart

> The Spring Cloud edition of Petclinic splits a familiar single application into eight modules with Eureka discovery, a config server, a gateway and chaos testing built in.

**spring-petclinic/spring-petclinic-microservices** — Distributed version of Spring Petclinic built with Spring Cloud

- Repository: https://github.com/spring-petclinic/spring-petclinic-microservices
- Stars: 2,218 · Forks: 2,962
- Language: Java
- License: Apache-2.0
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/spring-petclinic-spring-petclinic-microservices

## A splitting demonstration, derived from an older front end

The README is explicit about what this project is for. It calls this the distributed version of the Spring PetClinic Sample Application, and it says the branch was initially derived from the AngularJS version to demonstrate how to split a sample Spring application into microservices, with a link to Martin Fowler's article on the pattern. That framing matters, because it sets the expectation correctly: this is teaching material with working code, not a system designed to carry load.

Keeping the same domain is what makes the exercise readable. You already know that Petclinic stores owners, vets and visits, so when the repository splits them into three services you can see whether the boundary is drawn in a sensible place without first having to learn an unfamiliar business domain. The same logic explains why the frontend stayed on AngularJS. Rewriting the UI would have added a second variable to a demonstration that is supposed to be about the server side.

The component list in the README names the specific Spring projects doing the work: Spring Cloud Gateway, Spring Cloud Circuit Breaker, Spring Cloud Config, Micrometer Tracing, Resilience4j, Open Telemetry, and the Eureka service discovery from the Spring Cloud Netflix stack. Later sections of the README add the supporting cast, which is what makes this a full platform demo rather than just a split: Zipkin for tracing, Prometheus for metrics, Grafana for dashboards, and Spring Boot Admin for supervision.

There is a newer piece of the stack too. A GenAI Service provides a chatbot interface, and the README gives six concrete prompts it answers, including listing the owners who come to the clinic, checking whether any vets specialize in surgery, and creating a new owner. A service that can write through the same APIs the AngularJS front end calls is a more useful demonstration than a read-only one.

## Eight modules in the tree, seven in the services list

The repository tree holds eight service directories under `spring-petclinic-microservices`: admin-server, api-gateway, config-server, customers-service, discovery-server, genai-service, vets-service and visits-service. Alongside them sit the standard Maven wrapper files, a root `pom.xml`, `docker-compose.yml`, a `docker/` directory, `docs/`, `scripts/` and a `.devcontainer/`.

The README's services overview describes seven of those eight. Customers Service manages customer data, Vets Service handles information about veterinarians, Visits Service manages pet visit records, GenAI Service provides a chatbot interface, API Gateway routes client requests, Config Server handles centralized configuration, and Discovery Server is the Eureka registry. Each is said to have its own specific role and to communicate via REST APIs.

The Admin Server is the one module the overview skips, even though the README gives it its own line in the port table at localhost:9090 and the repository topics name spring-boot-admin. That is a gap in the enumeration rather than a missing component, and it is worth knowing if you go looking through the tree for what the README did not list.

Two infrastructure details in the compose file make the dependency graph explicit. Every one of the application services declares the same two dependencies, and both wait on a health condition rather than mere start:

```yaml
depends_on:
  config-server:
    condition: service_healthy
  discovery-server:
    condition: service_healthy
```

The same ordering rule appears in the prose, where the README asks that supporting services, Config and Discovery Server, be started before any other application. Getting that wrong produces confusing failures rather than a clear one, so it is good that it is stated twice.

## Two run modes, and why the port numbers disagree between them

The README describes two ways to get the system up, and they are not equivalent, which matters because they hand you different port assignments.

The first runs everything as local JVM processes. Every microservice is a Spring Boot application that can be started from an IDE or with the `../mvnw spring-boot:run` command. In this mode the README says Customers, Vets, Visits and GenAI Services come up on a random port, and to check the Eureka Dashboard for which. The fixed ports in this mode belong to the infrastructure: discovery on 8761, config on 8888, the gateway on 8080, Zipkin on 9411, admin on 9090, Grafana on 3030 and Prometheus on 9091.

The second uses Docker Compose, and here the compose file pins the domain services to fixed host ports instead: customers on 8081, visits on 8082 and vets on 8083, alongside config on 8888 and discovery on 8761. So the random-port instruction applies to the local JVM mode, and the pinned values apply to the container mode. Neither is wrong, and treating the README sentence as universal is the easy mistake here, because a reader following the pinned-port expectation while running from an IDE will find nothing listening where they expected.

Building the images needs a Maven profile, and the README is specific that Docker or Docker Desktop must be installed and running:

```bash
./mvnw clean install -P buildDocker
```

Podman is a supported substitute with its own flag, and cross-architecture builds are handled by overriding the platform, since the default image target is `linux/amd64`:

```bash
./mvnw clean install -PbuildDocker -Dcontainer.executable=podman
./mvnw clean install -P buildDocker -Dcontainer.platform="linux/arm64"
```

The `main` branch uses an Eclipse Temurin image with Java 17 as its base. Once the images exist, startup is a single `docker compose up` or `podman-compose up`.

## Health checks, memory ceilings, and the timeouts you should expect

The compose file is worth reading closely because it encodes several operational decisions that the prose only gestures at.

Startup ordering is delegated to Compose itself. The README says containers are ordered with the `service_healthy` condition of the Compose depends-on expression and the healthcheck of the service containers, which is the correct mechanism, since a container that has started is not necessarily a container that can serve requests. Each health check is a curl against the service's own port, with config server probed on an interval of 5s and a 10 second timeout over 10 retries, and discovery server on a 3 second timeout.

Resource limits are explicit, and modest. Every service in the file carries the same ceiling:

```yaml
deploy:
  resources:
    limits:
      memory: 512M
```

That is a tight budget for a JVM and it connects directly to a warning in the README, which says that under macOS or Windows you should make sure the Docker VM has enough memory, because the default settings are usually not enough and make `docker-compose up` painfully slow. The per-container ceiling does not explain the host requirement, but it explains why the host needs headroom: several services capped at 512 MB each still need the VM to be able to hold them.

One rough edge is documented rather than hidden. After services start, it takes a while for the API Gateway to be in sync with the service registry, so the README tells you not to be scared of initial Spring Cloud Gateway timeouts, and points you at the Eureka dashboard on port 8761 to track availability. For a sample application that is a reasonable way to describe a genuinely confusing first run, and the gateway timeout is exactly the symptom you would otherwise spend time diagnosing.

There is also a hybrid mode for when Compose misbehaves. The README suggests `./scripts/run_all.sh`, which starts the infrastructure via `docker compose` and all the Java applications via a standard `nohup java -jar` command, with logs under `${ROOT}/target/nameoftheapp.log`, and `./scripts/stop_all.sh` to reverse it.

## Chaos Monkey and configuration from your own Git repository

Two features go beyond what a minimal split needs, and both are opt-in.

The first is fault injection. By default the applications are started without the `chaos-monkey` profile, and the README says you can pass an optional flag to enable it and interact with Spring Boot Chaos Monkey:

```bash
./scripts/run_all.sh --chaos-monkey
```

The repository topics confirm this is a first-class concern rather than a stray dependency, listing both chaos-monkey and resilience4j. The README points at a second-level README under `scripts/chaos/` for detail, and mentions a `call_chaos.sh` helper script for enabling assaults. That helper is the interesting part conceptually: with a circuit breaker library in the stack, the sample needs a way to make the breaker open on purpose, otherwise the Resilience4j configuration is never exercised and the demo silently proves nothing about fault tolerance.

The second is decoupling configuration from the repository. The README explains that you can tell Config Server to use your local Git repository with the `native` Spring profile and the `GIT_REPO` environment variable, for example:

```bash
-Dspring.profiles.active=native -DGIT_REPO=/projects/spring-petclinic-microservices-config
```

The `native` profile name is the important part of that. It signals that Config Server is reading configuration from a local checkout rather than fetching it, which is what makes it usable for local work at all: without it, a developer changing a property has to push a Git commit before the change takes effect anywhere. The sample configuration lives in its own repository, referenced here by the example path, so working on configuration means working across two checkouts.

## Two thousand stars, twenty-nine hundred forks, and tags two years apart

The repository metadata holds two details worth reading closely.

The first is that this project has more forks than stars. It shows 2207 stars against 2932 forks, with 13 open issues. Forks outnumbering stars that heavily is unusual, and the explanation is visible in the README rather than hidden: the project exists to be taken apart. The badges offer to open it in GitHub Codespaces and in Codeanywhere, the tree carries a `.devcontainer/` directory, and the opening section is about demonstrating how to split an application. People fork it to run it, to point it at their own domain, or to experiment with the wiring, and a fork is the cheaper action for all three of those than a star.

The second is the gap between the last commit and the last release. Three releases are visible: v3.0.9 on 2023-12-16, v3.2.0 on 2023-12-29, and v3.4.1 on 2024-12-28. The gap between the first two is thirteen days, which suggests the 3.0 line was being caught up quickly, while the gap from v3.2.0 to v3.4.1 is a full year. The repository is not archived and the last push was on 2026-09-27, roughly twenty-one months after v3.4.1. So this is active work landing on `main` without a tag per change, and the tags have drifted well behind.

There is also a mismatch between the topic list and the stack description that is worth flagging without resolving. The topics include `hystrix` and `ribbon`, the two components most associated with the first generation of Netflix-style Spring Cloud, while the README's own component list names Spring Cloud Gateway, Spring Cloud Circuit Breaker and Resilience4j instead. Those are replacements for the earlier ribbon load balancing and Hystrix circuit breaking, so the topic list appears to retain older keywords while the code has moved on. Neither the topics nor the README is a reliable guide to which library is actually on the classpath, so the `pom.xml` is the place to settle it.

Finally, there is no project homepage set. For a repository this widely forked and this often used as a starting point, that is a small missed opportunity, and it means the README and the compose file are effectively the whole documentation.

## Conclusion

This repository is worth reading for the split itself rather than for Petclinic, since the domain you already know makes the service boundaries legible: customers, vets and visits are three genuinely separate nouns and the GenAI service sits on top of them as a client. What you get for the effort is a working reference for eight JVM processes, an ordered startup sequence, and observability wired in rather than bolted on afterwards. What you do not get is production configuration: 512 MB memory limits per container, health checks expressed as curl against the root path, and no mention of TLS, authentication or persistence anywhere in the running instructions. Track the `main` branch rather than the tags, since the last commit is dated 2026-09-27 while the newest release, v3.4.1, is from 2024-12-28. If you want to follow along, bring up the config and discovery services first and watch the Eureka dashboard on port 8761 while the rest register.

## FAQ

### What is spring-petclinic-microservices and what is it for?

It is the distributed version of the Spring PetClinic sample, derived from the AngularJS version to demonstrate how to split a Spring application into microservices. Customers, Vets, Visits and a GenAI chatbot become separate services behind a gateway, with Eureka discovery and a config server.

### Which services must start first when running spring-petclinic-microservices locally?

Config Server and Discovery Server, before any of the application services. The README states this, and the compose file enforces it by making each service depend on both of them with `condition: service_healthy`.

### How do the services in spring-petclinic-microservices communicate?

Via REST APIs, with Spring Cloud Gateway routing client requests to the appropriate service and Eureka resolving service locations at runtime. The services do not share a database module.

### How do I enable Chaos Monkey in spring-petclinic-microservices?

Pass the `--chaos-monkey` flag to `./scripts/run_all.sh`, which starts the applications with the chaos-monkey profile instead of without it. The README points at `scripts/chaos/README.md` and a `call_chaos.sh` helper for enabling individual assaults.

### Why do the ports change when I run from an IDE instead of Docker Compose?

Started as local JVM processes, the Customers, Vets, Visits and GenAI services get random ports and you check the Eureka dashboard on port 8761 for which. The compose file pins them instead, to 8081, 8082 and 8083, so the README's random-port note applies to the non-Docker mode.

## Sources

- [Issues](https://github.com/spring-petclinic/spring-petclinic-microservices/issues)
- [License: Apache-2.0](https://github.com/spring-petclinic/spring-petclinic-microservices/blob/main/LICENSE)
- [README](https://github.com/spring-petclinic/spring-petclinic-microservices/blob/main/README.md)
- [Releases](https://github.com/spring-petclinic/spring-petclinic-microservices/releases)
- [spring-petclinic/spring-petclinic-microservices on GitHub](https://github.com/spring-petclinic/spring-petclinic-microservices)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/spring-petclinic-spring-petclinic-microservices
