Chapter 16Lesson 03~115 minutes

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.

Design choicesPath rulesReconciliationDevelopment loopTradeoffs

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.
Design rule. Prefer the mechanism whose ownership model matches the problem. Overrides customize one application model. Includes compose independently owned models. Profiles conditionally enable services in one project. Separate projects create separate lifecycle/resource boundaries.

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.

Healthcheck design: test the layer required by the dependent service. A TCP socket check may prove a listener but not schema readiness; a deep query may be expensive or require sensitive credentials. Choose the smallest probe that represents the dependency contract.

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.

Next lesson

Next: Diagnostics, Failure Modes, Security, and Performance

Next, deliberately break merge paths, profiles, health logic, and watch rules. You will preserve the normalized model and runtime evidence so each failure is corrected at the causal layer rather than hidden by recreation.

Knowledge check

When should you prefer include over an override?

Why are profiles weaker isolation than separate projects?

Which Watch action is appropriate when a dependency file changes and the image must be rebuilt?

What is the limitation of service_healthy?

Why should a generated Compose file still be passed through docker compose config?

Official references and version notes

Version baseline checked 2026-09-21.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.