Learn to define and manage multi-container applications using Docker Compose, including service configuration, networking, volumes, and environment management.
Docker Compose is a tool for defining and running multi-container Docker applications. With a single YAML file, you can configure all your application services, networks, and volumes, then create and start everything with a single command. Docker Compose simplifies development workflows, enables reproducible environments, and serves as documentation for your application architecture. This lesson covers the complete Docker Compose specification and best practices for real-world applications.
A docker-compose.yml file defines your entire application stack:
Basic Structure:
1# Compose file format version (optional in v2)
2version: "3.9"
3
4# Service definitions
5services:
6 web:
7 image: nginx:alpine
8 ports:
9 - "80:80"
10
11 api:
12 build: ./api
13 ports:
14 - "3000:3000"
15
16# Network definitions
17networks:
18 frontend:
19 backend:
20
21# Volume definitions
22volumes:
23 db-data:
24
25# Secrets (for Swarm mode)
26secrets:
27 db_password:
28 file: ./secrets/db_password.txt
29
30# Configs (for Swarm mode)
31configs:
32 nginx_config:
33 file: ./nginx.confService Configuration Options:
1services:
2 myservice:
3 # Image or build
4 image: nginx:alpine
5 # OR
6 build:
7 context: ./app
8 dockerfile: Dockerfile
9 args:
10 NODE_ENV: production
11
12 # Container name (optional)
13 container_name: my-nginx
14
15 # Command override
16 command: ["nginx", "-g", "daemon off;"]
17 # OR entrypoint override
18 entrypoint: /docker-entrypoint.sh
19
20 # Environment
21 environment:
22 - NODE_ENV=production
23 - DB_HOST=postgres
24 # OR from file
25 env_file:
26 - .env
27 - .env.local
28
29 # Port mapping
30 ports:
31 - "8080:80" # host:container
32 - "127.0.0.1:9000:9000" # bind to localhost
33 - "3000" # random host port
34
35 # Volumes
36 volumes:
37 - ./src:/app/src # bind mount
38 - data:/app/data # named volume
39 - /app/node_modules # anonymous volume
40
41 # Networks
42 networks:
43 - frontend
44 - backend
45
46 # Dependencies
47 depends_on:
48 - db
49 - redis
50
51 # Restart policy
52 restart: unless-stopped
53
54 # Resource limits
55 deploy:
56 resources:
57 limits:
58 cpus: '0.5'
59 memory: 512MControl service startup order and health monitoring:
Basic Dependencies:
1services:
2 web:
3 image: nginx
4 depends_on:
5 - api
6 - db
7
8 api:
9 image: myapi
10 depends_on:
11 - db
12 - redis
13
14 db:
15 image: postgres
16
17 redis:
18 image: redisConditional Dependencies (Compose v2.1+):
1services:
2 web:
3 image: nginx
4 depends_on:
5 api:
6 condition: service_healthy
7 db:
8 condition: service_started
9 init:
10 condition: service_completed_successfully
11
12 api:
13 image: myapi
14 healthcheck:
15 test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
16 interval: 30s
17 timeout: 10s
18 retries: 3
19 start_period: 40s
20
21 db:
22 image: postgres
23 healthcheck:
24 test: ["CMD-SHELL", "pg_isready -U postgres"]
25 interval: 10s
26 timeout: 5s
27 retries: 5
28
29 init:
30 image: busybox
31 command: echo "Initialization complete"Health Check Options:
1services:
2 api:
3 image: myapi
4 healthcheck:
5 # Test command
6 test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
7 # OR shell form
8 test: curl -f http://localhost:3000/health || exit 1
9
10 # Check interval
11 interval: 30s
12
13 # Timeout for each check
14 timeout: 10s
15
16 # Number of retries before unhealthy
17 retries: 3
18
19 # Grace period before checks start
20 start_period: 60s
21
22 # Disable inherited healthcheck
23 # disable: trueStartup Order Visualization:
1depends_on order:
2
3 init ─────────────────────────┐
4 │ │
5 ▼ │
6 db ──────┬─────────┐ │
7 │ │ │ │
8 redis ────┤ │ │
9 │ │ │ │
10 ▼ ▼ ▼ │
11 api ────────────► web ◄──────┘
12
13Conditions:
14- service_started: Container is running
15- service_healthy: Health check passes
16- service_completed_successfully: Exit code 0Define custom networks and volumes for your application:
Network Configuration:
1services:
2 web:
3 networks:
4 - frontend
5 api:
6 networks:
7 - frontend
8 - backend
9 db:
10 networks:
11 - backend
12
13networks:
14 frontend:
15 driver: bridge
16
17 backend:
18 driver: bridge
19 internal: true # No external access
20 ipam:
21 config:
22 - subnet: 172.28.0.0/16
23
24 external-net:
25 external: true # Pre-existing network
26 name: my-existing-networkNetwork Aliases:
1services:
2 api:
3 networks:
4 backend:
5 aliases:
6 - api-service
7 - backend-api
8 ipv4_address: 172.28.0.10
9
10networks:
11 backend:
12 ipam:
13 config:
14 - subnet: 172.28.0.0/16Volume Configuration:
1services:
2 db:
3 volumes:
4 # Named volume
5 - db-data:/var/lib/postgresql/data
6
7 # Bind mount
8 - ./init:/docker-entrypoint-initdb.d:ro
9
10 # Anonymous volume
11 - /var/lib/postgresql/data
12
13 # tmpfs mount
14 - type: tmpfs
15 target: /tmp
16 tmpfs:
17 size: 100M
18
19volumes:
20 db-data:
21 driver: local
22 driver_opts:
23 type: none
24 device: /data/postgres
25 o: bind
26 labels:
27 backup: daily
28 environment: production
29
30 # External volume
31 shared-data:
32 external: true
33 name: my-shared-volumeLong-Form Volume Syntax:
1services:
2 web:
3 volumes:
4 - type: bind
5 source: ./html
6 target: /usr/share/nginx/html
7 read_only: true
8
9 - type: volume
10 source: nginx-cache
11 target: /var/cache/nginx
12 volume:
13 nocopy: true
14
15 - type: tmpfs
16 target: /tmp
17 tmpfs:
18 size: 100000000 # 100MBManage environment variables and application configuration:
Environment Variables:
1services:
2 api:
3 # Inline environment
4 environment:
5 - NODE_ENV=production
6 - DB_HOST=postgres
7 - DB_PORT=5432
8 # Variable from host
9 - API_KEY
10 # With default
11 - LOG_LEVEL=${LOG_LEVEL:-info}
12
13 # From file(s)
14 env_file:
15 - .env
16 - .env.local
17 - .env.${ENVIRONMENT:-development}.env File:
1# .env file
2POSTGRES_USER=app
3POSTGRES_PASSWORD=secret123
4POSTGRES_DB=myapp
5
6# Used in docker-compose.yml
7DB_HOST=postgres
8API_PORT=3000Variable Substitution:
1services:
2 api:
3 image: myapi:${TAG:-latest}
4 environment:
5 - DB_HOST=${DB_HOST:?DB_HOST is required}
6 - LOG_LEVEL=${LOG_LEVEL:-info}
7 - REPLICAS=${REPLICAS:-1}
8
9# Syntax:
10# ${VAR} - Value of VAR
11# ${VAR:-default} - Default if VAR is unset or empty
12# ${VAR-default} - Default if VAR is unset
13# ${VAR:?error} - Error if VAR is unset or empty
14# ${VAR?error} - Error if VAR is unsetMultiple Compose Files:
1# Base configuration
2# docker-compose.yml
3
4# Override for development
5# docker-compose.override.yml (auto-loaded)
6
7# Production override
8docker compose -f docker-compose.yml -f docker-compose.prod.yml up
9
10# Or use COMPOSE_FILE environment
11export COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
12docker compose upOverride Example:
1# docker-compose.yml (base)
2services:
3 api:
4 image: myapi:latest
5 environment:
6 - NODE_ENV=production
7
8# docker-compose.override.yml (development)
9services:
10 api:
11 build: ./api
12 volumes:
13 - ./api/src:/app/src
14 environment:
15 - NODE_ENV=development
16 - DEBUG=trueMaster Docker Compose CLI commands for managing your application:
Basic Commands:
1# Start services
2docker compose up
3
4# Start in detached mode
5docker compose up -d
6
7# Start specific services
8docker compose up api db
9
10# Build and start
11docker compose up --build
12
13# Recreate containers
14docker compose up --force-recreate
15
16# Stop services
17docker compose down
18
19# Stop and remove volumes
20docker compose down -v
21
22# Stop and remove images
23docker compose down --rmi allBuild Commands:
1# Build all services
2docker compose build
3
4# Build specific service
5docker compose build api
6
7# Build without cache
8docker compose build --no-cache
9
10# Build with build args
11docker compose build --build-arg NODE_ENV=productionService Management:
1# List running services
2docker compose ps
3
4# View logs
5docker compose logs
6
7# Follow logs
8docker compose logs -f
9
10# Logs for specific service
11docker compose logs -f api
12
13# Execute command in service
14docker compose exec api sh
15
16# Run one-off command
17docker compose run --rm api npm test
18
19# Scale service
20docker compose up -d --scale api=3Maintenance Commands:
1# Pull latest images
2docker compose pull
3
4# Push built images
5docker compose push
6
7# View service configuration
8docker compose config
9
10# Validate compose file
11docker compose config --quiet
12
13# View service images
14docker compose images
15
16# Pause/unpause services
17docker compose pause
18docker compose unpause
19
20# Restart services
21docker compose restart
22
23# View resource usage
24docker compose topProject Configuration:
1# Custom project name
2docker compose -p myproject up
3
4# Custom compose file
5docker compose -f custom-compose.yml up
6
7# Multiple compose files
8docker compose -f base.yml -f override.yml up
9
10# Environment file
11docker compose --env-file .env.production upBuild a production-ready full-stack application with React frontend, Node.js API, PostgreSQL, and Redis.
1# docker-compose.yml
2# Full-Stack Application - Development & Production Ready
3
4version: "3.9"
5
6# ==========================================
7# Services
8# ==========================================
9services:
10 # ----------------------------------------
11 # Frontend - React/Next.js
12 # ----------------------------------------
13 frontend:
14 build:
15 context: ./frontend
16 dockerfile: Dockerfile
17 target: ${BUILD_TARGET:-development}
18 args:
19 - NEXT_PUBLIC_API_URL=${API_URL:-http://localhost:3000}
20 container_name: ${PROJECT_NAME:-app}-frontend
21 ports:
22 - "${FRONTEND_PORT:-8080}:3000"
23 environment:
24 - NODE_ENV=${NODE_ENV:-development}
25 volumes:
26 - ./frontend/src:/app/src:delegated
27 - ./frontend/public:/app/public:delegated
28 - /app/node_modules
29 - /app/.next
30 networks:
31 - frontend
32 depends_on:
33 api:
34 condition: service_healthy
35 restart: unless-stopped
36 healthcheck:
37 test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"]
38 interval: 30s
39 timeout: 10s
40 retries: 3
41
42 # ----------------------------------------
43 # Backend API - Node.js/Express
44 # ----------------------------------------
45 api:
46 build:
47 context: ./api
48 dockerfile: Dockerfile
49 target: ${BUILD_TARGET:-development}
50 container_name: ${PROJECT_NAME:-app}-api
51 ports:
52 - "${API_PORT:-3000}:3000"
53 environment:
54 - NODE_ENV=${NODE_ENV:-development}
55 - PORT=3000
56 - DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@postgres:5432/${POSTGRES_DB:-appdb}
57 - REDIS_URL=redis://redis:6379
58 - JWT_SECRET=${JWT_SECRET:-development-secret}
59 - LOG_LEVEL=${LOG_LEVEL:-debug}
60 volumes:
61 - ./api/src:/app/src:delegated
62 - /app/node_modules
63 networks:
64 - frontend
65 - backend
66 depends_on:
67 postgres:
68 condition: service_healthy
69 redis:
70 condition: service_healthy
71 restart: unless-stopped
72 healthcheck:
73 test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
74 interval: 30s
75 timeout: 10s
76 retries: 3
77 start_period: 40s
78
79 # ----------------------------------------
80 # Background Worker
81 # ----------------------------------------
82 worker:
83 build:
84 context: ./api
85 dockerfile: Dockerfile
86 target: ${BUILD_TARGET:-development}
87 container_name: ${PROJECT_NAME:-app}-worker
88 command: npm run worker
89 environment:
90 - NODE_ENV=${NODE_ENV:-development}
91 - DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@postgres:5432/${POSTGRES_DB:-appdb}
92 - REDIS_URL=redis://redis:6379
93 volumes:
94 - ./api/src:/app/src:delegated
95 - /app/node_modules
96 networks:
97 - backend
98 depends_on:
99 postgres:
100 condition: service_healthy
101 redis:
102 condition: service_healthy
103 restart: unless-stopped
104 profiles:
105 - worker
106
107 # ----------------------------------------
108 # PostgreSQL Database
109 # ----------------------------------------
110 postgres:
111 image: postgres:15-alpine
112 container_name: ${PROJECT_NAME:-app}-postgres
113 ports:
114 - "${POSTGRES_PORT:-5432}:5432"
115 environment:
116 - POSTGRES_USER=${POSTGRES_USER:-app}
117 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-secret}
118 - POSTGRES_DB=${POSTGRES_DB:-appdb}
119 volumes:
120 - postgres-data:/var/lib/postgresql/data
121 - ./database/init:/docker-entrypoint-initdb.d:ro
122 networks:
123 - backend
124 restart: unless-stopped
125 healthcheck:
126 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app} -d ${POSTGRES_DB:-appdb}"]
127 interval: 10s
128 timeout: 5s
129 retries: 5
130
131 # ----------------------------------------
132 # Redis Cache
133 # ----------------------------------------
134 redis:
135 image: redis:7-alpine
136 container_name: ${PROJECT_NAME:-app}-redis
137 ports:
138 - "${REDIS_PORT:-6379}:6379"
139 command: redis-server --appendonly yes
140 volumes:
141 - redis-data:/data
142 networks:
143 - backend
144 restart: unless-stopped
145 healthcheck:
146 test: ["CMD", "redis-cli", "ping"]
147 interval: 10s
148 timeout: 5s
149 retries: 5
150
151 # ----------------------------------------
152 # Nginx Reverse Proxy (Production)
153 # ----------------------------------------
154 nginx:
155 image: nginx:alpine
156 container_name: ${PROJECT_NAME:-app}-nginx
157 ports:
158 - "80:80"
159 - "443:443"
160 volumes:
161 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
162 - ./nginx/certs:/etc/nginx/certs:ro
163 networks:
164 - frontend
165 depends_on:
166 - frontend
167 - api
168 restart: unless-stopped
169 profiles:
170 - production
171
172 # ----------------------------------------
173 # Adminer - Database Management
174 # ----------------------------------------
175 adminer:
176 image: adminer:latest
177 container_name: ${PROJECT_NAME:-app}-adminer
178 ports:
179 - "8081:8080"
180 networks:
181 - backend
182 depends_on:
183 - postgres
184 restart: unless-stopped
185 profiles:
186 - debug
187
188# ==========================================
189# Networks
190# ==========================================
191networks:
192 frontend:
193 driver: bridge
194 name: ${PROJECT_NAME:-app}-frontend
195
196 backend:
197 driver: bridge
198 internal: true
199 name: ${PROJECT_NAME:-app}-backend
200
201# ==========================================
202# Volumes
203# ==========================================
204volumes:
205 postgres-data:
206 name: ${PROJECT_NAME:-app}-postgres-data
207
208 redis-data:
209 name: ${PROJECT_NAME:-app}-redis-data
210
211# ==========================================
212# Usage Examples
213# ==========================================
214# Development:
215# docker compose up -d
216#
217# With worker:
218# docker compose --profile worker up -d
219#
220# Production:
221# docker compose --profile production up -d
222#
223# With debug tools:
224# docker compose --profile debug up -d
225#
226# Full stack:
227# docker compose --profile worker --profile debug up -d
228#
229# Build and start:
230# docker compose up -d --build
231#
232# View logs:
233# docker compose logs -f api
234#
235# Execute in container:
236# docker compose exec api npm run migrate
237#
238# Stop and cleanup:
239# docker compose down -vYour progress is saved in this browser, on this device only.