DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

Eight services and six pieces of infra — one docker compose up brings the whole stack up in dependency order

Forty-one days built the pieces of OrderHub — a config server, a service registry, an API gateway, and five Spring Boot services. Day 42 makes them ship together. Each service gets a production multi-stage Dockerfile, and one docker-compose.yml wires all eight alongside Postgres, Redis, Kafka, Prometheus, Grafana and Loki, so a single docker compose up brings the whole distributed system up on one laptop, in the right order, on one network. Here's how it fits together.

Multi-stage: the build toolchain never ships

Every service Dockerfile has two stages. A maven:3.9-eclipse-temurin-21 stage builds the jar; a slim eclipse-temurin:21-jre stage copies only the artifact and runs it. The Maven cache, the source, the whole build toolchain stay in the build stage and never reach the final image — the thing you ship is a lean JRE plus your jar, nothing else. That's the difference between an image measured in gigabytes and one measured in a couple hundred megabytes, and it means the running container has no compiler, no build tools, nothing an attacker could pivot on. One shared .dockerignore trims every build context so the daemon isn't handed target/ and .git on every build.

Layered jar + non-root runtime

Spring Boot can explode the jar into layers — dependencies first, then your own code — so a one-line code change rebuilds only the top layer while the fat dependency layer stays cached. That turns a rebuild from minutes into seconds. And the runtime image runs as a non-root user, not as root: a small change that shrinks the blast radius if anything ever escapes the process, and one that costs nothing to add.

depends_on + healthchecks make ordering real

A distributed stack has a boot order — config first, then the registry and stateful middleware, then the services register, then the gateway opens last. depends_on: condition: service_healthy is what makes that ordering real: a plain depends_on only waits for the container to be created, which is useless when Postgres is still initialising. Each service declares a healthcheck, and compose gates every dependency on the dependency actually reporting healthy — so nothing starts talking to something that isn't ready.

One network, found by name

All fourteen containers share one orderhub-net bridge, and Docker's embedded DNS lets each find the others by service name — config-server:8888, eureka-server:8761, kafka:9092. No hardcoded IPs, no host ports for internal chatter; only the gateway publishes a port, opening the single public door on :8080. Container-to-container traffic never leaves the bridge.

dev vs prod, same compose

The same compose file that runs the production-shaped images also carries the observability stack — Prometheus scrapes the services, Grafana dashboards them, Loki collects logs — so a developer gets the whole system, not just the happy path. Flip a build arg or an override and you're either running the slim JRE images or a faster local loop; the graph, the ports and the discovery names stay identical, which is the point of pinning it all down in one file.

Proven without a daemon

The whole thing is asserted by a plain JUnit + SnakeYAML test — no Docker daemon required in CI — that checks every module has a multi-stage Dockerfile and that the compose graph is well-formed (services, healthchecks and depends_on all present and consistent). The reactor went from 150 to 152 tests, BUILD SUCCESS. Testing the wiring without spinning up Docker keeps the suite fast and CI honest — a misnamed service or a missing healthcheck fails the build, not a flaky container. The exact same images are what a Kubernetes cluster will run next; compose here is just the single-host way to prove the whole graph stands up. A new engineer clones the repo and runs one line — docker compose up --build — to get the entire distributed system: config server, registry, gateway, five services, Kafka, Postgres, Redis and the full observability stack, all on their laptop.

See it come up service by service — config first, gateway last:

Live https://dev48v.infy.uk/orderhub/day42-dockerize-compose.html
Repo https://github.com/dev48v/order-hub-from-zero

Top comments (0)