Course outline · 0% complete

0/29 lessons0%

Course overview →

One file for the whole app

lesson 5-1 · ~12 min · 14/29

From unit 4, booting the two-container app by hand took roughly four or five long commands, in the right order, every single time.

The sequence was a network create, a volume create, a db run with volume and network, and an app run with network and ports, each carrying flags that must be remembered exactly.

StepCommand
1docker network create mynet
2docker volume create dbdata
3docker run -d --name db --network mynet -v dbdata:...
4docker run -d --name app --network mynet -p 8080:8000 ...

Missing a flag or running them out of order breaks the app. That repetition is exactly what docker compose eliminates.

docker compose

Compose lets you describe your whole multi-container setup in one file, docker-compose.yml, and boot it with one command:

docker compose up -d

The file is written in YAML, a config format where structure comes from indentation and most lines are key: value. Two spaces per level, no tabs. You will read YAML constantly from here on, since GitHub Actions in unit 7 uses it too.

Compose reads the file, then creates the network, the volumes, and every container, in the right order, with the right flags. Delete everything just as easily with docker compose down. The file lives in your repo, so the entire runnable app setup is version-controlled with git, like everything else.

The file, decoded line by line

services:
  app:
    build: .
    ports:
      - "8080:8000"
    environment:
      DATABASE_URL: postgres://shop:secret@db:5432/shop
    depends_on:
      - db
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata:
KeyMeaningHand-run equivalent
services:the containers to runone docker run each
build: .build this image from the local Dockerfiledocker build .
ports:published port mapping-p 8080:8000
environment:env vars handed to the container-e
depends_on:start orderordering by hand
image: postgres:16use a prebuilt imagedocker run postgres:16
volumes:mount a named volume-v dbdata:...

Each key under services: is one service, and its name becomes the container's network hostname, exactly like --name in lesson 4-2. The top-level volumes: block declares the named volume that the service then mounts.

Compose also creates a private network for these services automatically, so no docker network create is needed.

docker-compose.yml one file, in git up -d network shop_default (auto-created) app build: . db postgres:16 volume dbdata port 8080 to you
One compose file describes the whole stack. A single up command creates the network, the volume, and both containers in the right order.

Why the db hostname resolves

The app's DATABASE_URL can point at host db because compose puts all services on a shared network where each service name is a DNS hostname, exactly as in lesson 4-2.

Compose auto-creates a network and connects every service to it, and service names become hostnames just like container names on a user-defined network.

Compose service nameHostname other services use
dbdb
databasedatabase

Renaming the service to database forces the URL to change to match, which is a real source of confusion in reviews. The name in the compose file is not decoration, it is the DNS record.

build against image

The app service says build: . while db says image: postgres:16, and the difference is where the image comes from.

build: makes compose build the image from your local Dockerfile, running the unit 3 docker build for you. image: pulls a ready-made image from a registry.

KeySource of the imageTypical use
build: .your Dockerfileyour own code
image: postgres:16a registrystandard software

Your own code needs building, while standard software such as Postgres ships as a prebuilt image you simply pull. Most real compose files mix both, exactly like this one.

The filename compose looks for

The conventional filename is docker-compose.yml, and the newer compose.yaml is also accepted.

It is YAML, so it ends in .yml or .yaml, and the classic name starts with docker-compose.

FilenameAccepted
docker-compose.ymlyes, the classic
compose.yamlyes, the newer form
docker-compose.yamlyes

Compose reads it from the current directory when docker compose up runs, so the whole app definition travels inside the repo. Anyone who clones the project gets the deployment topology along with the code.