Docker Compose
Define and run multi-container applications with Docker Compose v2: services, networks, volumes, healthchecks, depends_on, and profiles.
Docker Compose lets you describe a multi-container application in a single YAML file and manage it with one command. Instead of typing long docker run commands for the app, database, and cache, you declare services, networks, and volumes once and bring everything up consistently. It is the standard tool for local development, integration testing, and small single-host deployments.
The Compose File
Modern Compose (v2, invoked as docker compose) follows the Compose Specification. The old top-level version: key is obsolete and can be omitted. The default file name is compose.yaml (docker-compose.yml is still supported).
services:
app:
build: .
ports:
- "8080:8080"
environment:
DB_HOST: db
DB_USER: app
DB_PASSWORD: ${DB_PASSWORD}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 5s
timeout: 3s
retries: 10
cache:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
Put the password in a .env file next to the Compose file (and keep that file out of Git):
DB_PASSWORD=change-me
Compose reads .env automatically for variable substitution.
Everyday Commands
docker compose up -d # create and start everything in the background
docker compose up -d --build # rebuild images first
docker compose ps # list project containers
docker compose logs -f app # follow one service's logs
docker compose exec db psql -U app appdb # run a command in a running service
docker compose run --rm app sh # one-off container for a service
docker compose stop # stop containers, keep them
docker compose down # stop and remove containers and networks
docker compose down -v # also remove named volumes (data loss)
docker compose config # validate and print the resolved configuration
Key Service Options
| Option | Purpose |
|---|---|
image |
Image to run |
build |
Build from a Dockerfile (context, dockerfile, args, target) |
ports |
Publish ports as "host:container" |
environment / env_file |
Set environment variables |
volumes |
Mount named volumes or bind mounts |
networks |
Attach to specific networks |
depends_on |
Control start order, optionally waiting for health |
healthcheck |
Define how readiness is tested |
restart |
Restart policy (no, always, on-failure, unless-stopped) |
command / entrypoint |
Override image defaults |
profiles |
Start a service only when its profile is enabled |
deploy.resources |
Resource limits and reservations |
depends_on and Readiness
Plain depends_on: [db] only controls start order; it does not wait until the database accepts connections. Combine it with a healthcheck and condition: service_healthy as in the example above so the app starts only after the database is ready. Applications should still retry connections, because dependencies can restart later.
Networking and Service Discovery
Compose creates one default network per project, and each service is reachable by its service name. In the example, the app connects to db:5432 and cache:6379. Ports under ports: are only needed for access from the host.
Development Overrides and Profiles
Compose automatically merges compose.yaml with compose.override.yaml, which is handy for development settings:
# compose.override.yaml
services:
app:
volumes:
- ./src:/app/src
environment:
LOG_LEVEL: debug
Use multiple files explicitly for other environments:
docker compose -f compose.yaml -f compose.prod.yaml up -d
Profiles keep optional services out of the default start:
services:
adminer:
image: adminer
ports: ["8081:8080"]
profiles: ["debug"]
docker compose --profile debug up -d
Common Mistakes
- Assuming
depends_onwaits for readiness. Withoutcondition: service_healthythe app may start before the database is usable. - Committing secrets in the Compose file. Use
.env(ignored by Git),env_file, or Compose secrets. - Running
docker compose down -vcasually. It deletes named volumes and your data. - Using
localhostto reach another service. Use the service name. - Mixing up
docker-composeanddocker compose. The hyphenated v1 tool is deprecated; use thedocker composeplugin.
Best Practices
- Pin image tags and keep Compose files in version control.
- Add healthchecks to services others depend on.
- Use named volumes for data and a
.envfile or secrets for credentials. - Use override files and profiles to separate development, test, and production concerns.
- Run
docker compose configto check the final merged configuration before deploying.
Interview Questions
1. What is Docker Compose and why use it?
Compose defines multi-container applications in a YAML file and manages their lifecycle with commands like docker compose up and down. It makes environments reproducible and removes the need for long, error-prone docker run commands.
2. Does depends_on wait for a service to be ready?
Not by default; it only orders container startup. To wait for readiness, define a healthcheck on the dependency and use depends_on with condition: service_healthy. Applications should also retry connections.
3. How do services in Compose find each other?
Compose creates a project network and registers each service name in Docker's DNS. A service can reach another by its service name and container port, for example db:5432.
4. What is the difference between docker compose stop, down, and down -v?
stop halts containers but keeps them. down stops and removes containers and the project network. down -v additionally removes named volumes declared in the file, deleting stored data.
5. How do you manage different configurations for dev and prod?
Use compose.override.yaml for development, pass extra files with -f for other environments, and use environment variables, env_file, or profiles to vary behaviour.
6. Is Compose suitable for production?
For single-host deployments and small setups it can work, with restart policies and healthchecks. For multi-host scaling, rolling updates, and self-healing across nodes, an orchestrator such as Kubernetes is the usual choice.
Summary
- Compose describes services, networks, and volumes in one YAML file and runs them with
docker compose. - The
version:key is not needed in Compose v2; the default file iscompose.yaml. - Use healthchecks with
depends_onconditions for real readiness ordering. - Keep secrets out of version control and be careful with
down -v.
Continue Learning
Previous: Docker Networking | Next: Dockerize Spring Boot App
Was this guide helpful?
CodeWithVenu is free to read, with no paywall. If this guide saved you time or helped you prepare, a small voluntary contribution helps me write more practical tutorials, architecture guides, and interview-prep content.