exit lab
Hands-on labs
Intermediate

Docker Compose: Next.js + Spring Boot + PostgreSQL

Run the whole stack — frontend, API, and database — with a single command and no manually installed services.

1 hr

Prerequisites

  • Docker and Docker Compose installed
  • A Dockerfile for both the Spring Boot API and the Next.js app

Architecture

Three services on one Compose network

frontendNext.js :3000
apiSpring Boot :8080
dbPostgreSQL :5432

Build checklist

Steps

0 of 4 complete

Step 1

Write docker-compose.yml

yaml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: appdb
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

  api:
    build: ./api
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/appdb
      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8080:8080"

  frontend:
    build: ./frontend
    environment:
      NEXT_PUBLIC_API_URL: http://localhost:8080
    depends_on:
      - api
    ports:
      - "3000:3000"

volumes:
  pgdata:
Step 2

Start every service

bash
docker compose up -d --build
Step 3

Watch the logs while it starts

bash
docker compose logs -f
Step 4

Confirm the frontend can reach the API

bash
curl http://localhost:3000

Expected result

What success looks like

Three containers running together, the API only starting once the database passes its healthcheck, and the frontend reachable on port 3000 with working API calls to the backend on port 8080.

Lessons learned

Tip

depends_on: condition: service_healthy is what actually prevents the app from racing the database on startup — plain depends_on only waits for the container to start, not for Postgres inside it to be ready to accept connections.

Tip

The API connects to db:5432, not localhost:5432 — Compose gives every service a DNS name on the shared network matching the service key in the YAML.

Tip

A named volume (pgdata) is what makes data survive docker compose down — without it, every recreate wipes the database.