After this lesson, you will be able to: Use Docker Compose to define multi-container applications: services, networks, volumes, environment variables, depends_on.
Compose lets you describe an entire stack (app + DB + cache + reverse proxy) in one YAML file and bring it up with one command. The default for dev environments; common for small production deploys.
Drop in your project root. Run `docker compose up -d`.
# docker-compose.ymlservices:web:build: .ports:- "3000:3000"environment:DATABASE_URL: postgres://app:app@db:5432/appREDIS_URL: redis://cache:6379depends_on:db:condition: service_healthycache:condition: service_startedrestart: unless-stoppeddb:image: postgres:16-alpineenvironment:POSTGRES_USER: appPOSTGRES_PASSWORD: appPOSTGRES_DB: appvolumes:- pgdata:/var/lib/postgresql/datahealthcheck:test: ["CMD-SHELL", "pg_isready -U app"]interval: 5stimeout: 5sretries: 5cache:image: redis:7-alpinerestart: unless-stoppedvolumes:pgdata:
Memorise these.
docker compose up # build + start, foregrounddocker compose up -d # detacheddocker compose up --build # force rebuilddocker compose down # stop + remove containers + networksdocker compose down -v # also remove volumes (DESTRUCTIVE)docker compose ps # statusdocker compose logs -f web # tail logs of one servicedocker compose exec web bash # shell into the web servicedocker compose restart web # restart one service
Compose puts every service on a default network. Services reach each other by SERVICE NAME, not IP. In the file above, `web` connects to the database with the URL `postgres://app:app@db:5432/app`, `db` is the service name, resolved to the container's IP automatically. If you need multiple networks (e.g. a public + private network), declare them under top-level `networks:` and attach services explicitly.
Without a volume, container data is lost on `docker compose down`. Database = lost. Named volumes (declared under top-level `volumes:`) survive container removal. Always use them for stateful services. Bind mounts (host path) are great for dev (live-edit code into a container) but tricky in production (host file permissions).
Naively, `depends_on: [db]` makes the web service WAIT for db to start. But Docker only waits for db's PROCESS to start, not for postgres to be ready to accept connections. Use `condition: service_healthy` + a healthcheck on the dependency (see the YAML above). That waits until postgres responds to pg_isready before starting web. Skipping the healthcheck means web sometimes crashes on first boot because db isn't ready.
Hardcoding secrets in docker-compose.yml. Use `.env` files (auto-loaded by compose) or `secret:` declarations. Skipping healthchecks on dependencies. Boot-order races bite. Using bind mounts for postgres data in production. Permission and performance problems on Mac/Windows. `docker compose down -v` in production by accident. Wipes data volumes. Aliasing is dangerous; train your fingers. Letting compose be the deploy tool in production. Compose works for small single-host deploys; for anything else, use Kubernetes or a managed container platform.
Pick the correct mitigation.
Sign in and purchase access to unlock this lesson.