Docker Compose Basics
Docker Compose is a YAML-based tool to define and run multi-container Docker applications.
Concept explained
At its simplest, a docker-compose.yml
lists services. Each service maps to an image or a build context, optional ports, environment variables, volumes and dependencies.
Key pieces:
- services: named containers that make up the app (web, db, redis).
- volumes: persistent storage (databases, uploads).
- networks: how services talk to one another (default network is created automatically).
- environment / env_file: configure services.
- depends_on: controls start order but not readiness – use healthchecks if you need service readiness.
Prefer the modern CLI command docker compose
(no hyphen) when using Docker Compose v2. Some systems may still support docker-compose
.
Step-by-step example
Below is a minimal app with a simple Node web service and Postgres. Put this docker-compose.yml
in your project root.
docker-compose.yml:
services:
web:
build: . # builds using ./Dockerfile
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://postgres:password@db:5432/app
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: password
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Minimal Dockerfile for the web service (Node example):
FROM node:18-slim
WORKDIR /app
COPY package.json ./
COPY app.js ./
EXPOSE 3000
CMD ["npm", "start"]
Common commands:
- Build and start in foreground:
docker compose up --build
- Start in detached mode:
docker compose up -d
- Stop and remove:
docker compose down
- View logs:
docker compose logs -f web
- Run a one-off command:
docker compose exec web sh
Variations & gotchas
- Healthchecks: depends_on does not wait for app readiness. Add a
healthcheck
to the DB and usecondition: service_healthy
in older compose specs or implement a retry in the app. - .env files: Compose supports an
.env
file; avoid committing secrets. See Environment variables & Secrets for patterns. - Bind mounts vs named volumes: bind mounts are handy for local development; use named volumes for persistent production data.
- Compose file version: modern versions of Docker Compose automatically use the latest schema, so you don't need to specify a version in your compose file.
- Scaling:
docker compose up --scale worker=3
works for stateless services, not for stateful DBs.
Don't rely on Compose as a production orchestrator for large, multi-host clusters. For small apps Compose is often sufficient.
Common mistakes
- Exposing databases publicly by publishing DB ports (e.g.,
- "5432:5432"
). Avoid unless needed. - Forgetting volumes for databases – leads to data loss when containers are removed.
- Using
latest
image tags in production – makes rollbacks and reproducibility hard. - Expecting
depends_on
to guarantee the dependent service is ready (it only controls start order). - Committing
.env
or credentials to version control.
Best practices
- Pin image versions (e.g.,
postgres:15
) for reproducible runs. - Use named volumes for stateful services and bind mounts for local dev.
- Keep small services and one responsibility per container.
- Add a sensible
restart
policy (e.g.,unless-stopped
). - Use healthchecks for services that other services depend on.
- Put secrets in a secret manager or use environment files kept out of VCS.
When to use / when not to use
When to use:
- Local development, CI tasks, small staging or production apps.
- Single-host deployment or simple multi-container stacks.
- Fast iteration workflows where Docker Compose reduces infra overhead.
When not to use:
- Large, multi-node clusters requiring service discovery, autoscaling, or advanced scheduling – use orchestration (e.g., Kubernetes).
- When you need multi-region deployments and complex network policies.
Key takeaways
- Docker Compose ties services, networks and volumes in a single file so you can run multi-container apps with one command.
- Use named volumes for persistent data, pin image versions, and add healthchecks for readiness.
docker compose up --build
starts the app;docker compose down
removes it – remember volumes persist unless removed explicitly.- Compose is great for local and small-scale deployments but not a drop-in replacement for full orchestrators.