How to Use Docker Compose Profiles for Managing Multiple Environments

Many Docker Compose files referenced throughout this series define a fixed set of services that all start together every time. As a homelab grows, you often want more flexibility —…

docker

Many Docker Compose files referenced throughout this series define a fixed set of services that all start together every time. As a homelab grows, you often want more flexibility — starting only certain services for testing, or separating a “core” stack from optional add-ons without maintaining entirely separate Compose files. Docker Compose profiles solve exactly this, letting a single file define multiple selectively-startable groups of services.

What Are Compose Profiles?

Profiles let you tag services within a single docker-compose.yml file, then choose at startup time which tagged groups (profiles) should actually run. Services without any profile assigned always start normally; services with a profile assigned only start when that specific profile is explicitly requested.

Why This Matters for a Growing Homelab

Rather than maintaining separate Compose files for “core services” versus “optional monitoring stack” versus “testing environment,” profiles let you keep everything in one organized file while still controlling exactly what starts in a given situation — useful for testing a new service addition without also restarting your entire existing stack, or for maintaining a genuinely optional monitoring layer you don’t always want running.

Basic Profile Syntax

yaml

services:
  nginx-proxy-manager:
    image: jc21/nginx-proxy-manager:latest
    # No profile - always starts

  netdata:
    image: netdata/netdata:latest
    profiles: ["monitoring"]

  grafana:
    image: grafana/grafana:latest
    profiles: ["monitoring"]

  test-app:
    image: myapp:testing
    profiles: ["testing"]

Starting Services with a Specific Profile

docker compose --profile monitoring up -d

This starts both the un-profiled services (nginx-proxy-manager) and anything tagged with the monitoring profile (netdata, grafana), while leaving testing-profiled services stopped.

Starting Multiple Profiles Simultaneously

docker compose --profile monitoring --profile testing up -d

Setting a Default Profile via Environment Variable

Rather than specifying --profile on every command, set it as an environment variable for your current shell session or in a .env file:

COMPOSE_PROFILES=monitoring

A Practical Example: Separating Core and Optional Services

Building on the various services covered throughout this series, a single Compose file might organize things like:

yaml

services:
  nginx-proxy-manager:
    # core - always runs

  pihole:
    # core - always runs

  netdata:
    profiles: ["monitoring"]

  grafana:
    profiles: ["monitoring"]

  loki:
    profiles: ["monitoring"]

  n8n:
    profiles: ["automation"]

  freshrss:
    profiles: ["automation"]

Running just docker compose up -d starts only your genuinely essential core services, while docker compose --profile monitoring --profile automation up -d brings up everything.

Using Profiles for a Testing/Staging Setup

For testing a new version or configuration change before applying it to your actual running service (relevant to the general caution encouraged throughout this series’ more impactful changes), define a profile-tagged test variant alongside your production service definition:

yaml

services:
  myapp-production:
    image: myapp:stable
    # core - always runs

  myapp-testing:
    image: myapp:latest
    profiles: ["testing"]
    ports:
      - '8081:8080'  # Different port to avoid conflicting with production

Stopping Only Profile-Specific Services

docker compose --profile monitoring down

This stops only the services tagged with that profile, leaving your always-on core services running undisturbed.

Combining Profiles with .env Files

For different homelab contexts (a “home” environment versus perhaps a secondary test server), separate .env files can define different default COMPOSE_PROFILES values, letting the same underlying Compose file behave differently depending on which environment file is active when you run commands.

Profiles vs Separate Compose Files

Profiles work well for services that are genuinely related and might reasonably share configuration or networking within one file. For truly independent stacks with no meaningful relationship (Vaultwarden versus Immich, for instance, covered in separate earlier guides), separate Compose files per service often remains the clearer, more maintainable organizational choice rather than combining everything into one large profile-tagged file.