Introduction to Docker Compose
Docker Compose is a tool for defining and running multi-container Docker applications. Instead of managing containers individually, you describe your entire application stack in a single file.
Why Docker Compose?
Without Docker Compose, starting a multi-container app requires multiple commands:
# Create network
docker network create myapp
# Start database
docker run -d \
--name postgres \
--network myapp \
-e POSTGRES_PASSWORD=secret \
-v postgres-data:/var/lib/postgresql/data \
postgres:15
# Start Redis
docker run -d \
--name redis \
--network myapp \
redis:alpine
# Start API
docker run -d \
--name api \
--network myapp \
-e DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb \
-e REDIS_URL=redis://redis:6379 \
-p 3000:3000 \
myapi
With Docker Compose, one file and one command:
# docker-compose.yml
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:alpine
api:
image: myapi
environment:
DATABASE_URL: postgresql://postgres:secret@postgres:5432/mydb
REDIS_URL: redis://redis:6379
ports:
- "3000:3000"
depends_on:
- postgres
- redis
volumes:
postgres-data:
# Start everything
docker compose up -d
Installing Docker Compose
Docker Compose V2 comes bundled with Docker Desktop. For Linux:
# Check if installed
docker compose version
# Install as Docker plugin (Linux)
sudo apt-get update
sudo apt-get install docker-compose-plugin
# Or standalone binary
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
V1 vs V2
| Feature | V1 (Legacy) | V2 (Current) |
|---|---|---|
| Command | docker-compose | docker compose |
| Installation | Separate binary | Docker CLI plugin |
| Performance | Slower | Faster |
| Build | Python-based | Go-based |
This course uses V2 syntax (docker compose).
Basic Compose File Structure
# docker-compose.yml (or compose.yml)
# Optional: Compose file version (mostly deprecated in V2)
version: "3.8"
# Define your services (containers)
services:
service-name:
image: image-name:tag
# ... service configuration
# Define named volumes
volumes:
volume-name:
# Define custom networks
networks:
network-name:
Your First Compose File
Let's create a simple web application with Nginx:
# docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
# Create content directory
mkdir html
echo "<h1>Hello from Docker Compose!</h1>" > html/index.html
# Start the service
docker compose up -d
# Visit http://localhost:8080
Essential Compose Commands
Starting Services
# Start all services
docker compose up
# Start in detached mode (background)
docker compose up -d
# Start specific services
docker compose up -d web api
# Build and start
docker compose up -d --build
# Force recreate containers
docker compose up -d --force-recreate
Stopping Services
# Stop all services
docker compose down
# Stop and remove volumes
docker compose down -v
# Stop and remove images
docker compose down --rmi all
# Stop without removing
docker compose stop
Viewing Status
# List running services
docker compose ps
# List all services (including stopped)
docker compose ps -a
# View logs
docker compose logs
# Follow logs
docker compose logs -f
# Logs for specific service
docker compose logs -f api
Other Commands
# Execute command in service
docker compose exec api bash
# Run one-off command
docker compose run --rm api npm test
# Restart services
docker compose restart
# Pull latest images
docker compose pull
# Build images
docker compose build
Project Name
Docker Compose prefixes resources with a project name:
# Default: directory name
# In /home/user/myapp:
docker compose up
# Creates: myapp_web_1, myapp_postgres_1, etc.
# Custom project name
docker compose -p customname up
# Creates: customname_web_1, etc.
# Or via environment variable
COMPOSE_PROJECT_NAME=customname docker compose up
File Naming
Docker Compose looks for these files by default:
compose.yml(preferred)compose.yamldocker-compose.ymldocker-compose.yaml
# Use specific file
docker compose -f docker-compose.prod.yml up
# Multiple files (merged)
docker compose -f docker-compose.yml -f docker-compose.override.yml up
Complete Example
A typical web application stack:
# docker-compose.yml
services:
# Frontend
web:
build: ./frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:4000
depends_on:
- api
# Backend API
api:
build: ./backend
ports:
- "4000:4000"
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
# Database
db:
image: postgres:15
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
# Cache
redis:
image: redis:alpine
volumes:
- redis-data:/data
volumes:
postgres-data:
redis-data:
# Start everything
docker compose up -d
# View running services
docker compose ps
# Check logs
docker compose logs -f api
# Stop everything
docker compose down
Benefits of Docker Compose
- Single Source of Truth: All configuration in one file
- Reproducibility: Same environment everywhere
- Simplicity: One command to start/stop
- Version Control: Track infrastructure changes
- Developer Onboarding: New developers run
docker compose up - Environment Parity: Match dev, staging, production
Key Takeaways
- Docker Compose manages multi-container applications
- Define services, networks, and volumes in a YAML file
- Use
docker compose up -dto start,docker compose downto stop - Services can reference each other by name
- Compose handles networking automatically
depends_onspecifies startup order (but not readiness)- Use
-dfor detached mode in development - The project name prefixes all created resources

