sehaj/learn
Docker/06 Compose

06.01 · with code · 11 min

Anatomy of a compose file

A compose.yaml is your docker run commands written down once. Learn its shape, how each flag maps to a key, and the five commands you'll use daily.

The core idea

Docker Compose reads a YAML file describing containers, networks and volumes, and makes reality match it. Instead of remembering a dozen commands and their order, you write down what you want once and run docker compose up. It's declarative: run it again and Compose only changes what's different.

From docker run to YAML

Every flag you've used has a key. Here's the database from step 4, both ways:

before
docker volume create notes-pgdata
docker run -d --name notes-db --network notes-backend \
-e POSTGRES_USER=notes -e POSTGRES_PASSWORD=notes -e POSTGRES_DB=notes \
-v notes-pgdata:/var/lib/postgresql postgres:18
compose.yaml
name: notes

services:
db:
  image: postgres:18
  environment:
    POSTGRES_USER: notes
    POSTGRES_PASSWORD: notes
    POSTGRES_DB: notes
  volumes:
    - pgdata:/var/lib/postgresql
  networks: [backend]

networks:
backend:

volumes:
pgdata:
docker runcompose key
image nameimage:
docker build -t ... ./apibuild: ./api (or build: { context, target })
-p 8080:80ports: ["8080:80"]
-e KEY=valueenvironment:
-v name:/pathvolumes:, plus the volume under top-level volumes:
--network namenetworks:, plus the network under top-level networks:
--restart unless-stoppedrestart: unless-stopped
--health-cmd ...healthcheck:
command after the imagecommand:

The four top-level keys

  • name: the project name. Compose prefixes everything it creates with it.
  • services: one entry per container. The key (db) is the service name, and also its DNS name on the project's networks.
  • networks and volumes: declared once here, referenced from services. If you declare no networks at all, Compose creates one called <project>_default and puts every service on it.

The commands

❯terminal
$ docker compose up -d
Network notes_frontend Created
Volume notes_pgdata Created
Container notes-cache-1 Created
Container notes-db-1 Created
Container notes-api-1 Created
Container notes-proxy-1 Created
...
$ docker compose ps --format 'table {{.Service}}\t{{.Name}}\t{{.Status}}\t{{.Ports}}'
SERVICE NAME STATUS PORTS
api notes-api-1 Up 5 seconds (healthy)
cache notes-cache-1 Up 11 seconds (healthy) 6379/tcp
db notes-db-1 Up 11 seconds (healthy) 5432/tcp
proxy notes-proxy-1 Up Less than a second 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
$ docker compose down

Output trimmed. Names follow the pattern project-service-number for containers and project_name for networks and volumes.

CommandDoes
docker compose up -dcreate or update everything, start it in the background
docker compose psthe project's containers
docker compose logs -f apifollow one service's logs (all services if you leave the name off)
docker compose downstop and remove containers and networks; keeps volumes
docker compose down -vthe same, plus the volumes, which means the data

Compose works from the folder containing compose.yaml (it also accepts the older name docker-compose.yml).

Predict first

You change only the proxy's port in compose.yaml from 8080:80 to 9090:80 and run docker compose up -d again. What happens?

Commit to an answer before you read on.
CheckpointTranslate the database

Write a compose.yaml with only the db service above. Run docker compose up -d, check the container, network and volume names with docker compose ps, docker network ls and docker volume ls, then docker compose down and confirm the volume survived.

STUCK? ▸

docker compose config prints the file as Compose understood it, after defaults and variables. If a key is missing there, it's indented wrong.

Recap

Interview questions

JuniorfreqMust-knowConcept

What is Docker Compose, and when would you use it instead of plain docker run?

Report a mistakeEdit this page
☰ Compose06.01 · 32/44next →