Docker Compose lets you define and manage multi-container applications with a single YAML file. Instead of running complex docker run commands, you declare all services, networks, and volumes in docker-compose.yml and manage the whole stack with simple commands.
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
depends_on ensures db starts before web. The named volume pgdata persists database data across container restarts.docker compose up -d
-d flag runs containers in detached mode (background). Without it, logs stream to your terminal. Run this from the directory containing docker-compose.yml. Use docker compose ps to verify all services started.docker compose down
up. Named volumes are preserved by default. Add -v to also remove named volumes (deletes all data — use with caution).docker compose up -d --build
--build flag forces Docker to rebuild images from Dockerfiles before starting. Use this after changing your Dockerfile or application code. Without it, Compose uses cached images even if the source has changed.docker compose logs -f
-f flag follows (tails) the logs in real time. Without it, all historical logs are printed and the command exits. Specify a service name to filter: docker compose logs -f web.docker compose exec web sh
docker exec -it but uses the service name instead of the container ID. Use bash if the container has bash installed.# .env file:
DB_PASSWORD=supersecret
APP_PORT=3000
# docker-compose.yml:
services:
app:
env_file:
- .env
ports:
- "${APP_PORT}:3000"
env_file directive loads variables from a file into the container. Variables in .env are also available for interpolation in the compose file itself with ${VAR} syntax. Never commit .env files with secrets to version control.services:
web:
image: myapp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
depends_on: service: condition: service_healthy will wait until the health check passes.docker compose up -d --scale web=3
--scale flag runs multiple instances of a service. Useful for testing load balancing locally. Note: port mapping must use a range (e.g., "8080-8082:80") or omit host port binding when scaling.docker-compose.yml file in your project root.services: key with image, ports, and environment.docker compose up -d to start all services in the background.docker compose logs -f to monitor logs and docker compose down to stop.docker compose up -d --build after code changes to rebuild and restart.docker-compose (with hyphen) is the older standalone Python-based tool (v1). docker compose (as a subcommand) is the newer Go-based plugin integrated into the Docker CLI (v2). They are functionally similar but v2 is faster. Modern Docker Desktop includes v2 by default.
Use named volumes (defined in the top-level volumes: section) or bind mounts (host directory paths). Named volumes are managed by Docker and survive docker compose down. They are deleted only when you explicitly remove them with docker compose down -v.
Compose automatically creates a shared network for all services in the same file. Services can reach each other using the service name as the hostname. For example, the web service can connect to the database at hostname db on its exposed port.