DevOps
Docker Compose in practice: a guide to the parts you use every day
A practical Docker Compose guide: services, volumes, networks, environment files, health checks, depends_on conditions, profiles, common commands and mistakes to avoid.
By Raktim Ranjit · Published · 3 min read
Short answer: Docker Compose describes a group of containers, their networks and volumes in one compose.yaml file, and starts them with docker compose up -d. It is the simplest way to run a multi-container application on a single machine, from development to small production deployments. Learn services, volumes, environment files, health checks and a handful of commands, and you can do most of what you need.
What does a Compose file look like?
services:
web:
build: .
ports:
- "127.0.0.1:3000:3000"
env_file: .env
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
restart: unless-stopped
volumes:
db_data:Two services, one named volume. Compose creates a private network automatically, and each service can reach the others by service name. From web, the database host is simply db.
How do volumes work?
- Named volumes (
db_data:) are managed by Docker and are the right place for database files. They survivedocker compose downand are removed bydocker compose down -v. - Bind mounts (
./config:/etc/app) map a host folder. Handy for config and in development. They depend on the host path existing, which caused a surprise on an install where the repository was not present, as I describe in an empty bind mount on CasaOS.
docker compose down -v deletes your named volumes and therefore your data. Do not put it in a habit or a script you run on a server.
How do you manage configuration and secrets?
- Put variables in a
.envfile next tocompose.yaml. Compose substitutes${VAR}from it automatically. env_file:passes a whole file into the container.- Keep
.envout of Git and add a.env.examplewith fake values. - After changing environment variables, recreate the container. A plain restart does not pick up new values from the file, which I learned the hard way and wrote up in docker restart ignores env-file.
What are health checks and `depends_on` for?
depends_on alone only waits for a container to start, not for the application inside to be ready. A database can take seconds to accept connections. Add a healthcheck to the database and use condition: service_healthy so the web service starts afterward. Without it you see random startup failures that vanish on restart.
Which commands matter?
docker compose up -d # create and start in the background
docker compose ps # status
docker compose logs -f web # follow logs for one service
docker compose exec db psql -U postgres # run a command in a running container
docker compose pull && docker compose up -d # update images
docker compose down # stop and remove containers (keeps volumes)
docker compose config # print the final, merged filedocker compose config is underused. It shows exactly what Compose will do after variable substitution, which finds most typos.
What else is worth knowing?
- Profiles let you define optional services (
profiles: [debug]) and start them with--profile debug. - Overrides:
compose.override.yamlis merged automatically, so you can keep development tweaks separate from the base file. - Restart policies:
unless-stoppedbrings services back after a reboot. - Resource limits:
mem_limitandcpusstop one container from taking the machine. - Logging: set a log size limit, otherwise container logs can fill the disk.
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }What are the common mistakes?
- Publishing the database port to the internet. Do not publish it at all, the other services reach it over the Compose network.
- Using
latesttags, then being surprised by a major version jump. - Storing data in the container filesystem. It disappears when the container is recreated.
- Forgetting that published ports can bypass the host firewall. Bind to
127.0.0.1and use a reverse proxy. - Treating Compose as high availability. It runs on one machine.
When one machine is not enough, read Kubernetes for beginners: do you need it. For security settings, see is Docker safe for production.
References
Author
Raktim Ranjit is a software engineer and the founder of NodeDR Infotech. He builds and maintains the software described here.