Technical documentation for implementing and extending Docker Compose service profiles in DevStack Core.
This document explains how service profiles are implemented in DevStack Core using Docker Compose native profile support.
Each service in docker-compose.yml has a profiles: key that determines which profiles will start that service.
| Service | Profiles | Rationale |
|---|---|---|
| vault | (none) | Always starts - required for credential management |
| postgres | minimal, standard, full | Core database for all profiles |
| pgbouncer | minimal, standard, full | Connection pooling for PostgreSQL |
| forgejo | minimal, standard, full | Git server available in all profiles |
| redis-1 | minimal, standard, full | Single Redis in minimal, cluster node 1 in standard/full |
| redis-2 | standard, full | Cluster node 2 (only in standard+) |
| redis-3 | standard, full | Cluster node 3 (only in standard+) |
| mysql | standard, full | Additional database (standard+) |
| mongodb | standard, full | NoSQL database (standard+) |
| rabbitmq | standard, full | Message queue (standard+) |
| prometheus | full | Metrics collection (full only) |
| grafana | full | Visualization (full only) |
| loki | full | Log aggregation (full only) |
| vector | full | Observability pipeline (full only) |
| cadvisor | full | Container monitoring (full only) |
| redis-exporter-1 | full | Redis metrics for node 1 (full only) |
| redis-exporter-2 | full | Redis metrics for node 2 (full only) |
| redis-exporter-3 | full | Redis metrics for node 3 (full only) |
| reference-api | reference | Python FastAPI code-first |
| api-first | reference | Python FastAPI API-first |
| golang-api | reference | Go with Gin |
| nodejs-api | reference | Node.js with Express |
| rust-api | reference | Rust with Actix-web |
- vault - No profile assignment means it starts with ANY profile or when no profile is specified
┌─────────────────────────────────────────────────────┐
│ MINIMAL (5 services, 2GB RAM) │
│ • vault, postgres, pgbouncer, forgejo, redis-1 │
└─────────────────────────────────────────────────────┘
│
│ adds: mysql, mongodb, redis-2,
│ redis-3, rabbitmq
▼
┌─────────────────────────────────────────────────────┐
│ STANDARD (12 services, 4GB RAM) │
│ • All minimal services │
│ • + mysql, mongodb, redis-2/3, rabbitmq │
└─────────────────────────────────────────────────────┘
│
│ adds: prometheus, grafana, loki,
│ vector, cadvisor, exporters
▼
┌─────────────────────────────────────────────────────┐
│ FULL (18 services, 6GB RAM) │
│ • All standard services │
│ • + prometheus, grafana, loki, vector, cadvisor │
│ • + redis-exporter-1/2/3 │
└─────────────────────────────────────────────────────┘
┌───────────────┐
│ REFERENCE │
│ (5 services) │
│ +1GB RAM │
└───────────────┘
│
│ Can combine with any profile
▼
┌──────────────┬──────────────┬──────────────┐
│ minimal │ standard │ full │
│ +reference │ +reference │ +reference │
└──────────────┴──────────────┴──────────────┘
services:
postgres:
profiles: ["minimal", "standard", "full"]
image: postgres:18
# ... rest of configurationservices:
vault:
# No profiles key = starts with any profile
image: vault:latest
# ... rest of configuration# Start with specific profile
docker compose --profile minimal up -d
# Combine multiple profiles
docker compose --profile standard --profile reference up -d
# Start without any profile (only services without profiles key)
docker compose up -d # Only starts vaultWhen running minimal profile:
- Only
redis-1starts - Redis runs in standalone mode (not cluster)
- No cluster initialization needed
- Direct connection:
redis-cli -h localhost -p 6379
When running standard or full profiles:
- All three Redis nodes start (
redis-1,redis-2,redis-3) - Cluster mode enabled in redis.conf
- Cluster initialization required after first start
- Cluster connection:
redis-cli -c -h localhost -p 6379
All services depend on Vault:
services:
postgres:
depends_on:
vault:
condition: service_healthyThis ensures:
- Vault starts first
- Vault becomes healthy (unsealed + initialized)
- Only then do other services start
- Services can fetch credentials from Vault
Services only depend on services in the same or broader profile:
services:
redis-exporter-1:
profiles: ["full"]
depends_on:
vault:
condition: service_healthy
redis-1:
condition: service_startedSince redis-exporter-1 is only in full profile, and redis-1 is in minimal, standard, and full, the dependency is always satisfied.
All services maintain their health checks regardless of profile:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 60s
timeout: 5s
retries: 5
start_period: 30sThe management script can check health with:
./devstack.py healthEach profile has optional environment override files:
configs/profiles/
├── minimal.env # Overrides for minimal profile
├── standard.env # Overrides for standard profile
├── full.env # Overrides for full profile
└── reference.env # Overrides for reference profile
Example configs/profiles/minimal.env:
# Redis configuration for minimal profile
REDIS_CLUSTER_ENABLED=false
REDIS_CLUSTER_INIT_REQUIRED=false
# Reduced connection limits for lighter workload
POSTGRES_MAX_CONNECTIONS=50
MYSQL_MAX_CONNECTIONS=50
# Faster health checks (smaller stack)
POSTGRES_HEALTH_INTERVAL=30s
VAULT_HEALTH_INTERVAL=30sExample configs/profiles/standard.env:
# Redis cluster configuration
REDIS_CLUSTER_ENABLED=true
REDIS_CLUSTER_INIT_REQUIRED=true
# Standard connection limits
POSTGRES_MAX_CONNECTIONS=100
MYSQL_MAX_CONNECTIONS=100
MONGODB_MAX_CONNECTIONS=100
# Standard health check intervals
POSTGRES_HEALTH_INTERVAL=60s
VAULT_HEALTH_INTERVAL=60s-
Minimal Profile
docker compose --profile minimal config --services # Should output: vault, postgres, pgbouncer, forgejo, redis-1 docker compose --profile minimal up -d docker ps --format "table {{.Names}}\t{{.Status}}" # Verify only 5 containers running
-
Standard Profile
docker compose --profile standard config --services # Should output: vault, postgres, pgbouncer, forgejo, redis-1/2/3, mysql, mongodb, rabbitmq docker compose --profile standard up -d docker ps --format "table {{.Names}}\t{{.Status}}" # Verify 12 containers running
-
Full Profile
docker compose --profile full config --services # Should output: all standard services + prometheus, grafana, loki, vector, cadvisor, exporters docker compose --profile full up -d docker ps --format "table {{.Names}}\t{{.Status}}" # Verify 18 containers running
-
Combined Profiles
docker compose --profile standard --profile reference config --services # Should output: all standard services + all reference apps docker compose --profile standard --profile reference up -d docker ps --format "table {{.Names}}\t{{.Status}}" # Verify 17 containers running (12 standard + 5 reference)
Measure actual resource usage:
# Total RAM usage by profile
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}" | \
awk '{sum+=$2} END {print "Total RAM:", sum/1024/1024/1024, "GB"}'
# Per-service RAM
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}"Expected results:
- Minimal: ~2GB RAM
- Standard: ~4GB RAM
- Full: ~6GB RAM
# Currently running standard profile
docker compose --profile standard ps
# Stop standard profile services
docker compose --profile standard down
# Start minimal profile
docker compose --profile minimal up -d# Add standard profile services to running minimal
docker compose --profile standard up -d
# This will:
# 1. Keep running minimal services (vault, postgres, etc.)
# 2. Start additional standard services (mysql, mongodb, redis-2/3, rabbitmq)# Stop all services
docker compose --profile standard down
# Start only minimal
docker compose --profile minimal up -d
# Or remove specific services:
docker compose rm -sf mysql mongodb redis-2 redis-3 rabbitmqMorning Routine (Standard Profile):
./devstack.py start --profile standard
./devstack.py health
./devstack.py redis-cluster-init # First time onlyEnd of Day:
./devstack.py stopWeekend (Keep Minimal Running):
# Stop standard, keep minimal (Git server stays up)
docker compose --profile standard down
docker compose --profile minimal up -dGitHub Actions (Minimal for Tests):
- name: Start DevStack Core
run: |
docker compose --profile minimal up -d
docker compose --profile minimal psLoad Testing (Full Profile):
- name: Start DevStack Core with Observability
run: |
docker compose --profile full up -d
./wait-for-health.shCheck if service is in the profile:
docker compose --profile minimal config --services | grep mysql
# Empty output = mysql not in minimal profileCheck running containers:
docker ps --format "{{.Names}}" | grep dev-Compare with expected services:
docker compose --profile minimal config --servicesIf services from multiple profiles are running:
# Nuclear option: stop everything
docker compose down --remove-orphans
# Start clean with desired profile
docker compose --profile standard up -d- Always specify profile - Don't rely on defaults
- Use minimal for CI/CD - Faster, lighter
- Use standard for development - Full feature set
- Use full for performance testing - Observability included
- Combine reference with others - Learn patterns alongside dev
- Document custom profiles - Add to profiles.yaml custom_profiles section
- Test profile switches - Ensure services stop/start correctly
- Monitor resources - Validate RAM estimates match reality
- Create profiles.yaml with profile definitions
- Document profile implementation strategy
- Add
profiles:keys to docker-compose.yml - Create profile-specific environment files
- Test minimal profile (5 services)
- Test standard profile (12 services)
- Test full profile (18 services)
- Test reference profile (5 services)
- Test combined profiles (standard + reference)
- Validate resource usage matches estimates
- Update documentation (README, INSTALLATION, USAGE)
- Create Python management script with profile support
- Add profile validation to CI/CD pipeline