Skip to content
Go back

Docker Compose Profiles: Run Only What You Need

By SumGuy 5 min read
Docker Compose Profiles: Run Only What You Need

You’ve got a Docker Compose setup with 15 services: databases, caches, message queues, debugging tools, and your app. In development, you need everything. In production, you need four. You could maintain two separate compose files, but that’s friction. Profiles solve this elegantly.

Profiles let you tag services and conditionally activate them at runtime. One compose file, multiple configurations.

The Problem They Solve

Imagine this setup:

docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: dev
redis:
image: redis:7
# Used in production
adminer:
image: adminer
# Database GUI, dev-only
ports:
- "8080:8080"
jaeger:
image: jaegertracing/all-in-one
# Distributed tracing, dev-only
ports:
- "6831:6831/udp"
- "16686:16686"
prometheus:
image: prom/prometheus
# Metrics, dev-only
ports:
- "9090:9090"
app:
build: .
depends_on:
- postgres
- redis

When you run docker-compose up, all 6 services start. In production, you only want postgres, redis, and app. The dev tools (adminer, jaeger, prometheus) are noise.

You could delete those services or maintain separate files:

docker-compose.yml (dev + prod)
docker-compose.prod.yml (prod only)

But every change to the base config means updating two files. Profiles are cleaner.

How Profiles Work

Assign services to a profile. Specify which profiles to activate:

docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: dev
redis:
image: redis:7
# Dev-only tools
adminer:
image: adminer
profiles:
- debug
ports:
- "8080:8080"
jaeger:
image: jaegertracing/all-in-one
profiles:
- debug
ports:
- "6831:6831/udp"
- "16686:16686"
prometheus:
image: prom/prometheus
profiles:
- debug
ports:
- "9090:9090"
app:
build: .
depends_on:
- postgres
- redis

Now:

Terminal window
# Start only postgres, redis, app
$ docker-compose up
Creating network ...
Creating postgres ...
Creating redis ...
Creating app ...
adminer, jaeger, prometheus are skipped
# Start with debug tools
$ docker-compose --profile debug up
Creating network ...
Creating postgres ...
Creating redis ...
Creating adminer ...
Creating jaeger ...
Creating prometheus ...
Creating app ...
# Activate multiple profiles
$ docker-compose --profile debug --profile testing up

Services without a profile always start. Services with a profile only start if that profile is activated.

Practical Dev/Prod Pattern

Here’s a real-world example: separate dev, testing, and production profiles:

docker-compose.yml
version: '3.8'
services:
# Always-on
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: mydb
redis:
image: redis:7
app:
build: .
environment:
DATABASE_URL: postgresql://postgres:secret@postgres/mydb
REDIS_URL: redis://redis:6379
# Development tools (dev profile)
adminer:
image: adminer
profiles:
- dev
ports:
- "8080:8080"
depends_on:
- postgres
pgweb:
image: sosedoff/pgweb
profiles:
- dev
ports:
- "8081:8081"
depends_on:
- postgres
environment:
DATABASE_URL: postgresql://postgres:secret@postgres/mydb?sslmode=disable
# Testing tools (test profile)
postgres-test:
image: postgres:15
profiles:
- test
environment:
POSTGRES_PASSWORD: test
POSTGRES_DB: test_db
tmpfs:
- /var/lib/postgresql/data
# Performance testing (perf profile)
locust:
image: locustio/locust
profiles:
- perf
ports:
- "8089:8089"
volumes:
- ./loadtest:/home/locust

Now you can:

Terminal window
# Minimal: just app + core services
$ docker-compose up
# Development: add debugging and admin tools
$ docker-compose --profile dev up
# Testing: add test database
$ docker-compose --profile test up
# Performance testing: add load generator
$ docker-compose --profile perf up
# All of it
$ docker-compose --profile dev --profile test --profile perf up

Set Default Profiles in Compose

You can set a default profile in the compose file so certain profiles always activate:

docker-compose.yml
version: '3.8'
# Activate 'dev' by default in development
services:
app:
build: .
# ...
adminer:
image: adminer
profiles:
- dev
# ...

Then create a .env file (or export):

Terminal window
COMPOSE_PROFILES=dev
Terminal window
$ export COMPOSE_PROFILES=dev
$ docker-compose up # Activates dev profile by default

Or in .env:

.env
COMPOSE_PROFILES=dev
Terminal window
$ docker-compose up # Reads .env, activates dev

Command Examples

Terminal window
# List all services (including those in profiles)
$ docker-compose config
# List active services only (current profile)
$ docker-compose ps
# Start a specific service in a profile
$ docker-compose --profile debug up adminer
# Execute command in a service within a profile
$ docker-compose --profile debug exec adminer bash
# View logs for a profiled service
$ docker-compose --profile debug logs adminer
# Stop, but keep the profile active
$ docker-compose --profile debug stop
# Remove everything (respects active profiles)
$ docker-compose --profile dev down

Real-World Microservices Example

docker-compose.yml
version: '3.8'
services:
# Core services
api:
build:
context: ./api
environment:
DB_URL: postgresql://postgres:pwd@postgres:5432/api
CACHE_URL: redis://redis:6379/0
postgres:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7
# Optional services
workers:
build:
context: ./workers
profiles:
- workers
depends_on:
- redis
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.0.0
profiles:
- search
environment:
discovery.type: single-node
kibana:
image: docker.elastic.co/kibana/kibana:8.0.0
profiles:
- search
ports:
- "5601:5601"
# Development-only
adminer:
image: adminer
profiles:
- dev
ports:
- "8080:8080"
volumes:
postgres_data:

Usage:

Terminal window
# Production: api, postgres, redis
$ docker-compose up -d
# Development: add admin tools
$ docker-compose --profile dev up -d
# With search indexing: add elasticsearch + kibana
$ docker-compose --profile search up -d
# Full stack: everything
$ docker-compose --profile dev --profile search --profile workers up -d

Checklist

Profiles are underused. They eliminate the friction of maintaining multiple compose files and let you keep one source of truth. Your 15-service dev stack doesn’t need to run locally every time.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Is Your Linux Server Destroying Its SSD?
Next Post
journalctl Queries Every Sysadmin Needs

Related Posts