Chapter 16Lesson 05~150 minutes

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.

Checkpoint labBase/dev/testEvidence packetProfilesReconciliation

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.
Checkpoint objective. Success is not “the app opened.” Success is an evidence-backed explanation of how source files became a normalized Compose model, which profiles/services were active, how dependency health affected startup, what Watch changed, which resources Compose reconciled, and why cleanup touched only the lab.

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

  1. Prediction 1: base model activates readiness and app; inspector remains inactive because the test profile is off.
  2. Prediction 2: dev model keeps loopback port 18017 and adds Watch rules/environment to app.
  3. Prediction 3: test model with --profile test includes inspector and removes the app's host-port publication because the test override sets an empty ports list.
  4. Prediction 4: app startup is delayed until readiness becomes healthy; later dependency failure is not automatically “fixed” by depends_on.
  5. 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 inspector appears only when expected.
  • readiness health 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?

The inspector container is absent in base but present in test. What mechanism caused that?

Why capture both container ID and image ID around Watch events?

If readiness becomes unhealthy after app starts, will depends_on: condition: service_healthy guarantee app recovery?

What is the first artifact to inspect when dev and test create unexpected resources?

What Chapter 17 boundary follows from this checkpoint?

Next lesson

Next: Container Networking Foundations: Bridge, Host, None, User-Defined Networks, Endpoints, and Namespaces: Concepts, Architecture, and Mental Model

Continue with the next lesson in the course sequence and carry forward the evidence-first Docker operating model.

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.