Why Docker Compose
Real applications are rarely a single container — a typical web app needs a backend API, a database, and maybe a cache. Docker Compose lets you describe all of these services, their networks, and volumes in one YAML file, then start or stop the entire stack with a single command.
Cricket analogy: A full match-day setup isn't just the batting team but also umpires, ground staff, and broadcast crew, and Docker Compose is like the match director's single sheet that lines up every one of these units to start or wrap the whole day with one call.
Anatomy of a docker-compose.yml
A Compose file defines services (containers), and optionally volumes and networks that those services share. Each service maps closely to the flags you'd otherwise pass to docker run.
Cricket analogy: A Compose file's 'services' section listing db and web is like a scorecard listing every squad involved in the day, batting and bowling, and each entry mirrors exactly the instructions a captain would otherwise give each squad individually, like docker run flags.
services:
api:
build: ./api
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=example
- POSTGRES_DB=appdb
volumes:
- db-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
db-data:Starting and Stopping the Stack
docker compose up builds (if needed) and starts every service defined in the file. docker compose down stops and removes the containers, and optionally the network and volumes.
Cricket analogy: docker compose up is like the toss followed by the umpire calling 'play' to start every unit of the match at once, while docker compose down is like stumps being called, packing up the ground, and optionally clearing the pitch and stands entirely.
docker compose up -d # build/start all services in the background
docker compose ps # list services and their status
docker compose logs -f api # follow logs for just the 'api' service
docker compose down # stop and remove containers and default network
docker compose down -v # also remove named volumes (destroys data!)docker compose down -v deletes the named volumes declared in the file, which permanently destroys any database data stored in them unless you have a backup.
Service Discovery and depends_on
Compose automatically creates a user-defined network for the project, so services can reach each other by their service name (e.g. db) just like with docker network create. depends_on controls startup order but does not wait for the dependency to be fully ready by default.
Cricket analogy: Compose creating a shared network so services reach each other by name like 'db' is like a stadium's internal radio channel where any staff member can just call out 'groundsman' by role and be heard, while depends_on merely ensures the groundsman arrives before play starts, not that the pitch is actually mowed yet.
services:
api:
build: ./api
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5Using condition: service_healthy alongside a healthcheck is the correct way to make one service wait until another is actually ready to accept connections, not just started.
Rebuilding and Scaling Services
When you change a Dockerfile or its build context, rebuild images before restarting. Compose can also run multiple replicas of a stateless service for simple local load testing.
Cricket analogy: Rebuilding images after changing a Dockerfile is like re-stringing a bat after altering its weighting before the next net session, and running multiple replicas locally is like setting up several bowling machines at once to simulate a full attack for practice.
docker compose build # rebuild images defined with 'build:'
docker compose up -d --build # rebuild and restart in one step
docker compose up -d --scale api=3 # run 3 replicas of the 'api' service- docker-compose.yml defines services, networks, and volumes for a multi-container app in one file
- docker compose up -d starts the whole stack; docker compose down stops and removes it
- Services reach each other by service name via Compose's automatically created network
- depends_on controls startup order only; combine with healthcheck + condition: service_healthy to wait for real readiness
- docker compose down -v deletes named volumes — use with caution, it is destructive
- docker compose logs -f <service> and docker compose ps are the primary debugging commands
Practice what you learned
1. What is the primary purpose of a docker-compose.yml file?
2. How do services in the same docker-compose.yml typically address each other?
3. What does `docker compose down -v` do that plain `docker compose down` does not?
4. By default, what does `depends_on` guarantee about service startup order?
5. Which Compose configuration correctly waits for a database to be truly ready before starting a dependent service?
Was this page helpful?
You May Also Like
Container Networking Basics
Understand Docker's default bridge network, port publishing, and how containers discover and communicate with each other on user-defined networks.
Environment Variables and Configuration
Learn how to configure containerized applications using environment variables, .env files, and Dockerfile defaults without rebuilding images.
Volumes and Persistent Storage
Learn how Docker volumes and bind mounts persist and share data beyond a container's lifecycle, and when to use each option.
Health Checks and Restart Policies
How Docker uses HEALTHCHECK instructions and restart policies to detect unhealthy containers and automatically recover from failures.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics