Chapter 15Lesson 04~115 minutes

Docker Compose Foundations: Services, Images, Builds, Networks, Volumes, Environment, and Project Lifecycle: Diagnostics, Failure Modes, Security, and Performance

Diagnose Compose failures without hiding first-failure evidence: project-name drift, environment/interpolation mistakes, readiness assumptions, unsafe volume teardown, service-name/network confusion, and build/image mismatch.

DiagnosticsReadinessProject isolationData safetyEvidence

Learning objectives

  • Preserve Compose source, normalized config, project identity, image IDs/digests, container state, logs, network/volume evidence, and application symptoms before correcting a failure.
  • Diagnose project-name mismatch, interpolation mistakes, service-start versus readiness problems, network/service-name confusion, and data-lifecycle mistakes causally.
  • Explain why down --volumes, broad prune, forced recreation, privileged containers, and secret printing are poor first-line troubleshooting shortcuts.
  • Use one intentionally broken configuration to demonstrate failure at model-validation time and one runtime example to distinguish internal connectivity from published-port failure.
  • Apply the least destructive correction and rerun only the smallest safe scope.
Evidence first. Before “fixing Compose,” establish which context, project, resolved model, images, containers, networks, volumes, and application endpoints produced the first failure. A destructive cleanup may make the symptom disappear while destroying the cause.

1. Diagnostic sequence: move from cheapest boundary to deepest runtime state

  1. Preserve the original error, command, timestamp, Compose source, and docker compose config output if it can be rendered safely.
  2. Confirm docker version, docker info, active context, and docker compose version.
  3. Confirm the exact project name and Compose file set.
  4. Confirm source/build/image identity: build trace, image ID/digest, pull policy.
  5. Inspect compose ps -a, container inspect, exit code, health, and logs.
  6. Inspect mounts/volumes/resources/security settings.
  7. Inspect project networks, DNS/service names, published ports, and application listeners.
  8. Inspect registry/external dependencies only when local evidence points outward.
  9. Apply the smallest correction and rerun the smallest affected scope.

2. Capture a compact evidence packet before mutation

docker version
docker context show
docker compose version

docker compose config > evidence/compose-config-before.yaml
docker compose config --environment > evidence/interpolation-before.txt
docker compose ps -a --format json > evidence/ps-before.json
docker compose images > evidence/images-before.txt
docker compose logs --no-color --timestamps --tail=200 > evidence/logs-before.txt

docker ps -a --filter label=com.docker.compose.project=da-compose15
docker network ls --filter label=com.docker.compose.project=da-compose15
docker volume ls --filter label=com.docker.compose.project=da-compose15

Do not dump real secret values into an evidence packet. Record the source/mechanism and whether a value was present, not the secret itself.

3. Intentionally broken example: fail safely at model-resolution time

Create a disposable copy of the lab and temporarily remove WEB_PORT from its interpolation inputs while the Compose file uses ${WEB_PORT:?set WEB_PORT}. Then run:

docker compose config --quiet

The expected failure says the required variable is unset. That is valuable: no container, network, or volume needs to be created. The cause is the Compose interpolation/configuration layer, not BuildKit, Nginx/BusyBox, the Docker daemon, or a firewall.

Repair: restore the synthetic non-secret WEB_PORT value in the intended source, re-run config --environment and config --quiet, then proceed. Do not “fix” this by hard-coding a secret or deleting Docker state.

4. Failure mode: the same files target a different project

If yesterday you used project da-compose15 but today execute with -p da-compose15-debug, compose ps can appear empty or up can create a second set of resources. Before claiming Docker “lost containers,” prove the project identity.

docker compose ls
docker compose -p da-compose15 ps -a
docker compose -p da-compose15-debug ps -a

docker ps -a --filter label=com.docker.compose.project=da-compose15
docker ps -a --filter label=com.docker.compose.project=da-compose15-debug

5. Failure mode: container started, dependency not ready

A process can be running while the application is still initializing or unhealthy. docker compose ps may show Up before a service-level probe succeeds. Preserve logs and health state; do not immediately restart every service.

