What are Compose profiles, and when would you use them?

6 minintermediatecompose-profilesoptional-services

Quick Answer

Profiles let you tag services in a single compose file so they're only started when their profile is explicitly activated — services with no profile assigned always start by default, while profile-tagged services are opt-in. This is useful for including optional or situational services (debugging tools, a local monitoring stack, a service only needed for a specific workflow) in the same compose file as your core application, without them cluttering or slowing down every ordinary docker compose up.

Detailed Answer

The problem: not every service should always start

A single compose file for a real project often accumulates services that aren't needed for every single docker compose up — a local monitoring dashboard, a debugging/admin tool, a service that's only relevant for a specific kind of testing. Without profiles, every one of these either has to be commented in and out manually, or has to live in an entirely separate compose file that needs to be merged in manually. Both approaches are more awkward than necessary.

Tagging services with a profile

services:
  api:
    image: myapi:1.0
    # no "profiles" key -- this service ALWAYS starts, regardless of active profiles

  db:
    image: postgres:16
    # also always starts

  pgadmin:
    image: dpage/pgadmin4
    profiles: ["debug"]        # only starts if the "debug" profile is explicitly activated

  load-tester:
    image: my-load-test-tool
    profiles: ["testing"]

Activating profiles

docker compose up -d
# Only "api" and "db" start -- pgadmin and load-tester are skipped entirely

docker compose --profile debug up -d
# Now "api", "db", AND "pgadmin" all start

docker compose --profile debug --profile testing up -d
# Multiple profiles can be activated together

A service with no profiles key at all is always included, regardless of which profiles, if any, are active. Profiles are purely an opt-in mechanism for the services explicitly tagged with one. They do not let you exclude a service that has no profile assigned.

Also settable via an environment variable

export COMPOSE_PROFILES=debug
docker compose up -d      # equivalent to passing --profile debug

Useful for setting a consistent default profile for a given developer's local environment or a specific CI job, without needing to remember to pass the flag on every single command.

Common real-world use cases

  • Debugging/admin tools — a database GUI (pgAdmin, Adminer), only needed occasionally, not on every developer's routine docker compose up.
  • Optional local observability — a local Prometheus/Grafana stack for developers who specifically want to inspect metrics locally, without imposing that overhead on everyone by default.
  • Environment- or workflow-specific services — a service only relevant for running a particular kind of test suite (a mock external API, a load-testing tool), kept in the same file as the core application for convenience, without slowing down or cluttering the default startup.

Why this beats maintaining multiple separate compose files

Before profiles existed, achieving this same "optional service, sometimes included" behavior typically required one of two approaches: multiple separate compose files merged together with -f base.yaml -f debug.yaml, or manually commenting services in and out. Both are more error-prone and harder to keep in sync than a single file with declarative, named profile tags that anyone on the team can discover just by reading the file.

Related Resources