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
healthcheckto the DB and usecondition: service_healthyin older compose specs or implement a retry in the app. - .env files: Compose supports an
.envfile; 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=3works 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
latestimage tags in production – makes rollbacks and reproducibility hard. - Expecting
depends_onto guarantee the dependent service is ready (it only controls start order). - Committing
.envor 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
restartpolicy (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 --buildstarts the app;docker compose downremoves 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.