Docker Compose for Development
Build reproducible local development environments with Docker Compose: services, volumes, networking, health checks, and hot reload.
Docker Compose orchestrates multi-container development environments. It eliminates "works on my machine" by defining the entire stack (app, database, cache, queues) in a single file that runs identically on every developer's machine.
Use Docker Compose for all local development that involves more than one service. Mount source code as bind volumes for hot reload. Use named volumes for databases. Profile compose files for dev vs CI vs staging.
Workflow
- Define your full stack in compose.ymlA single `docker compose up` should start the entire development environment without manual steps.
- Configure bind mounts for hot reloadWithout bind mounts, code changes require rebuilding the image - killing developer iteration speed.
- Use named volumes for persistent dataBind-mounted database data causes permission issues and is slow on macOS; named volumes are fast and portable.
- Add health checks and dependenciesServices start in parallel; without health checks, your app might try to connect to Postgres before it's ready.
- Use profiles for optional servicesNot every developer needs all services (e.g., Elasticsearch, Kafka); profiles let developers opt in.
Output Format
compose.yml with all services, health checks, named volumes for data, bind mounts for source, compose.override.yml for dev-only settings, profiles for optional services.
Examples
compose.yml service: `app: build: . volumes: - .:/app - /app/node_modules environment: DATABASE_URL: postgres://postgres:password@db:5432/mydb depends_on: db: condition: service_healthy ports: - "3000:3000"`Health check: `db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5`Named volume: `volumes: - pgdata:/var/lib/postgresql/data` (top-level) `volumes: pgdata:`Edge Cases & Gotchas
- macOS performance: Docker Desktop on macOS has slow bind mounts; use VirtioFS (Docker Desktop settings) or `delegated` mount option for 3-5× speedup
- M1/M2 architecture: add `platform: linux/amd64` for images not built for ARM, or use ARM-native images when available
- CI usage: use compose in CI for integration tests, but use `--project-name` flag to isolate parallel CI runs: `docker compose -p ci-run-${CI_JOB_ID} up -d`
When to Break These Rules
- Simple single-service apps (just a web server, no database) don't need Compose - just run the container directly with `docker run`.
Reference Files
Docker Compose spec: https://docs.docker.com/compose/compose-file/Docker Compose for dev: https://docs.docker.com/guides/use-case/databases/Compose watch (hot reload): https://docs.docker.com/compose/file-watch/