Docker Compose Tutorial: From Zero to Multi-Container Apps
Docker Compose Tutorial: From Zero to Multi-Container Apps
Docker Compose takes you from docker run incantations to describing a whole application stack in one YAML file. This tutorial goes from zero to running a multi-container app with networking, volumes, and environments.
Table of Contents
- Install Compose
- Your first compose file
- A real stack: app + database + cache
- Key concepts, explained fast
- Daily-driver commands
- Override files for environments
- Where to go next
Install Compose
Compose v2 ships as a Docker plugin — no separate install on Docker Desktop. On Linux servers:
docker compose version # should print v2.x
# if missing, on Debian/Ubuntu:
sudo apt-get install docker-compose-plugin
Your first compose file
Create compose.yaml (or the legacy docker-compose.yml):
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
docker compose up -d # start in background
docker compose ps # what's running
docker compose logs -f # stream logs
docker compose down # stop and remove containers
A real stack: app + database + cache
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/appdb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- dbdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 3s
retries: 10
cache:
image: redis:7-alpine
volumes:
dbdata:
Key concepts, explained fast
- Services = containers, defined declaratively. Each service gets a DNS name matching its key — db, cache — which is how app finds them.
- Networks — Compose creates a private network per project; services reach each other by name, nothing is exposed to the internet unless you publish a port.
- Volumes — named volumes persist data across down/up. Anonymous container storage does not.
- depends_on + healthcheck — controls start order; healthy conditions prevent race conditions like the app crashing before Postgres is ready.
- build: . — builds a local Dockerfile; combine with image: to name the result.
Daily-driver commands
docker compose up -d --build # rebuild changed images
docker compose exec db psql -U app appdb # shell into a service
docker compose restart app # restart one service
docker compose logs -f --tail=50 app
docker compose down -v # also delete volumes (careful!)
Override files for environments
Compose merges compose.yaml with compose.override.yaml automatically — keep dev tweaks there. For prod, use docker compose -f compose.yaml -f compose.prod.yaml up -d to layer explicit overrides.
Where to go next
When one file isn't enough: profiles (profiles: [debug]), secrets via secrets:, resource limits, and eventually our guide on running Compose in production. Compose scales to a surprisingly capable single-host platform before you ever need an orchestrator.
Related Articles
- Dockerfile Best Practices: Building Small, Secure Images
- Running WordPress on Docker Compose in Production
Last Updated: October 2026 Author: CloudOpsGuide Team Difficulty: Beginner Estimated Reading Time: 10 minutes