← Back to Docker Mastery
Intermediate14 min read

Docker Compose

Define multi-container applications with Docker Compose — services, networking, volumes, and environment configuration in a single YAML file.

Compose File Structure

Docker Compose defines multi-container apps in compose.yml (or docker-compose.yml). Each service is a container with its own image, ports, environment, and volumes. Compose handles networking between services automatically.

Services communicate by service name — a web service connects to db:5432, not localhost:5432. Compose creates a dedicated network for the project.

  • Use compose.yml (no hyphen) — the modern convention
  • depends_on with condition waits for health checks, not just start
  • Service names become DNS hostnames within the compose network
services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      retries: 5

volumes:
  pgdata:

Compose Commands

docker compose up starts all services. Add -d for detached mode. docker compose down stops and removes containers, networks, and optionally volumes. docker compose logs -f follows service logs.

Use docker compose exec web sh to access a running service. docker compose build rebuilds images. docker compose ps shows service status.

docker compose up -d          # Start all services
docker compose logs -f web      # Follow web logs
docker compose exec db psql -U user myapp
docker compose down -v          # Stop and remove volumes
docker compose up --build -d    # Rebuild and start

Networking in Compose

Compose creates a default bridge network for the project. All services join this network and resolve each other by name. Expose ports only for services that need external access.

Custom networks enable isolation between service groups. An frontend network for web-facing services and a backend network for databases adds security layers.

services:
  web:
    networks: [frontend, backend]
  api:
    networks: [backend]
  db:
    networks: [backend]

networks:
  frontend:
  backend:
    internal: true  # No external access

Environment and Profiles

Use env_file to load environment variables per service. Override with .env file in the project root for local development. Profiles activate optional services — a debug profile adds monitoring tools without running them by default.

Multiple compose files merge: docker compose -f compose.yml -f compose.prod.yml up combines base and production overrides.

services:
  web:
    env_file: .env
    profiles: ["default"]

  adminer:
    image: adminer
    ports: ["8080:8080"]
    profiles: ["debug"]

# Start with debug profile
# docker compose --profile debug up

Development Workflow

Mount source code as volumes for live development. The container runs the app, but code changes on the host reflect immediately. Combine with nodemon or vite dev server for hot reloading.

Use compose watch (Docker Compose v2.22+) for automatic file sync and rebuild on changes — a modern replacement for volume mount development.

services:
  web:
    build: .
    volumes:
      - ./src:/app/src
      - /app/node_modules  # Preserve container node_modules
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src

Get In Touch


Ready to discuss your next project? Drop me a message.