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 →