Checkpoint Lab — Advanced Compose: Profiles, depends_on, Health Conditions, Overrides, Includes, Watch, and Multi-File Design
Validate a base/dev/test Compose design before execution, prove profile/resource boundaries and health-gated startup, exercise safe watch behavior, and capture an evidence packet for reproducibility.
Learning objectives
- Design and validate a base/dev/test Compose model using one profile, one override, one include, a health-gated dependency, and Watch.
- Predict project/model/runtime state changes before execution and verify each prediction independently.
- Capture version, normalized configuration, active profiles, project resources, image/container identity, health, and watch evidence in one packet.
- Prove that development/test variants create only intended resources and that teardown preserves or deletes data only by explicit intent.
- Bridge advanced Compose reasoning to Chapter 17 by separating Compose model behavior from the underlying Docker network primitives.
1. Setup and assumptions
Use the Chapter 16 lab files from Lesson 2 or recreate them in a
fresh da-compose16-checkpoint directory. The mandatory
path is entirely local/free. It uses BusyBox 1.36.1, local BuildKit
through Compose build integration, a loopback-only published port,
and no registry/cloud/secret provider.
mkdir -p da-compose16-checkpoint/{app,tools,overlays,evidence,readiness}
cd da-compose16-checkpoint
docker version > evidence/docker-version.txt
docker info > evidence/docker-info.txt
docker context show > evidence/docker-context.txt
docker compose version > evidence/compose-version.txt
docker buildx version > evidence/buildx-version.txt
docker ps -a --filter label=com.docker.compose.project=da-compose16cp > evidence/preflight-containers.txt
Assumptions note: record whether your Compose
supports include and Watch. On current upstream v5.5.1
both are supported. If an older installation lacks one feature,
preserve the validation error and use the documented
simulation/fallback: ordinary multi-file composition for include
concepts, and manual copy/rebuild observation for Watch concepts. Do
not upgrade a production host solely for this lab.
2. Exact checkpoint configuration
Create app/index.html:
<!doctype html><html><body><h1>Compose 16 checkpoint</h1></body></html>
Create app/Dockerfile:
# syntax=docker/dockerfile:1
FROM busybox:1.36.1
WORKDIR /site
COPY --chown=65534:65534 index.html /site/index.html
USER 65534:65534
EXPOSE 8080
CMD ["httpd", "-f", "-p", "8080", "-h", "/site"]
Create readiness/index.html:
ready
Create tools/compose.yaml:
services:
inspector:
image: busybox:1.36.1
profiles: [test]
command: ["sh", "-c", "echo inspector-active; exec sleep 3600"]
labels:
devops-academy.lab: compose16-checkpoint
Create base compose.yaml:
name: da-compose16cp
include:
- ./tools/compose.yaml
services:
readiness:
image: busybox:1.36.1
command: ["httpd", "-f", "-p", "8090", "-h", "/www"]
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8090/"]
interval: 2s
timeout: 1s
retries: 10
volumes:
- ./readiness:/www:ro
labels:
devops-academy.lab: compose16-checkpoint
app:
build: ./app
image: da-compose16cp-app:lab
depends_on:
readiness:
condition: service_healthy
ports:
- "127.0.0.1:18017:8080"
labels:
devops-academy.lab: compose16-checkpoint
Create overlays/compose.dev.yaml:
services:
app:
environment:
APP_VARIANT: dev
develop:
watch:
- action: sync
path: ./app
target: /site
initial_sync: true
ignore:
- Dockerfile
- action: rebuild
path: ./app/Dockerfile
Create overlays/compose.test.yaml:
services:
app:
environment:
APP_VARIANT: test
ports: []
3. Predictions before execution
-
Prediction 1: base model activates
readinessandapp;inspectorremains inactive because thetestprofile is off. - Prediction 2: dev model keeps loopback port 18017 and adds Watch rules/environment to app.
-
Prediction 3: test model with
--profile testincludesinspectorand removes the app's host-port publication because the test override sets an empty ports list. -
Prediction 4: app startup is delayed until
readiness becomes healthy; later dependency failure is not
automatically “fixed” by
depends_on. - Prediction 5: Watch sync changes app runtime files without an image rebuild; Dockerfile edit triggers a build/reconciliation path.
4. Validate all environment models before any up
docker compose -f compose.yaml config --quiet
docker compose -f compose.yaml config > evidence/base.normalized.yaml
docker compose -f compose.yaml -f overlays/compose.dev.yaml config --quiet
docker compose -f compose.yaml -f overlays/compose.dev.yaml config > evidence/dev.normalized.yaml
docker compose -f compose.yaml -f overlays/compose.test.yaml --profile test config --quiet
docker compose -f compose.yaml -f overlays/compose.test.yaml --profile test config > evidence/test.normalized.yaml
docker compose -f compose.yaml config --profiles > evidence/base.profiles.txt
docker compose -f compose.yaml -f overlays/compose.test.yaml --profile test config --services > evidence/test.services.txt
Review the three normalized files side by side. This is where you prove the intended environment differences without creating anything.
5. Execute the base model and verify health-gated startup
docker compose -f compose.yaml up -d --build
docker compose -f compose.yaml ps -a > evidence/base.ps.txt
docker inspect da-compose16cp-readiness-1 --format '{{json .State.Health}}' > evidence/readiness.health.json
docker inspect da-compose16cp-app-1 --format 'id={{.Id}} image={{.Image}} state={{.State.Status}}' > evidence/app.base.identity.txt
curl -fsS http://127.0.0.1:18017/ > evidence/app.base.response.html
Verify that inspector does not exist. Its absence is
intended profile state, not failure.
6. Prove the profile/environment resource boundary
Bring down the base project first so the environment transition is explicit, then apply the test model with the profile enabled:
docker compose -f compose.yaml down
docker compose \
-f compose.yaml \
-f overlays/compose.test.yaml \
--profile test \
up -d --build
docker compose -f compose.yaml -f overlays/compose.test.yaml --profile test ps -a > evidence/test.ps.txt
docker ps -a --filter label=com.docker.compose.project=da-compose16cp --no-trunc > evidence/test.engine-resources.txt
Expected: inspector exists in the test profile view.
The test model should not publish the dev/base host port if the
normalized model confirms the ports override. If the observed result
differs, trust the normalized model and actual Engine inspection
over assumptions about list merging.
7. Exercise the dev Watch path
Transition cleanly to the dev model:
docker compose -f compose.yaml -f overlays/compose.test.yaml --profile test down
docker compose -f compose.yaml -f overlays/compose.dev.yaml up -d --build
docker compose -f compose.yaml -f overlays/compose.dev.yaml ps -q app > evidence/dev.app-id.before-watch.txt
docker image inspect da-compose16cp-app:lab --format '{{.Id}}' > evidence/dev.image-id.before-watch.txt
Run Watch in a second terminal:
docker compose -f compose.yaml -f overlays/compose.dev.yaml watch app
Modify the page in the first terminal:
printf '%s
' '<!doctype html><html><body><h1>Compose 16 checkpoint synced</h1></body></html>' > app/index.html
sleep 2
curl -fsS http://127.0.0.1:18017/ > evidence/dev.after-sync.html
docker compose -f compose.yaml -f overlays/compose.dev.yaml ps -q app > evidence/dev.app-id.after-sync.txt
docker image inspect da-compose16cp-app:lab --format '{{.Id}}' > evidence/dev.image-id.after-sync.txt
For the sync event, container/image identity should normally remain stable while runtime file content changes. Now add a harmless Dockerfile label to trigger rebuild, observe Watch, then record new identities. Stop Watch after collecting evidence.
printf '
LABEL devops-academy.checkpoint-rebuild="1"
' >> app/Dockerfile
# After the watch rebuild completes:
docker compose -f compose.yaml -f overlays/compose.dev.yaml ps -q app > evidence/dev.app-id.after-rebuild.txt
docker image inspect da-compose16cp-app:lab --format '{{.Id}}' > evidence/dev.image-id.after-rebuild.txt
8. Evidence packet checklist
| Evidence | Why it matters |
|---|---|
| Docker/Compose/Buildx versions + context | Proves client/daemon/tool assumptions. |
| Base/dev/test normalized YAML | Proves exact model after file/include/profile/interpolation processing. |
| Profiles/services lists | Proves optional-service selection. |
| Container IDs and image IDs before/after Watch | Separates sync from rebuild/reconciliation. |
| Dependency health JSON | Proves what the startup gate observed. |
| Project resource inventory with Compose labels | Proves concrete Engine ownership. |
| HTTP response evidence | Proves external application path separately from container running state. |
| Assumptions/limitations note | Documents platform/version differences and any simulated capability. |
Create a concise assumptions note:
cat > evidence/ASSUMPTIONS.txt <<'EOF'
Lab: Docker Compose Chapter 16 checkpoint
Upstream baseline checked: 2026-09-21
Mandatory path: local Docker Engine/Desktop-compatible Compose; no cloud services
No real secrets or production endpoints used
Watch evidence is development-only and not a production deployment mechanism
Healthcheck proves only the local readiness endpoint defined in compose.yaml
EOF
9. Verification checklist
- All three models validated successfully before runtime execution.
- Base/dev/test normalized outputs differ only where intended.
-
Profile-gated
inspectorappears only when expected. -
readinesshealth evidence exists before app startup evidence is accepted. - Project labels identify only checkpoint resources.
- Watch sync changed runtime content without being mistaken for an image rebuild.
- Watch rebuild produced image/container reconciliation evidence.
- No real secret, Docker socket, privileged mode, or broad host mount was introduced.
10. Cleanup and rollback
docker compose -f compose.yaml -f overlays/compose.dev.yaml down --remove-orphans
# Verify project ownership is gone:
docker ps -a --filter label=com.docker.compose.project=da-compose16cp
docker network ls --filter label=com.docker.compose.project=da-compose16cp
# Remove only the exact disposable image after checking no container uses it:
docker image rm da-compose16cp-app:lab 2>/dev/null || true
# Keep evidence/ if you want to review the checkpoint packet.
No broad prune or volume deletion is necessary. The lab has no named persistent volume; all cleanup remains project-scoped.
11. What Chapter 16 adds to a production Docker operating model
You can now treat multi-file Compose as a deterministic model transformation rather than “several YAML files that happen to work.” A secure operating model records file order, include sources, profile activation, normalized output, dependency/health semantics, exact image identity, and project resource ownership before relying on runtime success.
You also have a disciplined development loop: Watch can accelerate local iteration, but its scope and state changes are explicit, and a runtime sync is never confused with a rebuilt release artifact.
12. Bridge to Chapter 17
Compose names networks and connects service endpoints, but those conveniences sit on Docker networking primitives. Chapter 17 drops beneath the Compose model to inspect bridge, host, none, user-defined networks, endpoints, and network namespaces directly. Keep the Chapter 16 distinction in mind: Compose declares connectivity; the Engine/network driver implements it.
Knowledge check
Why must the checkpoint preserve base/dev/test normalized models?
They prove the exact application model Compose resolved for each environment and let runtime resource differences be traced back to declared inputs.
The inspector container is absent in base but present in test. What mechanism caused that?
The test profile activation, not a different Docker
daemon or image-store behavior.
Why capture both container ID and image ID around Watch events?
A sync can change runtime files while preserving image/container identity, while a rebuild changes image state and usually causes service reconciliation/replacement.
If readiness becomes unhealthy after app starts, will
depends_on: condition: service_healthy guarantee
app recovery?
No. It is a startup coordination condition; ongoing resilience still belongs to application retry/reconnection behavior and appropriate operational controls.
What is the first artifact to inspect when dev and test create unexpected resources?
The normalized Compose model for each exact ordered file/profile invocation, before deleting or recreating runtime resources.
What Chapter 17 boundary follows from this checkpoint?
Compose declares project networks and service attachments, while Docker networking drivers/endpoints/namespaces implement the actual connectivity.
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.