Advanced Compose: Profiles, depends_on, Health Conditions, Overrides, Includes, Watch, and Multi-File Design: Concepts, Architecture, and Mental Model
Scale Compose safely by reasoning about ordered files, include boundaries, profiles, dependency health, normalized configuration, and watch-driven reconciliation before changing project resources.
Learning objectives
- Explain how multiple Compose files, include models, profiles, interpolation, and dependency conditions become one normalized application model.
- Distinguish merge semantics from include semantics, especially their different relative-path rules and ownership boundaries.
- Explain what profiles activate, what they do not activate, and why targeting a profiled service is not equivalent to enabling the whole profile.
- Separate container start ordering from dependency readiness and from resilient application-level retry behavior.
- Use read-only Compose commands to prove active files, profiles, normalized services, paths, health conditions, and project resources before execution.
up will create.
1. Why advanced Compose becomes surprising
A single compose.yaml is easy to reason about. A real
development repository often adds an override for local source, an
optional database admin tool, test-only services, reusable
subprojects, health-gated dependencies, and a watch loop. The
complexity is not primarily YAML syntax; it is
composition order and state ownership.
Two files can both mention the same service, a path can resolve
relative to a different file than expected, a profile can silently
omit a service, and a healthy container can still be a poor proxy
for end-to-end application readiness. The answer is not more
trial-and-error up commands. The answer is to normalize
and inspect first.
2. Mental model: sources become one application model before Engine reconciliation
Think of advanced Compose as a compilation pipeline. Ordered files are parsed and merged. Included models are loaded with their own project-directory context. Variables are interpolated. Profiles select service participation. The resulting application model contains dependency conditions and watch rules. Only then does Compose reconcile the model with project-scoped Docker resources.
flowchart TD
A[Base compose.yaml] --> M[Model assembly]
B[Ordered override files] --> M
C[Included Compose models] --> M
D[Interpolation inputs] --> M
E[Active profiles] --> M
M --> N[docker compose config normalized model]
N --> G[Dependency graph conditions + health]
G --> R[Project reconciliation containers networks volumes]
R --> W[Optional watch loop sync / rebuild / sync+restart]
R --> O[Runtime evidence ps logs inspect health]
The critical causal boundary is between
model assembly and
runtime reconciliation.
docker compose config can validate and render the
former without creating containers. That makes it the first
diagnostic tool for merge, interpolation, profile, include, and path
questions.
3. State inventory: what must be recorded
| State | What it means | Evidence | Typical mistake |
|---|---|---|---|
| Ordered Compose files |
The sequence supplied with -f; later values can
override/extend earlier ones.
|
Command line, source revision, config output.
|
Reading only the last YAML file and assuming it is standalone. |
| Include source | A Compose application imported as a model after local file merge. |
include: declaration and normalized model.
|
Treating include as ordinary YAML merge. |
| Active profiles | Set of profile names participating in service selection. |
--profile/COMPOSE_PROFILES,
config --profiles.
|
Assuming all services in the file always start. |
| Normalized model | What Compose actually resolved after merge, include, interpolation, and profile logic. | docker compose ... config. |
Treating source fragments as final truth. |
| Dependency condition | When a dependent may be created/started relative to its dependency. | Normalized depends_on, health state. |
Equating ordering with application resilience. |
| Watch action | Development reaction to host file changes. | develop.watch plus watch event/output. |
Syncing secrets or generated dependencies unintentionally. |
| Resolved paths | Actual build contexts, bind paths, env files, and watch paths after path rules. | Normalized config plus filesystem inspection. | Assuming every override path is relative to its own file. |
| Project resources | Concrete Engine objects owned by the Compose project. |
Project labels, IDs, ps, network/volume
inspection.
|
Confusing model validity with successful runtime state. |
4. Merge files: order matters, and paths belong to the base file
When multiple files are supplied with -f, Compose
merges them in order. Matching scalar-like values are generally
overridden by later files while sequences/maps follow
Compose-specific merge rules. The important operational rule is
simpler:
inspect the merged model rather than inferring it from YAML
appearance.
Current Docker documentation resolves relative paths in merged files from the base Compose file (the first file supplied). This applies to build contexts, environment files, bind mounts, and similar resources. An override stored in another directory does not automatically make its paths relative to that override.
docker compose \
-f compose.yaml \
-f overlays/compose.dev.yaml \
config --quiet
docker compose \
-f compose.yaml \
-f overlays/compose.dev.yaml \
config > evidence/compose.dev.normalized.yaml
The first command changes no Engine state. It asks Compose to validate model construction. The second records exactly what Compose believes the development model is.
5. Include: compose application models, not fragments
include exists for modular application composition.
Each included Compose model has its own project-directory context
for relative paths, then its resources are copied into the current
model. This solves an ownership problem that ordinary overrides
intentionally do not solve.
Use include when a component is maintained as a meaningful Compose sub-model with its own directory and files. Use an override when you are intentionally changing the same project model, such as adding development mounts or changing a port. Compose warns on include resource-name conflicts instead of silently merging conflicting definitions.
6. Profiles select optional services, not arbitrary YAML blocks
A service without profiles is active by default. A
service with one or more profiles participates only when a matching
profile is enabled—unless that service is explicitly targeted by a
command. Targeting a profiled service auto-enables that service's
profile context for the target and its declared dependencies; it
does not automatically start every sibling service that happens to
share the profile.
docker compose config --profiles
# Example activation:
docker compose --profile debug config --services
# Enable all declared profiles for inspection:
docker compose --profile "*" config --services
Profiles are best for optional local tools, debugging, or test
helpers. Core services should usually remain unprofiled so a plain
docker compose up produces the application's baseline
model.
7. depends_on: ordering can be health-aware, but it is
not a distributed resilience mechanism
Current Compose supports dependency conditions including
service_started, service_healthy, and
service_completed_successfully. With
service_healthy, Compose waits for the dependency's
healthcheck to report healthy before starting the dependent service.
That is useful startup coordination. It does not eliminate the need for application-level retry/backoff when a dependency later restarts, the network changes, credentials rotate, or a remote service becomes unavailable. A robust application should tolerate dependency churn rather than relying on one startup gate.
8. Compose Watch is a development reconciliation loop
Watch monitors local source paths for services built from local
source. Current documentation requires Compose 2.22.0+ and documents
sync, rebuild, and
sync+restart. Watch also has prerequisites inside the
service image (stat, mkdir,
rmdir) and needs the configured container user to write
to sync targets.
Watch is not a deployment controller. It is a developer feedback loop. Its safety depends on narrow source paths, explicit targets, ignore rules, and excluding secret/configuration trees that should not be copied into a running container.
9. Read-only evidence-first workflow
docker version
docker info
docker context show
docker compose version
# Validate and render without creating resources:
docker compose -f compose.yaml -f compose.dev.yaml config --quiet
docker compose -f compose.yaml -f compose.dev.yaml config --services
docker compose -f compose.yaml -f compose.dev.yaml config --profiles
docker compose -f compose.yaml -f compose.dev.yaml config > evidence/model.yaml
# If a project already exists:
docker compose -p da-compose16 ps -a
docker ps -a --filter label=com.docker.compose.project=da-compose16
docker network ls --filter label=com.docker.compose.project=da-compose16
docker volume ls --filter label=com.docker.compose.project=da-compose16
Only after the normalized model matches intent should
up be allowed to reconcile Engine resources.
10. Five invariants for the rest of the chapter
- Normalize before execute. Treat merged YAML fragments as source, not resolved truth.
- Paths have ownership rules. Merged files use the base file; included models can keep their own project directory.
- Profiles control optional services. They do not make dependencies magically available.
- Health is scoped evidence. It proves only what the healthcheck actually tests.
- Watch is a dev loop. Do not let it become an uncontrolled secret/configuration synchronization mechanism.
Knowledge check
Why is docker compose config the first tool for a
suspected override problem?
Because it renders the normalized model after merge, interpolation, include, and profile processing without requiring runtime mutation.
Where are relative paths in an ordinary multi-file
-f merge resolved?
From the base Compose file, normally the first file supplied. They are not automatically relative to each override file.
How does include differ from an override
file?
An included Compose file is loaded as its own Compose application model with its own project-directory context, then its resources are copied into the current model.
Does service_healthy remove the need for
application retry logic?
No. It coordinates startup against one health signal; the application still needs resilience to later dependency failures and network/service churn.
Why can Compose Watch be dangerous if configured too broadly?
It can synchronize unintended files—including sensitive material—into containers, trigger unnecessary rebuild/restart loops, and create high I/O load.
Official references and version notes
- Docker Docs — Merge Compose files: ordered-file merge rules and base-file path resolution.
- Docker Docs — Include Compose files: per-included-model project-directory semantics and modular application models.
- Docker Docs — Profiles: conditional services, explicit profile activation, and targeted-service behavior.
-
Docker Docs — Startup order:
service_started,service_healthy, andservice_completed_successfully. -
Docker Docs — Compose Watch: prerequisites,
sync,rebuild, andsync+restart. -
Docker Docs —
docker compose config: normalized model, profiles, services, variables, hashes, and quiet validation. -
Compose services reference:
profiles,depends_on, health checks, and service attributes. - Docker Compose v5.5.1 (released 2026-09-03), current upstream baseline checked for this chapter.
Upstream
Compose is v5.5.1. include requires Compose 2.20.3+ in
current Docker documentation; Compose Watch requires 2.22.0+.
Because installations can lag or be vendor-packaged, every lab
records docker compose version and validates features
with docker compose config before changing runtime
state.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.