Three steps
- Write a Dockerfile for the app.
- Declare the services in
compose.yaml(the old namedocker-compose.ymlis still read). docker compose up -d.
services:
api:
build: .
ports: ["3000:3000"]
environment:
DB_HOST: db
depends_on: [db]
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: changeme-example
volumes: [db-data:/var/lib/postgresql/data]
volumes:
db-data:
flowchart LR
Y["compose.yaml"] -- "docker compose up -d" --> N["network node-app_default"]
N --- A["node-app-api-1"]
N --- D["node-app-db-1"]
D --- V[("volume node-app_db-data")]
Use docker compose (Compose v2, a Docker CLI plugin). The Python docker-compose v1 has been unsupported since 2023, and the top-level version: key is now obsolete.
Compose manages the whole lifecycle
| Command | Meaning |
|---|---|
docker compose up -d |
Create/start every service; only recreates containers whose config changed |
docker compose ps |
Service status |
docker compose logs -f api |
Stream the logs of service api |
docker compose down |
Stop and remove containers and the network; keeps volumes |
docker compose down -v |
Also removes named volumes — database data is lost |
docker compose -f compose.yaml -f compose.prod.yaml up -d |
Merge several files; later files override earlier ones |
Common traps
depends_ononly waits for thedbcontainer to start, not for Postgres to accept connections. To really wait, add ahealthchecktodbandcondition: service_healthy.down -vdeletes the data in volumes; do not use it where the data matters.
More service keys
services:
api:
build:
context: .
target: runtime
args: { NODE_VERSION: "20" }
image: registry.example.com/node-app:1.2.0
command: ["node", "server.js", "--port", "3000"]
restart: unless-stopped
depends_on:
db: { condition: service_healthy }
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 10
build and image together build the image and tag it with that name, ready for docker compose push. restart follows the same policies as docker run --restart.
Everyday commands
| Command | Meaning |
|---|---|
docker compose build |
Build or rebuild images |
docker compose pull |
Pull newer versions of images |
docker compose exec db psql -U postgres |
Run a command in a running service |
docker compose restart api |
Restart one service |
docker compose config |
Show the merged, resolved configuration |
docker compose watch |
Sync or rebuild automatically when files change (with develop.watch rules) |
Fast inner loop with watch
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
Source edits are copied into the running container, and a dependency change triggers a rebuild, without hand-written bind mounts.
Where Compose fits
The Compose file format is an open Compose Specification, also read by other tools. Compose is excellent for development, CI test environments, and small single-host deployments. For several hosts, self-healing across machines, and rolling updates at scale, the same ideas continue in Swarm and Kubernetes later in this track.