Docker Compose — Multi-Container Apps
IntermediateDocker Compose defines and runs multi-container applications with a single YAML file — replacing dozens of docker run commands with `docker compose up`.
Overview
Real applications are rarely a single container. A typical web app needs an app server, a database, a cache, and maybe a queue — each in its own container. Running each with docker run, manually linking networks and volumes, is error-prone and not reproducible. Docker Compose solves this with a declarative docker-compose.yml: define all services, networks, and volumes in one file. `docker compose up` builds images, creates the network, starts all services in dependency order, and streams logs. `docker compose down` tears everything down. Compose is also the standard tool for local development environments.
Full Stack docker-compose.yml
A realistic Compose file for a Next.js app with PostgreSQL and Redis. Services on the same Compose file automatically join a shared network and can reach each other by service name.
# docker-compose.yml
version: '3.9'
services:
# ── Database ────────────────────────────────────────
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
# ── Cache ────────────────────────────────────────────
redis:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
# ── API Backend ──────────────────────────────────────
api:
build:
context: ./backend
dockerfile: Dockerfile
environment:
DATABASE_URL: postgres://user:secret@postgres:5432/myapp
REDIS_URL: redis://redis:6379
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy # wait for DB healthcheck to pass
redis:
condition: service_started
restart: unless-stopped
# ── Frontend ─────────────────────────────────────────
frontend:
build: ./frontend
environment:
NEXT_PUBLIC_API_URL: http://localhost:8000
ports:
- "3000:3000"
depends_on:
- api
volumes:
postgres-data:
redis-data:Essential Compose Commands
Compose commands map naturally to docker commands but operate on all services at once. The new `docker compose` (v2) is integrated into the Docker CLI.
# Start all services (build if needed), stream logs
docker compose up
# Start in background
docker compose up -d
# Rebuild images then start
docker compose up --build
# Start only specific services
docker compose up -d postgres redis
# View logs
docker compose logs -f api # follow logs for api service
docker compose logs --tail=50 # last 50 lines from all services
# Run a one-off command in a service container
docker compose exec api sh # open shell
docker compose exec postgres psql -U user -d myapp
# Scale a service (run multiple replicas)
docker compose up --scale api=3 -d
# Stop and remove containers, networks (NOT volumes)
docker compose down
# Stop and remove everything including volumes (âš ï¸ deletes DB data!)
docker compose down -vEnvironment Files & Overrides
Use .env files for environment-specific values and docker-compose.override.yml for dev vs prod differences without duplicating the base file.
# .env (auto-loaded by Compose — do not commit to git!)
POSTGRES_PASSWORD=mysecretpassword
API_PORT=8000
# docker-compose.yml (base — production values)
services:
api:
image: myregistry/api:${IMAGE_TAG}
environment:
DATABASE_URL: postgres://user:${POSTGRES_PASSWORD}@postgres:5432/myapp
# docker-compose.override.yml (auto-merged in dev — git-committed)
# Only differences from base config needed here
services:
api:
build: ./backend # build locally in dev, use image in prod
volumes:
- ./backend/src:/app/src # hot-reload source code
environment:
LOG_LEVEL: debug
# docker-compose.prod.yml (explicit prod overrides)
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -dKey Points to Remember
- 1Compose defines multi-container apps declaratively in a single YAML file.
- 2All services in a Compose file share a default network and resolve each other by service name.
- 3depends_on with condition: service_healthy waits for healthchecks before starting dependent services.
- 4docker-compose.override.yml is auto-merged and is the standard pattern for dev overrides.
- 5Use .env files for secrets and environment-specific values — never hardcode passwords.
- 6docker compose down -v deletes volumes — use with caution, it wipes database data.
Interview Questions
Sign in to ask AriaWhat does depends_on do in Docker Compose?
How do you handle environment-specific configuration in Compose?
Ask Aria about Docker Compose — Multi-Container Apps
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.