Developer

Understanding Service Inclusion with Docker Compose Profiles

Docker Compose profiles control which services are included for a particular use case. They are useful when one Compose file contains optional debugging tools, migration jobs, or development services.

To predict the result of a Compose command, separate two questions:

  1. Which services enter the application model?
  2. Which services does the command actually start?

That distinction becomes important when you directly target a profiled service. Compose can activate the target’s profile without starting every other service assigned to that profile. Dependencies introduce another constraint: a reference to an excluded service does not always activate it.

Profiles apply to services

The profiles attribute belongs to a service definition. Services without it are always enabled. A profiled service is ignored when none of its profiles are active, unless a command explicitly targets that service. Other top-level elements are not affected by profiles and remain active in the model (Docker profiles reference).

In other words, profiles select services. They do not conditionally remove top-level networks, volumes, configs, or other non-service elements.

A service can be assigned to one or more profiles:

services:
  backend:
    image: backend

  debugger:
    image: debugger
    profiles:
      - debug

Here, backend is always enabled because it has no profiles attribute. debugger is enabled when debug is active or when a command directly targets debugger.

Enabling profiles explicitly

You can enable a profile with --profile:

docker compose --profile debug up

Alternatively, use the COMPOSE_PROFILES environment variable:

COMPOSE_PROFILES=debug docker compose up

Multiple profiles can be enabled with repeated flags or a comma-separated environment variable:

docker compose --profile frontend --profile debug up
COMPOSE_PROFILES=frontend,debug docker compose up

Docker also documents --profile "*" for enabling all profiles (Using profiles with Compose).

Explicitly enabling a profile makes its assigned services eligible under the application model. Direct service targeting has a narrower command-level effect: it starts the target and its applicable depends_on dependencies, but not unrelated services merely because they share the target’s automatically activated profile (Using profiles with Compose).

Docker’s four-service example

The profiles reference illustrates these rules with four services:

services:
  web:
    image: web_image

  test_lib:
    image: test_lib_image
    profiles:
      - test

  coverage_lib:
    image: coverage_lib_image
    depends_on:
      - test_lib
    profiles:
      - test

  debug_lib:
    image: debug_lib_image
    depends_on:
      - test_lib
    profiles:
      - debug

The relationships are straightforward but produce different outcomes:

  • web has no profile, so it is always enabled.
  • test_lib belongs to test.
  • coverage_lib belongs to test and depends on test_lib.
  • debug_lib belongs to debug but depends on test_lib, which belongs to test.

That last dependency demonstrates why depends_on does not universally bring a profile-excluded service into the model.

Documented outcomes

The following results are limited to Docker’s official example (Docker profiles reference).

Profiles or explicit target Documented outcome
No profile enabled The application model contains only web.
test enabled The model contains web, test_lib, and coverage_lib.
debug enabled The model contains web and debug_lib, but is invalid because debug_lib depends on the excluded test_lib.
test and debug enabled The model contains all four services.
Target test_lib test_lib and the test profile become active.
Target coverage_lib coverage_lib and the test profile become active; test_lib is pulled in through depends_on.
Target debug_lib The model is invalid because debug_lib and its test_lib dependency have no common profile.
Enable test and target debug_lib Targeting activates debug; test_lib is pulled in, and both debug_lib and test_lib start.

The important contrast is between coverage_lib and debug_lib.

Both coverage_lib and its dependency test_lib belong to test. When coverage_lib is targeted, its profile becomes active and Docker’s example says that test_lib is pulled in through depends_on.

By comparison, debug_lib belongs to debug, while test_lib belongs to test. Enabling only debug, or targeting only debug_lib, does not activate test. The documented result is an invalid model rather than automatic expansion into the dependency’s profile.

If test is explicitly enabled while debug_lib is targeted, both sides of the relationship are available. Targeting activates debug, while the explicitly enabled test profile makes test_lib available as a dependency.

Targeting a service is not the same as enabling its profile

When you explicitly target a profiled service, Compose adds that service’s profile to the active-profile set. However, the corresponding command starts only the target and its declared depends_on dependencies. Other services sharing the profile are not started unless they are also targeted or the profile is explicitly enabled with --profile or COMPOSE_PROFILES (Using profiles with Compose).

These statements can therefore both be true:

  • The targeted service’s profile becomes active.
  • Unrelated services assigned to that profile do not start.

Application-model activation and a command’s start set should not be treated as interchangeable.

References other than depends_on also have explicit limits. References through links, extends, or shared-resource syntax such as service:xxx do not automatically activate a service excluded by profiles. Compose returns an error instead (Docker profiles reference).

depends_on does not guarantee readiness

Profile selection determines whether a dependency can participate in the model. It does not show that the dependency is healthy or ready to serve requests.

With short-form depends_on, Compose starts dependency services before the dependent service, but does not wait for those dependencies to become healthy (Compose services reference):

depends_on:
  - db

Long-form depends_on can specify a condition such as service_healthy, but that condition is separate from profile activation.

When evaluating a Compose command, ask:

  1. Which services have no profile?
  2. Which profiles were explicitly enabled?
  3. Was a profiled service targeted directly?
  4. Are its required dependencies available under that profile arrangement?
  5. Are you asking about model membership or the command’s start set?
  6. Does the dependency require a separate health condition?

Following those questions keeps profile activation, dependency validity, and service startup from being conflated.

Sources and further reading

Back to top ↑

Rody

Founder & CEO · RodyTech LLC

Founder of RodyTech LLC in Iowa. I write practical notes on automation, infrastructure, security, and software decisions for builders and business operators.

No comments yet

Leave a comment

Your email address will not be published. Required fields are marked *