⏱️4 min read · 812 words
Docker Compose is the standard tool for running multi-container applications in development and production. In 2026, Docker Compose v2 with the new include directive, profiles, secrets management, and GPU support has made it more powerful than ever. This guide takes you from a single container to a full production stack.
📋 Table of Contents
Docker Compose v2 — What Changed
docker compose(no hyphen) — built-in to Docker CLI- Include directive — split compose files across multiple files
- Profiles — selective service startup (dev vs prod)
- Service dependencies —
healthcheck-based startup ordering - Watch mode — auto-sync code changes without rebuilding
- GPU support —
deploy.resources.reservations.devices
Basic compose.yaml
# compose.yaml (preferred name in 2026, Docker auto-discovers)
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379/0
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
volumes:
- .:/app
networks:
- backend
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- certs:/etc/nginx/certs
depends_on:
- web
networks:
- backend
volumes:
postgres_data:
redis_data:
certs:
networks:
backend:
driver: bridge
Essential Docker Compose Commands
# Start all services
docker compose up -d
# View logs
docker compose logs -f # all services
docker compose logs -f web # specific service
# Rebuild and restart
docker compose up -d --build web
# Stop all
docker compose down
# Stop + remove volumes
docker compose down -v
# Execute command in running container
docker compose exec web bash
docker compose exec db psql -U postgres myapp
# Scale a service
docker compose up -d --scale worker=3
# Check service status
docker compose ps
docker compose top
# Validate compose file
docker compose config
Profiles: Dev vs Prod
services:
web:
build: .
ports:
- "8000:8000"
# No profile = always started
debug:
image: busybox
profiles: [dev] # only with --profile dev
network_mode: "service:web"
flower:
image: mher/flower
profiles: [monitoring]
ports: ["5555:5555"]
environment:
CELERY_BROKER_URL: redis://cache:6379/0
prometheus:
image: prom/prometheus
profiles: [monitoring]
ports: ["9090:9090"]
grafana:
image: grafana/grafana
profiles: [monitoring]
ports: ["3000:3000"]
# Start with dev profile
docker compose --profile dev up -d
# Start with monitoring
docker compose --profile monitoring up -d
# Combine profiles
docker compose --profile dev --profile monitoring up -d
Secrets Management
# Don't use plain environment variables for passwords in production!
services:
web:
environment:
- DATABASE_URL=postgresql://postgres@db:5432/myapp
secrets:
- db_password
- jwt_secret
db:
image: postgres:16-alpine
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt # or use external: true for Docker Swarm secrets
jwt_secret:
environment: JWT_SECRET # pull from host environment
Dockerfile Best Practices
# Multi-stage build for Python (production image)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
# Install in a venv to isolate
RUN python -m venv /opt/venv && /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
# Production image
FROM python:3.12-slim
# Security: don't run as root
RUN useradd --create-home appuser
WORKDIR /home/appuser
COPY --from=builder /opt/venv /opt/venv
COPY . .
ENV PATH="/opt/venv/bin:$PATH"
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
CMD ["gunicorn", "main:app", "--workers", "4", "--bind", "0.0.0.0:8000"]
Watch Mode (Development)
# compose.yaml — watch configuration
services:
web:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
- action: rebuild
path: requirements.txt
- action: rebuild
path: Dockerfile
# Enable watch mode (auto-sync code changes)
docker compose watch
Production Compose Stack
# Use multiple compose files for environment overrides
docker compose -f compose.yaml -f compose.prod.yaml up -d
# compose.prod.yaml overrides:
# - Remove bind mounts (no source code)
# - Use external secrets
# - Add resource limits
# - Set restart policies
# compose.prod.yaml
services:
web:
image: myapp:1.2.3 # specific image tag, not build
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: '3'
Docker Compose in 2026 covers the full development-to-production lifecycle. Use compose watch for fast local development, profiles for optional services, and multi-file composition to keep dev and prod configs clean.
📚 You might also like
🔗 Share this article




✍️ Leave a Comment