Keep configurations DRY! The include directive enables modular, reusable Compose setups.
Basic include usage
Split configurations into logical modules:
# compose.yml
include:
- path: ./services/database.yml
- path: ./services/cache.yml
- path: ./services/monitoring.yml
services:
app:
image: myapp:latest
depends_on:
- postgres
- redis
# services/database.yml
services:
postgres:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Project-wide organization
Structure complex projects:
project/
├── compose.yml # Main entry point
├── common/
│ ├── networks.yml # Shared networks
│ └── volumes.yml # Shared volumes
├── services/
│ ├── frontend.yml # Frontend services
│ ├── backend.yml # Backend services
│ └── database.yml # Data layer
└── environments/
├── dev.yml # Development overrides
└── prod.yml # Production config
# compose.yml
include:
- path: ./common/networks.yml
- path: ./common/volumes.yml
- path: ./services/frontend.yml
- path: ./services/backend.yml
- path: ./services/database.yml
- path: ${COMPOSE_ENV:-./environments/dev.yml}
Conditional includes
Include files based on environment:
# compose.yml
include:
- path: ./base.yml
- path: ./monitoring.yml
env_file: .env.monitoring # Only if file exists
- path: ${EXTRA_SERVICES:-/dev/null}
required: false # Don't fail if missing
services:
app:
image: myapp
Run with optional services:
# Basic setup
docker compose up
# With monitoring
touch .env.monitoring
docker compose up
# With extra services
EXTRA_SERVICES=./debug.yml docker compose up
Team collaboration
Share common configurations:
# team/shared.yml
x-default-logging: &default-logging
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
shared-db:
image: postgres:15
<<: *default-logging
volumes:
- shared_data:/var/lib/postgresql/data
volumes:
shared_data:
# compose.yml
include:
- path: ./team/shared.yml
services:
app:
image: myapp
depends_on:
- shared-db
Service libraries
Create reusable service definitions:
# lib/elasticsearch.yml
services:
elasticsearch:
image: elasticsearch:8.11.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
volumes:
- es_data:/usr/share/elasticsearch/data
volumes:
es_data:
# lib/kibana.yml
services:
kibana:
image: kibana:8.11.0
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
depends_on:
- elasticsearch
ports:
- "5601:5601"
profiles: ["monitoring"] # Service-level profile
# compose.yml
include:
- path: ./lib/elasticsearch.yml
- path: ./lib/kibana.yml
services:
app:
image: myapp
environment:
ES_HOST: elasticsearch
Override with includes
Layer configurations:
# base.yml
services:
app:
image: myapp:latest
environment:
LOG_LEVEL: info
# dev-overrides.yml
services:
app:
environment:
LOG_LEVEL: debug
volumes:
- .:/app
# compose.yml
include:
- path: ./base.yml
- path: ./dev-overrides.yml # Merges with base
# Result: app has LOG_LEVEL=debug and volume mount
Include with variables
Parameterize included paths:
# compose.yml
include:
- path: ./services/${SERVICE_SET:-standard}.yml
- path: ./configs/${REGION:-us-east}.yml
services:
app:
image: myapp:${VERSION:-latest}
Usage:
# Default configuration
docker compose up
# Custom service set and region
SERVICE_SET=premium REGION=eu-west docker compose up
Pro tip
Validate complex include structures:
#!/bin/bash
# validate-compose.sh
echo "Validating Compose configuration..."
# Check all included files exist
for file in $(grep -E '^\s*- path:' compose.yml | awk '{print $3}'); do
if [ ! -f "$file" ]; then
echo "❌ Missing include: $file"
exit 1
fi
echo "✓ Found: $file"
done
# Validate final configuration
if docker compose config > /dev/null 2>&1; then
echo "✅ Configuration valid"
# Show final service list
echo "Services configured:"
docker compose config --services | sed 's/^/ - /'
else
echo "❌ Configuration invalid"
docker compose config
exit 1
fi
Modular configurations scale with your project!