docker compose ps -a
docker inspect "$(docker compose ps -q web)" --format '{{json .State}}'
docker compose logs --no-color --timestamps web
docker compose exec probe wget -S -O - http://web:8080/

If Chapter 15 uses a health check, inspect it. Chapter 16 will teach dependency health conditions in depth. The core lesson here is that ordering and readiness are different.

6. Failure mode: assuming a service name works in every network scenario

Service-name DNS is available to containers attached to the relevant Compose network. It is not a universal hostname on the host, on unrelated networks, or in network_mode: host. First inspect network attachments, then resolve from the failing consumer's network namespace.

docker compose exec probe sh -c 'cat /etc/resolv.conf; nslookup web || true'
docker inspect "$(docker compose ps -q probe)" --format '{{json .NetworkSettings.Networks}}'
docker inspect "$(docker compose ps -q web)" --format '{{json .NetworkSettings.Networks}}' 

7. Failure mode: deleting data to troubleshoot an unrelated service problem

docker compose down and volume deletion are deliberately separate. If the web endpoint is failing, deleting the application data volume is rarely a justified first action. Preserve the exact volume identity, inspect mounts, and verify whether the symptom is even storage-related.

Disruptive action. The option that tells compose down to remove project named/anonymous volumes is destructive. Use it only when data deletion is the explicit, authorized objective and the exact project/volume has been verified. Never use broad prune as a troubleshooting shortcut.

8. Failure mode: the Compose source changed but the service is still using an older image

Compose source, build context, built image, and running container are separate states. Compare them rather than assuming up rebuilt anything you changed.

docker compose config --hash web
docker compose images web
docker inspect "$(docker compose ps -q web)" --format 'container={{.Id}} image={{.Image}}'
docker image inspect da-compose15-web:lab --format 'image={{.Id}} created={{.Created}}' 

If the intended change is in the build context, rebuild the specific service and reconcile only that service. Preserve the before/after image IDs and build trace.

9. Performance diagnosis: locate the slow layer before changing concurrency or cache

Slow compose up --build can be a BuildKit/cache issue, image pull issue, health/readiness delay, application startup issue, or an overloaded Engine. Use timestamps and component-specific evidence. Do not delete the builder cache merely because a build is slow; Chapter 11 established that cache state itself is evidence and can be shared.

10. Shortcuts this course does not recommend

  • Do not commit real credentials into .env or print them in logs.
  • Do not use privileged containers, Docker socket mounts, broad capabilities, or host networking merely to “make Compose work.”
  • Do not disable TLS, firewall controls, seccomp, AppArmor, or SELinux as a generic diagnosis.
  • Do not blindly restart the daemon or all services before preserving evidence.
  • Do not use broad image/container/volume prune as routine repair.
  • Do not remove persistent volumes to solve a network, image, or project-name problem.

11. Smallest-safe-scope examples

Observed cause Smallest correction Evidence after correction
Required interpolation variable missing Restore the non-secret configuration source; rerun compose config. Normalized model plus interpolation environment.
Wrong project name Invoke the intended project name; do not recreate unrelated resources. Project labels and compose ps.
Only web image stale Build/reconcile web only. Before/after image ID and web container ID.
Internal DNS works, host port fails Inspect port binding/host path. Container port mapping, host listener/request result.
Application not ready Fix/readiness logic or application startup cause. Health/application probe and logs.
Next lesson

Next: Checkpoint Lab

The checkpoint combines the chapter into one evidence packet: normalized configuration, project identity, image/build state, network/service discovery, named-volume persistence, lifecycle transitions, and explicit data-preservation teardown.

Knowledge check

A compose config required-variable error appears before any containers exist. Which layer failed?

Why can an empty docker compose ps be a project-name problem rather than lost containers?

A service shows Running but its health probe fails. What does Running prove?

Why is deleting a named volume a poor generic fix?

A rebuilt image exists but the running container reports an older image ID. Which state must be reconciled?

Official references and version notes

Version baseline checked 2026-09-21.

Upstream Compose is v5.5.1. The course still treats docker compose version, docker version, docker info, and the active context as execution evidence. The top-level Compose version: field is obsolete/informative; the current Compose implementation validates against the current schema.

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.