Running a Subset of Services with Compose Profiles

You ran docker compose up and a service you expected never started — or one you wanted excluded came up anyway — because profile selection at launch does not match how the services are tagged. This page covers the exact --profile and COMPOSE_PROFILES mechanics for launching a subset, building on the design in Compose Profiles and Targeted Environments.

Diagnostic

Confirm which services a given selection actually resolves to before you launch:

#!/usr/bin/env bash
# what will actually start?
set -euo pipefail
echo "== default (no profile) =="
docker compose config --services
echo "== with frontend profile =="
docker compose --profile frontend config --services
# BAD: the worker you needed is gated behind a profile you didn't select
== default (no profile) ==
app
db
# (worker is missing — it carries profiles: ["full-stack"])

Root cause

A service tagged with profiles: is excluded by default and only starts when one of its profiles is requested — via a --profile <name> flag or the COMPOSE_PROFILES environment variable. Untagged services always start. If you launch without naming the profile that contains a service, Compose silently leaves it out; there is no error, just a missing container.

Resolution

  1. Start a single profile by name. This launches untagged services plus everything tagged with that profile.
#!/usr/bin/env bash
set -euo pipefail
docker compose --profile frontend up -d --wait
  1. Select multiple profiles by repeating the flag. The union of all named profiles starts.
#!/usr/bin/env bash
set -euo pipefail
docker compose --profile full-stack --profile observability up -d --wait
  1. Or set COMPOSE_PROFILES (comma-separated) so the selection applies to every subsequent compose command in the shell, including down, logs, and ps.
#!/usr/bin/env bash
set -euo pipefail
export COMPOSE_PROFILES=full-stack,observability
docker compose up -d --wait
docker compose ps
  1. Start one specific profiled service directly by naming it — Compose enables that service's profile automatically when you target it explicitly.
#!/usr/bin/env bash
set -euo pipefail
docker compose run --rm worker npm run worker:once
  1. Tear down using the same profile selection, or the profiled containers linger.
#!/usr/bin/env bash
set -euo pipefail
docker compose --profile full-stack --profile observability down --remove-orphans

Expected output

[+] Running 3/3
 ✔ Container db-1     Healthy
 ✔ Container app-1    Started
 ✔ Container worker-1 Started
docker compose --profile full-stack config --services
# app
# db
# worker

Prevention

  1. Pin the team default in a committed .env so the common case needs no flag, and document the override in the README.
# .env
COMPOSE_PROFILES=full-stack
  1. Avoid the most common pitfall: an always-on service that depends_on a profiled service. Compose will pull the profiled dependency in even when you did not select it, producing surprising startups. Keep dependencies in the same profile as their dependents, or leave shared dependencies untagged.
#!/usr/bin/env bash
# bin/check-profile-deps.sh — fail if a non-profiled service depends on a profiled one
set -euo pipefail
docker compose config --services > /tmp/always.txt
docker compose --profile full-stack --profile observability config --services > /tmp/all.txt
if ! diff -q /tmp/always.txt /tmp/all.txt >/dev/null; then
  echo "Profiled services exist; verify no always-on service depends on them." >&2
fi

macOS (Docker Desktop): all selected profiles share the VM's resource budget; selecting observability on top of full-stack can exceed the RAM slider and trigger OOM kills. WSL2: COMPOSE_PROFILES exported in PowerShell does not reach the distro; export it inside WSL2 or rely on the committed .env. Apple Silicon (ARM64): if a profiled image lacks an arm64 manifest, pin platform: linux/amd64 on that one service so a subset launch does not fail under emulation.

Rollback

#!/usr/bin/env bash
set -euo pipefail
unset COMPOSE_PROFILES
docker compose --profile full-stack --profile observability down --remove-orphans