Advanced Compose: Profiles, depends_on, Health Conditions, Overrides, Includes, Watch, and Multi-File Design: Configuration, Design Choices, and Tradeoffs
Choose intentionally among override files, include, profiles, separate projects, dependency conditions, watch actions, and generated configuration, with each decision tied to observable Docker state.
Learning objectives
- Choose between overrides and include based on ownership, path context, conflict behavior, and desired modularity.
- Choose between profiles and separate Compose projects based on lifecycle isolation and resource ownership.
- Use dependency conditions as startup coordination while preserving application-level retry and failure recovery.
- Map Watch actions to the exact state they change and select the narrowest action that preserves a fast feedback loop.
- Compare environment overlays with generated configuration using reproducibility, auditability, portability, and rollback evidence.
1. Override files versus include
| Choose | Use when | Path behavior | Operational evidence | Main risk |
|---|---|---|---|---|
| Ordered override files | Same application needs dev/test/prod variations. | Relative paths resolve from the base Compose file. | Ordered -f list plus normalized config. |
Hidden merge assumptions or surprising path resolution. |
include |
A sub-domain/component is maintained as a meaningful Compose model. | Included model gets its own project-directory context. | Include source plus normalized integrated model. | Name conflicts or importing more capability than intended. |
An override is excellent for “same app, different local settings.” Include is better for “this team owns a reusable Compose sub-model.” Do not choose include merely because a file is long; choose it when the ownership and path context are independently meaningful.
2. Profiles versus separate projects
Profiles keep optional services in the same project identity. That is convenient for a debug UI, test runner, or local mail catcher that should share networks/resources with the core application. Separate projects are stronger isolation: independent project names, independent lifecycle commands, and normally separate project-scoped resource names.
| Scenario | Profiles | Separate projects | Why |
|---|---|---|---|
| Optional debug UI against same database | Good fit | Usually unnecessary | Shared application lifecycle/network is intentional. |
| Two independent integration environments | Weak fit | Good fit | Need separate project/resource identity and teardown boundaries. |
| One-shot test runner | Good fit | Possible | Profile/targeted service keeps it near the application model. |
| Untrusted helper owned by another team | Caution | Often safer | Separate lifecycle/trust boundary can reduce accidental coupling. |
3. depends_on conditions versus application retry
service_started coordinates creation/start ordering.
service_healthy waits for a configured healthcheck.
service_completed_successfully can gate on successful
completion of a one-shot dependency. These are useful orchestration
hints inside a Compose project.
They are not substitutes for resilient clients. A database can become unhealthy after the dependent service starts. DNS can briefly fail. A dependency can restart. Production-quality clients still need timeout, retry/backoff, idempotency where relevant, and meaningful error reporting.
4. Watch actions: change only the state you need
| Action | State changed | Best use | Evidence | Risk to control |
|---|---|---|---|---|
sync |
Files in the running container target. | Interpreted/hot-reload source. | Watch event plus file/app observation; container/image ID stable. | Syncing secrets, host-only artifacts, or incompatible native dependencies. |
rebuild |
Image and reconciled service container. | Dependencies or compiled artifacts requiring image rebuild. | Build trace, new image/container IDs. | Slow feedback and repeated dangling/superseded images. |
sync+restart |
Files then service process/container restart. | Configuration consumed at process startup. | Sync event plus restart/container state. | Restart loops from unstable watched files. |
Watch ignore rules are relative to each watch path. The
current documentation also applies .dockerignore rules
and ignores common temporary/editor files and .git.
Still, explicitly exclude credentials and large generated trees.
5. Environment overlays versus generated Compose configuration
Hand-authored overlays are reviewable and work well when differences are small and stable. Generated Compose configuration can be appropriate when another source of truth controls many machine-derived values, but the generation step becomes part of your supply chain and must be versioned, deterministic, and captured as evidence.
Never skip the normalized-model checkpoint just because a generator produced the YAML. The evidence chain becomes: generator version/input → generated Compose files → normalized model → project resources → application state.
6. Reconciliation is not “restart everything”
docker compose up compares desired model state with
existing project resources and creates/recreates as needed. Recent
Compose releases have continued to refine digest-based
reconciliation; v5.5.0 specifically changed image digest
reconciliation. That is one reason courses should not hard-code
assumptions such as “up always recreates” or “up never recreates.”
When a configuration change matters, record the before/after container ID, image digest/ID, labels, mounts, networks, and health state. Let evidence show whether reconciliation replaced the object.
7. Worked decision: three environments, one repository
Suppose a team needs a stable core app, a local debugger, and a CI test runner:
| Need | Choice | Prerequisites | Predicted state | Evidence |
|---|---|---|---|---|
| Core app | Base compose.yaml |
Current Compose, explicit project naming. | Always-active core services. | Normalized base model + project labels. |
| Local debugger | debug profile |
Tool is authorized to share app network/resources. | Extra service only when profile active. |
config --services with/without profile +
runtime inventory.
|
| Local source edits | Dev override + Watch | Compose 2.22+, writable target, local build. | Sync/rebuild only app service. | Watch events + image/container IDs. |
| Reusable dependency model | include |
Compose 2.20.3+, no conflicting resource names. | Imported services/resources with their own path context. | Include source revision + normalized model. |
| CI test isolation | Distinct project name per run | Unique project ID and bounded cleanup. | Independent project-scoped containers/networks/volumes. | Project labels and exact teardown. |
8. Trust and least privilege still apply to Compose convenience
An included file, override, or generated model can request host bind mounts, privileged mode, devices, host namespaces, or the Docker socket. Model composition is therefore a trust boundary. Review imported Compose models before execution, especially when they originate from another repository or branch.
The safe workflow is to inspect source provenance and normalized output, reject unnecessary privilege, and run untrusted workloads on appropriately isolated builders/hosts—not to treat Compose modularity as a security sandbox.
Knowledge check
When should you prefer include over an
override?
When the imported component is a meaningful independently maintained Compose model whose own path context and ownership should be preserved.
Why are profiles weaker isolation than separate projects?
Profiled and unprofiled services still belong to the same Compose project and can intentionally share project-scoped resources/lifecycle; separate project names create distinct ownership boundaries.
Which Watch action is appropriate when a dependency file changes and the image must be rebuilt?
rebuild, because the desired state is a new image
and reconciled service container rather than only updated
runtime files.
What is the limitation of service_healthy?
It gates startup on a healthcheck result at that time; it does not provide ongoing application retry/resilience after startup.
Why should a generated Compose file still be passed through
docker compose config?
The generator adds another input stage; normalized Compose output remains the authoritative view of what Compose will apply after all file/interpolation/profile rules.
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.