One container is not an environment
The previous course ended with an image that runs this application. That is one process. A working environment for this app is at least two, because the app needs PostgreSQL, and a realistic one adds a job worker and sometimes more.
Run those by hand and the session looks like this:
docker network create nor_net
docker volume create nor_pgdata
docker run -d --name nor_db --network nor_net \
-v nor_pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=secret postgres:17
docker run -d --name nor_web --network nor_net -p 3000:80 \
-e DATABASE_URL=postgres://postgres:secret@nor_db/newbie_on_rails_development \
-e RAILS_MASTER_KEY=... newbie_on_rails
It works, but nobody can work that way. Nobody remembers it, nobody can review it, and a colleague joining the project has to get it in a message instead of reading it in the repository.
What Compose is
Compose is that session, written down as a file and executed by one command:
docker compose up
One file describes the services, the network between them, the volumes that outlive them and the configuration each receives. It is checked into the repository, so the environment is reviewable in a pull request like anything else.
Note the command: docker compose, two words. The old docker-compose with a hyphen is the original standalone Python tool. Current Compose is a plugin of the Docker CLI. Both may work on your machine, and the hyphenated one is the one that will eventually stop working.
What it is not
Two boundaries are worth setting right away, because both are common misunderstandings.
First, Compose runs containers on one machine. It has no scheduling across hosts, no rolling deploys and no automatic failover, so it is no production orchestrator. For this application, production is Kamal (the next course), and Compose is the development environment.
Second, Compose works alongside your Dockerfile. It builds or pulls images, and the Dockerfile from the previous course still defines the image. Everything you learned there still applies. Compose is the layer that says how instances of those images relate.
flowchart TB
D["Dockerfile: how one image is built"] --> I["Image"]
I --> C["compose.yaml: how services relate"]
C --> S1["web"]
C --> S2["db"]
C --> S3["worker"]
The smallest useful file
For this application:
name: newbie_on_rails
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
web:
build: .
command: ./bin/rails server -b 0.0.0.0
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://postgres:secret@db/newbie_on_rails_development
depends_on:
- db
volumes:
pgdata:
Every line of the hand-typed session is in there, and three things now come free. Services find each other by name: web reaches PostgreSQL at the hostname db, with no addresses anywhere. The network is created automatically. And the whole environment starts, stops and is destroyed as a unit. The file is still a sketch: it runs the image with its production settings, and this app has three more databases (cache, queue and cable) that DATABASE_URL does not cover. Lesson 7 builds the version that boots.
The file name matters
Compose looks for these names, in this order:
compose.yaml ← the canonical name, prefer this
compose.yml
docker-compose.yaml ← kept for backward compatibility
docker-compose.yml ← kept for backward compatibility
Most tutorials say docker-compose.yml, because that was the name for years. compose.yaml is the current one. If both exist, Compose prefers the canonical file, and that situation is confusing to debug, so keep only one.
What this course covers
- One container is not an environment (you are here).
- The file: services, images and what changed.
- Networking and start order.
- Data that survives a restart.
- Configuration and secrets.
- Working in it day to day.
- Putting it together: a Compose file for this app.
By the end you will have a compose.yaml that starts this application and its database with one command, and you will know which of its lines carry the weight.
Key takeaways
- A working environment is several containers. You can run them by hand, but you cannot work that way.
- Compose writes that session down as a file in the repository, started with
docker compose up. - Use
docker compose(two words); the hyphenateddocker-composeis the older standalone tool. - Compose is a development tool for one machine. It is no production orchestrator, and it does not replace your Dockerfile.
- Prefer the canonical
compose.yaml; thedocker-compose.ymlname in most tutorials is kept only for compatibility.
Keep going with lesson 2
Services, images, and what changed
The obsolete version key, top-level elements, build versus image, command overrides, binding to 0.0.0.0 and YAML anchors.
A free account also gives you the quizzes, your progress and notes, and the discussion.