Docker Compose Guide¶
Navigation: Home → Deployment → Docker Compose
Overview¶
Docker Compose simplifies multi-container Docker applications by defining services, networks, and volumes in a single YAML file. This guide covers the docker-compose.yml configuration for the Simon Stijnen Portfolio and explores advanced multi-service setups.
Table of Contents¶
- Quick Start
- Configuration Breakdown
- Common Operations
- Advanced Configurations
- Multi-Service Examples
- Environment Management
- Troubleshooting
Quick Start¶
Prerequisites¶
- Docker Compose v2.0+ (bundled with Docker Desktop)
- Docker Engine 20.10+
One-Command Deployment¶
# Start services in background
docker compose up -d
# View logs
docker compose logs -f
# Stop services
docker compose down
# Rebuild and restart
docker compose up -d --build
Configuration Breakdown¶
Current Configuration¶
name: personal-website
services:
website:
container_name: personal-website
build:
context: .
restart: unless-stopped
ports:
- "3000:3000"
Line-by-Line Explanation¶
Project Name¶
Purpose:
- Sets project name for all resources
- Prefixes network, volume, and container names
- Used by
docker composecommands
Effect:
# Without name: directory-based prefix
docker compose up
# Creates: website-website-1
# With name: explicit prefix
docker compose up
# Creates: personal-website
Service Definition¶
- Defines a service named
website - Can have multiple services (web, database, cache, etc.)
- Each service represents a container
Container Name¶
Purpose:
- Sets explicit container name
- Overrides default naming (
<project>-<service>-<number>) - Makes container easier to reference
With vs Without:
| Configuration | Container Name |
|---|---|
Without container_name |
personal-website-website-1 |
With container_name |
personal-website |
Note: Cannot scale service with explicit container name:
Build Configuration¶
Simple Form:
Extended Form:
build:
context: . # Build context directory
dockerfile: Dockerfile # Dockerfile path (optional, defaults to Dockerfile)
args: # Build arguments (optional)
NODE_VERSION: 24
target: runner # Target stage in multi-stage build (optional)
cache_from: # Cache sources (optional)
- personal-website:latest
Build Context:
.= current directory- Path relative to docker-compose.yml location
- Contains Dockerfile and source code
Restart Policy¶
Available Policies:
| Policy | Behavior | Use Case |
|---|---|---|
no |
Never restart | Development, debugging |
always |
Always restart, even after Docker daemon restart | Critical services |
unless-stopped |
Restart unless manually stopped | Recommended for production |
on-failure |
Restart only on non-zero exit code | Retry on errors |
on-failure:3 |
Restart maximum 3 times | Prevent restart loops |
Examples:
# Always restart (even if manually stopped)
restart: always
# Restart only on failure, max 5 times
restart: on-failure:5
# Never restart (development)
restart: "no" # Quotes required for 'no'
Port Mapping¶
Format: "HOST:CONTAINER" or "HOST_IP:HOST_PORT:CONTAINER_PORT"
Examples:
# Map different host port
ports:
- "8080:3000" # Access at http://localhost:8080
# Bind to specific interface
ports:
- "127.0.0.1:3000:3000" # Only localhost access
# Multiple ports
ports:
- "3000:3000" # HTTP
- "9229:9229" # Node.js debugger
# Random host port
ports:
- "3000" # Docker assigns random port
# Port range
ports:
- "3000-3002:3000-3002"
Security Note:
# ❌ Exposed to internet (if server has public IP)
ports:
- "3000:3000"
# ✅ Local only
ports:
- "127.0.0.1:3000:3000"
Common Operations¶
Starting Services¶
# Start in background (detached)
docker compose up -d
# Start with logs visible
docker compose up
# Start specific service
docker compose up -d website
# Force recreate containers
docker compose up -d --force-recreate
# Rebuild images before starting
docker compose up -d --build
# Pull latest images before starting
docker compose up -d --pull always
Stopping Services¶
# Stop containers (preserves containers)
docker compose stop
# Stop and remove containers
docker compose down
# Stop and remove with volumes
docker compose down -v
# Stop and remove with images
docker compose down --rmi all
# Stop specific service
docker compose stop website
Viewing Logs¶
# Follow all logs
docker compose logs -f
# Follow specific service
docker compose logs -f website
# Last 100 lines
docker compose logs --tail=100
# Since timestamp
docker compose logs --since 2024-01-01T00:00:00
# With timestamps
docker compose logs -t -f
Executing Commands¶
# Execute command in running service
docker compose exec website sh
# Run as root
docker compose exec -u root website sh
# Execute one-off command
docker compose exec website node --version
# Run command in new container
docker compose run --rm website npm test
Service Management¶
# List running services
docker compose ps
# List all containers (including stopped)
docker compose ps -a
# View service configuration
docker compose config
# Validate configuration
docker compose config --quiet
# View service logs
docker compose logs website
# Restart service
docker compose restart website
# Pause/Unpause (suspend execution)
docker compose pause website
docker compose unpause website
Building Images¶
# Build all services
docker compose build
# Build specific service
docker compose build website
# Build with no cache
docker compose build --no-cache
# Build with progress
docker compose build --progress plain
# Pull base images before building
docker compose build --pull
Advanced Configurations¶
Complete Configuration Example¶
name: personal-website
services:
website:
container_name: personal-website
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: 24
target: runner
image: personal-website:latest
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
- NEXT_PUBLIC_SITE_URL=${SITE_URL}
env_file:
- .env.production
volumes:
- ./logs:/app/logs
- website-cache:/app/.next/cache
networks:
- web
depends_on:
database:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: "2"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
web:
driver: bridge
volumes:
website-cache:
driver: local
Environment Variables¶
Inline Environment¶
Environment File¶
.env file:
Variable Substitution¶
services:
website:
environment:
- SITE_URL=${SITE_URL:-https://localhost:3000}
- VERSION=${VERSION}
ports:
- "${HOST_PORT:-3000}:3000"
Usage:
# Use default values
docker compose up -d
# Override with environment variables
HOST_PORT=8080 SITE_URL=https://example.com docker compose up -d
# Use .env file
echo "HOST_PORT=8080" > .env
docker compose up -d
Volumes¶
Named Volumes¶
services:
website:
volumes:
- website-data:/app/data
- logs:/app/logs
volumes:
website-data:
driver: local
logs:
driver: local
Bind Mounts¶
services:
website:
volumes:
# Absolute path
- /host/path:/container/path
# Relative path
- ./local/path:/app/data
# Read-only mount
- ./config:/app/config:ro
# Named volume
- data-volume:/app/data
Volume Options¶
volumes:
website-data:
driver: local
driver_opts:
type: none
o: bind
device: /path/on/host
postgres-data:
driver: local
driver_opts:
type: nfs
o: addr=10.0.0.1,rw
device: ":/exported/path"
Networks¶
Multiple Networks¶
services:
website:
networks:
- frontend
- backend
database:
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external access
Network Aliases¶
External Networks¶
Health Checks¶
services:
website:
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
database:
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
timeout: 5s
retries: 5
Dependencies¶
services:
website:
depends_on:
database:
condition: service_healthy
redis:
condition: service_started
database:
healthcheck:
test: ["CMD", "pg_isready"]
interval: 5s
Conditions:
service_started: Wait for container to start (default)service_healthy: Wait for health check to passservice_completed_successfully: Wait for one-off task
Multi-Service Examples¶
Website + PostgreSQL¶
name: personal-website
services:
website:
container_name: personal-website
build: .
restart: unless-stopped
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@database:5432/portfolio
depends_on:
database:
condition: service_healthy
networks:
- app-network
database:
container_name: personal-website-db
image: postgres:16-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=portfolio
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres-data:
Website + Redis Cache¶
name: personal-website
services:
website:
container_name: personal-website
build: .
restart: unless-stopped
ports:
- "3000:3000"
environment:
- REDIS_URL=redis://cache:6379
depends_on:
- cache
networks:
- app-network
cache:
container_name: personal-website-cache
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
redis-data:
Full Stack with Reverse Proxy¶
name: personal-website
services:
nginx:
container_name: nginx-proxy
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- website
networks:
- web
website:
container_name: personal-website
build: .
restart: unless-stopped
expose:
- "3000"
environment:
- DATABASE_URL=postgresql://postgres:password@database:5432/portfolio
- REDIS_URL=redis://cache:6379
depends_on:
database:
condition: service_healthy
cache:
condition: service_healthy
networks:
- web
- backend
database:
container_name: postgres-db
image: postgres:16-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=portfolio
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- backend
cache:
container_name: redis-cache
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- backend
networks:
web:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
postgres-data:
redis-data:
Environment Management¶
Multiple Environment Files¶
# Directory structure
.
├── docker-compose.yml # Base configuration
├── docker-compose.dev.yml # Development overrides
├── docker-compose.prod.yml # Production overrides
├── .env.development # Dev environment variables
└── .env.production # Prod environment variables
Base Configuration¶
docker-compose.yml:
Development Override¶
docker-compose.dev.yml:
services:
website:
build:
target: development
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
command: npm run dev
Usage:
# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# Shorthand with COMPOSE_FILE
export COMPOSE_FILE=docker-compose.yml:docker-compose.dev.yml
docker compose up -d
Production Override¶
docker-compose.prod.yml:
services:
website:
restart: always
environment:
- NODE_ENV=production
deploy:
resources:
limits:
cpus: "2"
memory: 1G
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Usage:
Profile-Based Configuration¶
name: personal-website
services:
website:
build: .
ports:
- "3000:3000"
database:
image: postgres:16-alpine
profiles:
- full-stack
cache:
image: redis:7-alpine
profiles:
- full-stack
Usage:
# Start only website
docker compose up -d
# Start website + database + cache
docker compose --profile full-stack up -d
Troubleshooting¶
Common Issues¶
Port Already in Use¶
# Error: "port is already allocated"
# Find process using port
lsof -i :3000
# Use different port
docker compose up -d
# Edit docker-compose.yml:
# ports:
# - "8080:3000"
Service Won't Start¶
# View detailed logs
docker compose logs website
# Check service status
docker compose ps
# View configuration
docker compose config
# Validate syntax
docker compose config --quiet
Build Failures¶
# Rebuild without cache
docker compose build --no-cache
# View build output
docker compose build --progress plain
# Build specific service
docker compose build website
Network Issues¶
# Recreate networks
docker compose down
docker compose up -d
# Inspect network
docker network inspect personal-website_default
# Test connectivity between services
docker compose exec website ping database
Debugging Commands¶
# View all containers (including stopped)
docker compose ps -a
# View resource usage
docker compose stats
# Execute shell in container
docker compose exec website sh
# View service configuration
docker compose config --services
# View volumes
docker compose config --volumes
# View networks
docker compose config --networks
Logs and Monitoring¶
# Follow logs with timestamps
docker compose logs -f -t
# Filter logs by service
docker compose logs -f website
# Last N lines
docker compose logs --tail=50 website
# Since specific time
docker compose logs --since="2024-01-01T00:00:00"
# Export logs
docker compose logs --no-color > logs.txt
See Also¶
- Docker Guide - Comprehensive Docker reference
- Dockerfile Documentation - Multi-stage build details
- CI/CD Pipeline - Automated deployments
- Production Configuration - Production best practices
Next Steps¶
- Set Up Multi-Service: Add database or cache using examples above
- Environment Management: Configure development and production overrides
- Automation: Integrate with CI/CD Pipeline
- Production Deploy: Review Deployment Strategies
Last Updated: February 2026
Docker Compose Version: v2.24+
Compose File Version: 3.8 (implied, version key removed in v2)
Compatibility: Docker Compose v2.0+