Docker Compose
Language Overview¶
Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. This guide covers Docker Compose best practices for maintainable, production-ready container orchestration.
Key Characteristics¶
- File Name:
docker-compose.ymlordocker-compose.yaml - Format: YAML
- Primary Use: Multi-container applications, development environments, testing
- Version: Compose file format version 3.8+ (Docker Compose V2)
Quick Reference¶
| Category | Convention | Example | Notes |
|---|---|---|---|
| File Naming | |||
| Development | docker-compose.yml |
docker-compose.yml |
Default compose file |
| Production | docker-compose.prod.yml |
docker-compose.prod.yml |
Production overrides |
| Testing | docker-compose.test.yml |
docker-compose.test.yml |
Test environment |
| Top-Level Keys | |||
version |
Compose file version | version: "3.8" |
File format version |
services |
Container definitions | Service configurations | Required |
networks |
Network definitions | Custom networks | Optional |
volumes |
Volume definitions | Persistent storage | Optional |
| Service Configuration | |||
image |
Container image | image: node:20-alpine |
Docker image to use |
build |
Build configuration | build: ./app |
Build from Dockerfile |
ports |
Port mapping | ports: ["3000:3000"] |
Host:container |
environment |
Environment vars | NODE_ENV: production |
Container env vars |
volumes |
Mount points | ./src:/app/src |
Host:container paths |
depends_on |
Service dependencies | depends_on: [db] |
Start order |
networks |
Network assignment | networks: [frontend] |
Attach to networks |
| Best Practices | |||
| Version Pinning | Pin image versions | node:20.10.0-alpine |
Avoid latest tag |
.env Files |
Use env files | .env for secrets |
Never commit secrets |
| Health Checks | Define health checks | healthcheck: {...} |
Service readiness |
| Resource Limits | Set limits | mem_limit, cpus |
Prevent resource exhaustion |
| Common Patterns | |||
| Web + DB | Multi-tier apps | web + db services |
Standard pattern |
| Dev Overrides | Use multiple files | -f compose.yml -f dev.yml |
Layer configurations |
| Secrets | Use secrets (v3.1+) | secrets: block |
Secure sensitive data |
Basic Structure¶
Simple Compose File¶
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
database:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Services¶
Service with Build¶
services:
web:
build:
context: ./web
dockerfile: Dockerfile
args:
NODE_ENV: production
image: myapp/web:latest
container_name: web-app
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- API_URL=http://api:8080
depends_on:
- api
- database
restart: unless-stopped
Service Configuration Options¶
services:
app:
image: myapp:latest
container_name: my-app
hostname: app-server
# Resource limits
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Restart policy
restart: unless-stopped
# User
user: "1000:1000"
# Working directory
working_dir: /app
# Command override
command: ["npm", "start"]
Networks¶
Default Network¶
## Services can communicate using service names as hostnames
services:
web:
image: nginx
api:
image: myapi
# Can access nginx at http://web
Custom Networks¶
services:
frontend:
image: nginx
networks:
- frontend_net
- backend_net
api:
image: myapi
networks:
- backend_net
database:
image: postgres
networks:
- backend_net
networks:
frontend_net:
driver: bridge
backend_net:
driver: bridge
internal: true # No external access
Network Configuration¶
networks:
custom_network:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-custom
ipam:
driver: default
config:
- subnet: 172.28.0.0/16
gateway: 172.28.0.1
Volumes¶
Named Volumes¶
services:
database:
image: postgres:15
volumes:
- db_data:/var/lib/postgresql/data
- db_backup:/backup
volumes:
db_data:
driver: local
db_backup:
driver: local
driver_opts:
type: none
o: bind
device: /path/to/backup
Bind Mounts¶
services:
web:
image: nginx
volumes:
# Bind mount - full path
- /host/path:/container/path
# Bind mount - relative path
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
# Named volume
- app_data:/data
# Anonymous volume
- /app/node_modules
volumes:
app_data:
Environment Variables¶
Direct Environment Variables¶
services:
app:
image: myapp
environment:
NODE_ENV: production
DATABASE_URL: postgresql://user:pass@db:5432/mydb
API_KEY: ${API_KEY} # From host environment
Environment File¶
services:
app:
image: myapp
env_file:
- .env
- .env.production
Example .env file:
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@db:5432/mydb
REDIS_URL=redis://redis:6379
Complete Application Example¶
Full-Stack Web Application¶
version: '3.8'
services:
# Frontend
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: production
image: myapp/frontend:latest
container_name: myapp-frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:8080
networks:
- frontend_net
depends_on:
- api
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 5s
retries: 3
# Backend API
api:
build:
context: ./api
dockerfile: Dockerfile
image: myapp/api:latest
container_name: myapp-api
ports:
- "8080:8080"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@database:5432/myapp
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET}
env_file:
- .env.production
networks:
- frontend_net
- backend_net
depends_on:
database:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
# Database
database:
image: postgres:15-alpine
container_name: myapp-database
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- backend_net
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis Cache
redis:
image: redis:7-alpine
container_name: myapp-redis
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- backend_net
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Nginx Reverse Proxy
nginx:
image: nginx:alpine
container_name: myapp-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
networks:
- frontend_net
depends_on:
- frontend
- api
restart: unless-stopped
networks:
frontend_net:
driver: bridge
backend_net:
driver: bridge
internal: true
volumes:
postgres_data:
driver: local
redis_data:
driver: local
Development vs Production¶
Development Compose File¶
docker-compose.dev.yml:
version: '3.8'
services:
app:
build:
context: .
target: development
volumes:
# Hot reload
- ./src:/app/src
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=*
command: npm run dev
ports:
- "3000:3000"
- "9229:9229" # Debug port
Production Compose File¶
docker-compose.prod.yml:
version: '3.8'
services:
app:
build:
context: .
target: production
environment:
- NODE_ENV=production
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Using Multiple Compose Files¶
## Development
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
## Production
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Extends and Anchors¶
Using Anchors (YAML feature)¶
version: '3.8'
x-common-variables: &common-variables
NODE_ENV: production
LOG_LEVEL: info
x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services:
web:
image: myapp/web
environment:
<<: *common-variables
PORT: 3000
logging: *default-logging
api:
image: myapp/api
environment:
<<: *common-variables
PORT: 8080
logging: *default-logging
Docker Compose Commands¶
Common Commands¶
## Start services
docker-compose up
## Start in detached mode
docker-compose up -d
## Build images
docker-compose build
## Build with no cache
docker-compose build --no-cache
## Stop services
docker-compose stop
## Stop and remove containers
docker-compose down
## Stop and remove containers, volumes, and images
docker-compose down -v --rmi all
## View logs
docker-compose logs
## Follow logs
docker-compose logs -f
## Logs for specific service
docker-compose logs -f api
## Execute command in running container
docker-compose exec api sh
## Run one-off command
docker-compose run api npm test
## List containers
docker-compose ps
## View running processes
docker-compose top
Security Best Practices¶
Never Hardcode Secrets¶
Avoid storing sensitive data in docker-compose.yml:
## Bad - Hardcoded secrets
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: MySecretPassword123 # ❌ Exposed in version control!
API_KEY: sk-1234567890abcdef # ❌ Hardcoded!
## Good - Use environment files
services:
db:
image: postgres:15
env_file:
- .env # ✅ Gitignored file with secrets
## Good - Use Docker secrets (Swarm mode)
services:
db:
image: postgres:15
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
## Good - Use external secret references
services:
app:
image: myapp:latest
environment:
DB_PASSWORD: ${DB_PASSWORD} # ✅ From environment
Key Points:
- Never commit secrets to docker-compose.yml
- Use
.envfiles (add to.gitignore) - Use Docker secrets for Swarm mode
- Use environment variables for 12-factor apps
- Reference external secret managers (Vault, AWS Secrets Manager)
- Rotate secrets regularly
Use Minimal, Trusted Images¶
Only use official, verified, and minimal base images:
## Bad - Unknown or outdated images
services:
app:
image: randomuser/myapp:latest # ❌ Untrusted source!
# Using 'latest' tag - unpredictable
## Good - Official, version-pinned, minimal images
services:
app:
image: node:20.10.0-alpine # ✅ Official, specific version, minimal
# alpine variant is smaller and has fewer vulnerabilities
db:
image: postgres:15.5-alpine # ✅ Official PostgreSQL with specific version
## Good - Use digest pinning for immutability
services:
app:
image: node@sha256:abcd1234... # ✅ Immutable digest
Key Points:
- Use official images from Docker Hub
- Pin specific versions (never use
latest) - Use minimal variants (
alpine,distroless) - Verify image signatures
- Use digest pinning for critical services
- Regularly update base images
Run as Non-Root User¶
Never run containers as root:
## Bad - Running as root (default)
services:
app:
image: node:20-alpine
# No user specified - runs as root ❌
## Good - Run as non-root user
services:
app:
image: node:20-alpine
user: "1000:1000" # ✅ Non-root user
# Or use 'node' user built into Node image
# user: node
## Good - Define non-root user in Dockerfile
# Dockerfile
FROM node:20-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
Key Points:
- Always specify a non-root user
- Use UID:GID format for clarity
- Create users in Dockerfile
- Never use UID 0 (root)
- Test that application works as non-root
- Use
read_onlyfilesystems where possible
Limit Resources¶
Prevent resource exhaustion:
## Bad - No resource limits
services:
app:
image: myapp:latest
# No limits - can consume all host resources ❌
## Good - Set resource limits
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# Prevent fork bombs
pids_limit: 100
db:
image: postgres:15-alpine
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
Key Points:
- Set CPU and memory limits
- Set PID limits to prevent fork bombs
- Use reservations for guaranteed resources
- Monitor resource usage
- Adjust limits based on actual usage
- Prevent denial of service
Network Segmentation¶
Isolate services with network boundaries:
## Bad - All services on default bridge
services:
web:
image: nginx:alpine
ports:
- "80:80"
app:
image: myapp:latest
db:
image: postgres:15
# All on same network - no isolation ❌
## Good - Separate networks for isolation
services:
web:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend # Only frontend network
app:
image: myapp:latest
networks:
- frontend # Connect to both
- backend
db:
image: postgres:15
networks:
- backend # Only backend network - isolated from web
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # ✅ No external access
Key Points:
- Create separate networks for tiers
- Use
internal: truefor backend networks - Limit exposed ports
- Use service names for internal DNS
- Implement zero-trust networking
- Monitor network traffic
Read-Only Filesystems¶
Use read-only root filesystems:
## Good - Read-only filesystem
services:
app:
image: myapp:latest
read_only: true # ✅ Immutable root filesystem
tmpfs:
- /tmp # Writable tmpfs for temporary files
- /var/run
nginx:
image: nginx:alpine
read_only: true
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro # ✅ Read-only config
- nginx-cache:/var/cache/nginx # Writable volume for cache
- nginx-run:/var/run
volumes:
nginx-cache:
nginx-run:
Key Points:
- Use
read_only: truefor immutable containers - Mount tmpfs for temporary writable space
- Mount configs as read-only (
:ro) - Use volumes for persistent writable data
- Prevents malware persistence
- Enhances security posture
Security Options¶
Enable security features:
## Good - Security options enabled
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true # ✅ Prevent privilege escalation
- apparmor=docker-default # Enable AppArmor
# - seccomp=seccomp-profile.json # Custom seccomp profile
cap_drop:
- ALL # ✅ Drop all capabilities
cap_add:
- NET_BIND_SERVICE # Only add required capabilities
privileged: false # ✅ Never use privileged mode
Key Points:
- Always set
no-new-privileges:true - Drop all capabilities, add only required ones
- Never use
privileged: true - Enable AppArmor or SELinux
- Use custom seccomp profiles
- Minimize attack surface
Container Health and Availability Checks¶
Implement health checks for availability and security:
## Good - Health checks configured
services:
app:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:15-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Key Points:
- Define health checks for all services
- Use appropriate intervals and timeouts
- Monitor health check status
- Restart unhealthy containers
- Use health checks for rolling updates
- Prevent zombie containers
Common Pitfalls¶
Port Conflict with Host¶
Issue: Mapping container ports to already-used host ports causes container startup failure.
Example:
## Bad - Port 80 likely in use on host
services:
web:
image: nginx
ports:
- "80:80" # ❌ Conflicts if host already has service on port 80
api:
image: myapi
ports:
- "80:8080" # ❌ Also tries to bind host port 80!
Solution: Use unique host ports or let Docker assign random ports.
## Good - Unique host ports
services:
web:
image: nginx
ports:
- "8080:80" # ✅ Web on host port 8080
api:
image: myapi
ports:
- "8081:8080" # ✅ API on host port 8081
## Good - Random host ports
services:
web:
image: nginx
ports:
- "80" # ✅ Docker assigns random host port
Key Points:
- Check for port conflicts with
docker psandnetstat - Use high ports (>1024) to avoid conflicts
- Omit host port to let Docker assign random port
- Use
docker-compose portto find assigned ports
Missing Depends_On for Service Dependencies¶
Issue: Services starting before dependencies are ready causes connection failures.
Example:
## Bad - No dependency specification
services:
api:
image: myapi
environment:
- DB_HOST=db
# ❌ May start before database is ready!
db:
image: postgres:15
Solution: Use depends_on with health checks.
## Good - Explicit dependencies with health checks
services:
api:
image: myapi
depends_on:
db:
condition: service_healthy # ✅ Wait for healthy state
environment:
- DB_HOST=db
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
Key Points:
depends_oncontrols startup order- Use
condition: service_healthywith healthchecks - Healthchecks ensure service is actually ready
- Without healthcheck,
depends_ononly waits for container start
Volume Mount Path Typos¶
Issue: Typos in volume mount paths cause data to be written to wrong locations or errors.
Example:
## Bad - Typo in container path
services:
app:
image: myapp
volumes:
- ./data:/app/data
- ./config:/app/cofig # ❌ Typo! Should be /app/config
Solution: Double-check all paths and test volume mounts.
## Good - Correct paths
services:
app:
image: myapp
volumes:
- ./data:/app/data # ✅ Correct
- ./config:/app/config # ✅ Correct
- ./logs:/app/logs:rw # Specify read-write explicitly
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data # ✅ Named volume
volumes:
postgres_data:
Key Points:
- Verify container paths match application expectations
- Use absolute paths or
./for relative paths - Named volumes persist independently of containers
- Use
:rofor read-only,:rwfor read-write
Network Name Collision¶
Issue: Not specifying network names causes Docker to generate unpredictable names.
Example:
## Bad - Auto-generated network names
services:
web:
image: nginx
networks:
- frontend # ❌ Network name will be prefixed with directory name
networks:
frontend: # Becomes "myproject_frontend" (unpredictable)
Solution: Use explicit network names or accept generated names consistently.
## Good - Explicit network names
services:
web:
image: nginx
networks:
- frontend
networks:
frontend:
name: app_frontend # ✅ Explicit name
driver: bridge
## Good - Accept generated names but document
# Networks will be prefixed with project name
# Project name from directory or -p flag
services:
web:
networks:
- frontend # ✅ Consistent within project
networks:
frontend: # Will be ${PROJECT}_frontend
Key Points:
- Docker Compose prefixes network names with project name
- Set project name with
-pflag ornamein compose file - Use
name:in network definition for explicit naming - External networks use
external: true
Environment File Path Errors¶
Issue: Wrong paths to .env files cause variables to not load.
Example:
## Bad - Incorrect env_file path
services:
api:
image: myapi
env_file:
- .env # ❌ Relative to current directory, not compose file location!
- ../config.env # ❌ May not exist
Solution: Use correct relative paths from compose file location.
## Good - Correct paths
services:
api:
image: myapi
env_file:
- ./.env # ✅ Same directory as compose file
- ./config/.env # ✅ Subdirectory
environment:
- NODE_ENV=production # Explicit override
## Good - Check file existence
## Before running: test -f .env || cp .env.example .env
Key Points:
env_filepaths are relative to compose file location- Use
environment:for explicit values environment:overridesenv_filevalues- Commit
.env.example, not.env
Anti-Patterns¶
❌ Avoid: Hardcoded Secrets¶
## Bad - Hardcoded password
services:
database:
environment:
POSTGRES_PASSWORD: mysecretpassword
## Good - Use environment variables
services:
database:
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
❌ Avoid: latest Tag¶
## Bad - Unpredictable
services:
app:
image: myapp:latest
## Good - Specific version
services:
app:
image: myapp:1.2.3
❌ Avoid: Not Using Volumes for Data¶
## Bad - Data lost when container stops
services:
database:
image: postgres
## Good - Persistent volume
services:
database:
image: postgres
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
❌ Avoid: Not Using Health Checks¶
## Bad - No health checks
services:
api:
image: myapi:1.0
ports:
- "8080:8080"
## Good - With health check
services:
api:
image: myapi:1.0
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 40s
❌ Avoid: Running as Root¶
## Bad - Default root user
services:
app:
image: node:22-alpine
command: npm start
## Good - Specify non-root user
services:
app:
image: node:22-alpine
user: "node"
command: npm start
❌ Avoid: Not Setting Resource Limits¶
## Bad - No resource limits (can exhaust host)
services:
app:
image: myapp:1.0
## Good - Set limits
services:
app:
image: myapp:1.0
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
❌ Avoid: Not Using Depends On¶
## Bad - Services start in parallel (race condition)
services:
api:
image: myapi:1.0
database:
image: postgres:14
## Good - Explicit dependencies
services:
api:
image: myapi:1.0
depends_on:
database:
condition: service_healthy
database:
image: postgres:14
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
Best Practices¶
Use .dockerignore¶
node_modules
npm-debug.log
.git
.env
.DS_Store
Health Checks¶
services:
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Resource Limits¶
services:
app:
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Dependency Management¶
services:
api:
depends_on:
database:
condition: service_healthy
Tool Configuration¶
docker-compose.yml Validation¶
## Validate compose file syntax
docker compose config
## Validate and show final configuration
docker compose config --no-interpolate
## Validate specific file
docker compose -f docker-compose.prod.yml config
.dockerignore¶
## Version control
.git
.gitignore
.gitattributes
## CI/CD
.github
.gitlab-ci.yml
.travis.yml
## Documentation
*.md
docs/
LICENSE
## Dependencies
node_modules/
vendor/
__pycache__/
*.pyc
## Build artifacts
dist/
build/
*.egg-info/
## IDE
.vscode/
.idea/
*.swp
*.swo
## Environment
.env.local
.env.*.local
*.log
## Testing
coverage/
.nyc_output/
EditorConfig¶
## .editorconfig
[docker-compose*.{yml,yaml}]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
VS Code Settings¶
{
"[dockercompose]": {
"editor.defaultFormatter": "redhat.vscode-yaml",
"editor.formatOnSave": true
},
"yaml.schemas": {
"https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json": [
"docker-compose*.yml",
"docker-compose*.yaml"
]
},
"yaml.customTags": [
"!reference sequence"
]
}
Pre-commit Hooks¶
## .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: ['--allow-multiple-documents']
- id: check-added-large-files
- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
hooks:
- id: yamllint
args: ['-d', '{extends: default, rules: {line-length: {max: 120}}}']
files: docker-compose.*\.ya?ml$
yamllint Configuration¶
## .yamllint
extends: default
rules:
line-length:
max: 120
level: warning
indentation:
spaces: 2
indent-sequences: true
comments:
min-spaces-from-content: 1
document-start: disable
truthy:
allowed-values: ['true', 'false', 'yes', 'no']
Makefile¶
## Makefile
.PHONY: up down build logs ps validate
up:
docker compose up -d
down:
docker compose down
build:
docker compose build
rebuild:
docker compose build --no-cache
logs:
docker compose logs -f
ps:
docker compose ps
validate:
docker compose config --quiet
@echo "✓ docker-compose.yml is valid"
validate-prod:
docker compose -f docker-compose.prod.yml config --quiet
@echo "✓ docker-compose.prod.yml is valid"
clean:
docker compose down -v
docker system prune -f
exec-web:
docker compose exec web sh
exec-db:
docker compose exec db psql -U postgres
docker-compose.override.yml¶
Used for local development overrides:
## docker-compose.override.yml
## This file is automatically merged with docker-compose.yml
## Use for local development settings
services:
web:
environment:
- DEBUG=true
- LOG_LEVEL=debug
volumes:
- ./src:/app/src:delegated
ports:
- "3000:3000"
- "9229:9229" # Node.js debug port
command: npm run dev
db:
ports:
- "5432:5432" # Expose PostgreSQL locally
References¶
Official Documentation¶
Additional Resources¶
Status: Active