Compose profiles let you group services so you can start only a subset of your stack for specific scenarios (e.g., lightweight testing vs. full production).
In your docker-compose.yml, add a profiles list to each service you want to group:
services:
postgres:
profiles: ["core"]
...
jobrunner:
profiles: ["core"]
nextjs:
profiles: ["full"]
authserver:
profiles: ["full"]
How it works
docker compose up(no--profile)
Starts only services without aprofileskey.
In the example above, nothing would start unless you define a default (non-profiled) service.docker compose --profile core up
Starts only services tagged withcore(postgres,jobrunner).
Ideal for a lightweight testing stack.docker compose --profile full up
Starts only services tagged withfull(nextjs,authserver).Multiple profiles are supported:
docker compose --profile core --profile full up
This allows you to maintain one Compose file while running minimal stacks for testing or the full environment as needed.
If a service should always run, omit the
profileskey. It will start regardless of profile flags.
Multiple Databases and Profiles
Docker Compose does not allow multiple services with the same name—even across different profiles.
If you need both a production database and a unit test database, give them unique service names and assign each to its own profile:
services:
app-db:
profiles: ["prod"]
unit-test-db:
profiles: ["test"]
Database Configuration Strategy
- Your application containers use one
DATABASE_URLat a time. - Each profile startup determines which database the app connects to.
-
For example:
- In a
testprofile, setDATABASE_URLtounit-test-db. - In a
prodprofile, setDATABASE_URLtoapp-db.
- In a
There is no runtime toggling — each profile spin-up decides which database your app connects to based on the environment variables provided at startup.
Alternative Approach
Instead of profiles, you can maintain separate Compose files:
docker-compose.test.ymldocker-compose.prod.yml
Each file can hardcode the correct database host.
This provides clearer separation but requires maintaining multiple files.
Concrete Example with unit-test-db and app-db
Below is a clean, complete working example.
1️⃣ docker-compose.yml
services:
# -----------------
# Databases
# -----------------
unit-test-db:
image: postgres:15
container_name: unit-test-db
profiles: ["test"]
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports:
- "5433:5432"
app-db:
image: postgres:15
container_name: app-db
profiles: ["prod"]
environment:
POSTGRES_USER: prod
POSTGRES_PASSWORD: prod
POSTGRES_DB: proddb
ports:
- "5432:5432"
# -----------------
# Application
# -----------------
app:
build: .
depends_on:
- unit-test-db
- app-db
environment:
DATABASE_URL: ${DATABASE_URL}
2️⃣ What DATABASE_URL Should Look Like
Inside Docker, the hostname is the service name.
Test profile → connect to unit-test-db
postgresql://test:test@unit-test-db:5432/testdb
Prod profile → connect to app-db
postgresql://prod:prod@app-db:5432/proddb
3️⃣ Running with Different Environment Variable Sets
✅ Option A — Use Separate .env Files (Recommended)
.env.test
DATABASE_URL=postgresql://test:test@unit-test-db:5432/testdb
.env.prod
DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb
Start test stack
docker compose --profile test --env-file .env.test up
Start prod stack
docker compose --profile prod --env-file .env.prod up
This is the cleanest and most maintainable approach.
✅ Option B — Inline Environment Variables
Test
DATABASE_URL=postgresql://test:test@unit-test-db:5432/testdb \
docker compose --profile test up
Prod
DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb \
docker compose --profile prod up
4️⃣ What Actually Starts
When you run:
docker compose --profile test up
Only:
unit-test-dbapp
will start.
When you run:
docker compose --profile prod up
Only:
app-dbapp
will start.
Clean Mental Model
| Profile | DB Container | DATABASE_URL host |
|---|---|---|
| test | unit-test-db | unit-test-db |
| prod | app-db | app-db |
The service name becomes the DNS hostname inside Docker’s network.
Each profile launch defines:
- Which database container runs
- Which
DATABASE_URLyour app uses
There is no live switching — environment selection happens at startup.
Top comments (0)