Tutorial

Multi-Container Apps with Docker Compose

Tutorial · DevOps

Cover: Multi-Container Apps with Docker Compose

A real app is rarely one container — it's a web server, a database, maybe a cache, all talking to each other. Docker Compose lets you describe that whole stack in one file and bring it up with one command.

A minimal docker-compose.yml

version: '3.8'
services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: devpassword
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Two services, each getting its own container, networked together automatically so web can reach db by that name alone — no manual IP configuration.

Bringing it up

docker compose up

This builds the web image from the Dockerfile in the current directory, pulls the official postgres:15 image, starts both, and streams logs from each to your terminal. Add -d to run it in the background.

Why the volume matters

Without the volumes entry, Postgres would write its data files inside the container's writable layer — and lose everything the moment the container is removed. The named volume db-data persists on the host, surviving container restarts and rebuilds.

Environment variables and .env

# .env
DB_PASSWORD=devpassword
services:
  db:
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}

Compose automatically reads a .env file in the same directory, so secrets and config don't need to be hardcoded into the YAML itself — and the actual values can differ safely between developer machines.

depends_on controls start order, not readiness. Postgres being "started" isn't the same as it being ready to accept connections. For anything beyond local development, add a healthcheck or a retry loop in the app itself rather than assuming the database is reachable the instant the container starts.

Common commands

docker compose down          # stop and remove containers
docker compose logs -f web   # follow logs for one service
docker compose exec db psql -U postgres  # shell into a running container

These three cover the vast majority of day-to-day Compose usage once the stack is running.

Comments