DEV Community

teaganga
teaganga

Posted on

Using Docker Compose Profiles to unit tests part of the application

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"]
Enter fullscreen mode Exit fullscreen mode

How it works

  • docker compose up (no --profile)
    Starts only services without a profiles key.
    In the example above, nothing would start unless you define a default (non-profiled) service.

  • docker compose --profile core up
    Starts only services tagged with core (postgres, jobrunner).
    Ideal for a lightweight testing stack.

  • docker compose --profile full up
    Starts only services tagged with full (nextjs, authserver).

  • Multiple profiles are supported:

  docker compose --profile core --profile full up
Enter fullscreen mode Exit fullscreen mode

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 profiles key. 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"]
Enter fullscreen mode Exit fullscreen mode

Database Configuration Strategy

  • Your application containers use one DATABASE_URL at a time.
  • Each profile startup determines which database the app connects to.
  • For example:

    • In a test profile, set DATABASE_URL to unit-test-db.
    • In a prod profile, set DATABASE_URL to app-db.

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.yml
  • docker-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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Prod profile → connect to app-db

postgresql://prod:prod@app-db:5432/proddb
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

.env.prod

DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb
Enter fullscreen mode Exit fullscreen mode

Start test stack

docker compose --profile test --env-file .env.test up
Enter fullscreen mode Exit fullscreen mode

Start prod stack

docker compose --profile prod --env-file .env.prod up
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Prod

DATABASE_URL=postgresql://prod:prod@app-db:5432/proddb \
docker compose --profile prod up
Enter fullscreen mode Exit fullscreen mode

4️⃣ What Actually Starts

When you run:

docker compose --profile test up
Enter fullscreen mode Exit fullscreen mode

Only:

  • unit-test-db
  • app

will start.

When you run:

docker compose --profile prod up
Enter fullscreen mode Exit fullscreen mode

Only:

  • app-db
  • app

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_URL your app uses

There is no live switching — environment selection happens at startup.

Top comments (0)