Run your whole stack with one command — describe an app plus its database in a compose.yaml, bring it all up with docker compose up, and wire in volumes, networks, and live-reload.
Typing long docker run commands for every service gets old fast. Docker Compose puts your whole stack in one file: each service is a container. Here the api is built from your Dockerfile and depends on a postgres service; the database persists to a named volume. Compose automatically puts both on one network, so the api reaches the database at the host "db".
# compose.yaml
services:
api:
build: . # build from the Dockerfile here
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/postgres
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:One command builds images, creates the network and volumes, and starts every service. -d runs them in the background. You watch logs across all services together, and down stops and removes everything in one go (your named volumes survive unless you add -v).
Build images, create the network + volumes, start everything in the background
docker compose up -dSee what's running
docker compose psFollow logs from all services together
docker compose logs -fStop and remove the containers + network (named volumes survive)
docker compose downFor development, override the api service to bind-mount your source and run the dev server, so code edits reload instantly inside the container. The anonymous /app/node_modules volume keeps the image's installed modules from being hidden by your host folder — the same trick from the volumes lesson, now in Compose.
# compose.yaml (dev) — the api service
api:
build: .
command: npm run dev # a watcher that reloads on change
ports:
- "3000:3000"
volumes:
- .:/app # your code, live
- /app/node_modules # keep the image's node_modules
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/postgres
depends_on:
- db