advanced~2h

Docker & Docker Compose

You now have several services, a database, Redis, and Kafka — all needing to run together. This module is how to stop starting each one manually, in the right order, by hand.

Learning objectives

  • Beginner: Write a Dockerfile that packages a Spring Boot service as a runnable container image.
  • Intermediate: Write a docker-compose.yml that brings up the service alongside Postgres, Redis, and Kafka with correct startup ordering.
  • Advanced: Configure Compose networking so services discover each other by service name rather than hardcoded IPs, matching how they'd resolve each other in production.
FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY target/order-service-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8081 ENTRYPOINT ["java", "-jar", "app.jar"]

💻 Code example

FROM eclipse-temurin:21-jre-alpine WORKDIR /app COPY target/order-service-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8081 ENTRYPOINT ["java", "-jar", "app.jar"]

◆ The problem

A realistic version of this capstone system needs Postgres, Redis, a Kafka broker, and three Spring Boot services all running together, started in a sane order (services shouldn't start before their database is ready), and networked so they can find each other by name. Starting each with a separate docker run command, in the right order, by hand, doesn't scale past a couple of containers.

services: postgres: image: postgres:16 environment: POSTGRES_PASSWORD: secret POSTGRES_DB: orderdb ports: ["5432:5432"] redis: image: redis:7 ports: ["6379:6379"] kafka: image: apache/kafka:latest ports: ["9092:9092"] environment: KAFKA_PROCESS_ROLES: 'broker,controller' KAFKA_LISTENERS: 'PLAINTEXT://:9092,CONTROLLER://:9093' order-service: build: ./order-service ports: ["8081:8081"] depends_on: [postgres, kafka] environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orderdb SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 notification-service: build: ./notification-service depends_on: [kafka] environment: SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092

docker compose up -d # the entire system, started in dependency order, one command

💻 Code example

services: postgres: image: postgres:16 environment: POSTGRES_PASSWORD: secret POSTGRES_DB: orderdb ports: ["5432:5432"] redis: image: redis:7 ports: ["6379:6379"] kafka: image: apache/kafka:latest ports: ["9092:9092"] environment: KAFKA_PROCESS_ROLES: 'broker,controller' KAFKA_LISTENERS: 'PLAINTEXT://:9092,CONTROLLER://:9093' order-service: build: ./order-service ports: ["8081:8081"] depends_on: [postgres, kafka] environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orderdb SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 notification-service: build: ./notification-service depends_on: [kafka] environment: SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092

◆ Under the hood — why "postgres" and "kafka" work as hostnames

Docker Compose automatically creates a private network shared by every service defined in the file, and registers each service's name as a resolvable hostname on that network — this is exactly why order-service 's datasource URL above can say jdbc:postgresql://postgres:5432/orderdb using the literal string postgres rather than an IP address: Compose's built-in DNS resolves that service name to the right container automatically, and it would keep working even if the container's actual IP changed on a restart.

▲ Pitfall

depends_on controls start order, not readiness — it starts the Postgres container before order-service, but doesn't wait for Postgres to actually finish booting and accept connections. A service that connects to its database immediately on startup can still fail against a Postgres container that's merely "started" but not yet "ready." Production Compose setups typically add an explicit healthcheck and condition: service_healthy to genuinely wait for readiness, not just process start.

✓ Quick recap

Why can order-service's config reference "postgres" as a hostname instead of an IP? Docker Compose creates a shared network and resolves each service's name via built-in DNS automatically. Does depends_on guarantee a dependency is actually ready to accept connections? No — only that its container has started; genuine readiness needs an explicit healthcheck condition.

Want a visual for this concept?

Generate a diagram tailored to “Docker & Docker Compose” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Logging← Back to all Spring Boot chapters