20.2.1 Compose Learning Step
A focused guide to Compose Learning Step, connecting core concepts with practical Docker and container operations.
Docker Compose is the tool that brings multiple containers together as a single, coordinated application. The Compose learning step teaches you to define multi-service applications in a declarative file, start and stop them with single commands, and understand how Compose manages the networking, volumes, and dependencies between services.
What Docker Compose Does
Running multiple containers manually means executing separate docker run commands for each service, creating networks by hand, passing connection parameters as environment variables, and remembering the exact flags for each container. Docker Compose replaces all of that with a single compose.yml file that describes every service, its configuration, its connections to other services, and its volumes — and then starts everything with one command.
Compose is included with Docker Desktop on Mac and Windows. On Linux, it is installed as the docker compose plugin.
The compose.yml File
Compose reads a file named compose.yml (or docker-compose.yml for the legacy version) in the current directory. This file uses YAML to define services:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
This file defines two services: a web server and a PostgreSQL database. The volumes section at the top level declares the named volume db_data that Docker manages.
Starting the Application
docker compose up -d
Compose reads compose.yml, creates the services' containers, connects them to a shared network, and starts them. The -d flag runs them in the background (detached mode).
On first run, Compose pulls any images not already present locally. Subsequent runs use cached images unless the image tags have changed.
Verifying Services Are Running
docker compose ps
NAME IMAGE STATUS PORTS
project-db-1 postgres:15 running 5432/tcp
project-web-1 nginx:alpine running 0.0.0.0:8080->80/tcp
Each service is named with the project name prefix (defaulting to the directory name) and a numeric suffix.
Built-In DNS Between Services
Compose creates a private network for the application and configures DNS so that each service is reachable by its service name. A container in the web service can connect to the database at hostname db on port 5432 — Docker resolves db to the database container's IP address automatically.
No manual network creation, no hardcoded IP addresses.
Building Application Images
When a service uses a Dockerfile instead of a prebuilt image, use the build key:
services:
web:
build: .
ports:
- "3000:3000"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
build: . tells Compose to run docker build in the current directory to create the web service image. The image is rebuilt only when you explicitly request it.
To rebuild images:
docker compose build
Or rebuild and restart in one step:
docker compose up -d --build
Service Dependencies
Services often have startup order requirements: the web server should not start until the database is ready. Use depends_on to declare ordering:
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
With condition: service_healthy, Compose waits until the db service's healthcheck passes before starting web. The healthcheck runs pg_isready to verify PostgreSQL is accepting connections.
Viewing Logs
docker compose logs
Shows aggregated logs from all services, prefixed with the service name.
docker compose logs -f web
Streams live logs from the web service only.
Stopping and Removing
Stop running containers without removing them:
docker compose stop
Stop and remove containers, networks, and anonymous volumes:
docker compose down
Remove containers, networks, and named volumes:
docker compose down -v
The -v flag removes named volumes, which deletes persisted data. Use it with care.
Environment Variables in Compose
Compose supports a .env file in the same directory as compose.yml for variable substitution:
.env file:
POSTGRES_PASSWORD=mysecret
APP_PORT=3000
compose.yml:
services:
web:
build: .
ports:
- "${APP_PORT}:3000"
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
Compose reads .env automatically and substitutes variables in the YAML. Sensitive values stay out of the committed compose.yml file.
Multiple Compose Files
Compose supports merging multiple files for environment-specific overrides:
docker compose -f compose.yml -f compose.override.yml up -d
compose.override.yml is automatically loaded alongside compose.yml if it exists in the same directory. This pattern keeps production and development configurations separate: the base compose.yml defines services, and compose.override.yml adds development-specific volume mounts and debug settings.
The Architecture Compose Establishes
All services share a private network. The web service reaches db by hostname. The web service publishes port 8080 to the host. The database port is not exposed to the host — it is only accessible from within the Compose network.
Running Commands Inside a Service
docker compose exec web sh
Opens a shell inside the running web service container. Useful for inspecting the filesystem, running database migrations, or debugging connection issues from inside the container.
docker compose exec db psql -U postgres
Opens a PostgreSQL prompt inside the db service container.
Compose in Development vs Production
For local development, Compose is the standard way to run the full application stack on a developer's machine with a single command. For production, Compose is suitable for single-host deployments. For multi-host deployments with high availability, Kubernetes or Docker Swarm take over the orchestration role.