Chapter 15Lesson 03~105 minutes

Docker Compose Foundations: Services, Images, Builds, Networks, Volumes, Environment, and Project Lifecycle: Configuration, Design Choices, and Tradeoffs

Choose intentionally between image and build, default and explicit networks, named volumes and bind mounts, environment sources, project naming, and foreground/detached workflows while tying each choice to observable state.

Design choicesEnvironmentProject namingBuild vs imageTradeoffs

Learning objectives

  • Choose between an immutable image: reference and a local build: definition based on release and developer workflow requirements.
  • Choose default versus explicit networks and named volumes versus bind mounts using portability, isolation, and data-ownership evidence.
  • Distinguish Compose interpolation sources from container environment sources and secrets.
  • Apply project-name precedence intentionally to avoid cross-project collisions.
  • Choose foreground/detached operation based on observability and operator workflow rather than habit.
Design rule. Compose convenience is useful only when ownership remains explicit: who supplies the image, who builds it, who owns the network, who owns the data, which source sets each environment value, and which project a lifecycle command targets.

1. image: versus build:: artifact consumption versus artifact production

image: identifies a runnable artifact. It can be a tag or digest reference. build: describes how Compose should build an image from source. A service may contain both; current Compose then follows pull/build policy rules. If no pull_policy is provided, Compose can attempt to pull the image before building when both are present.

Choice Good fit Evidence to retain Tradeoff
Digest-pinned image Controlled release/deployment input. Registry digest, platform, pull evidence. Requires an external build/promotion workflow.
Versioned image tag + digest record Readable release alias with immutable audit evidence. Tag resolution + digest at release time. Tag can move unless policy prevents it.
Local build: Developer loop and source-controlled reproducible build recipe. Source revision, normalized build config, BuildKit trace, output image ID/digest. Build environment/cache become part of operational evidence.
Both image + build Workflow that names built output and may pull/build under policy. Resolved pull_policy, build/pull trace. Ambiguous intent if policy is not understood.

2. Default network versus explicit networks

The default project network is often ideal for a small local application: every service joins one bridge and is discoverable by service name. Explicit networks become valuable when you need segmentation, different connectivity domains, or a deliberate external network boundary.

Do not confuse network declaration with application reachability. A container can be attached to the expected network while the application listens on the wrong interface/port. Conversely, a published host port is not required for service-to-service communication on the same project network.

3. Named volume versus bind mount

Choice Strength Cost / coupling Evidence
Named volume Engine-managed lifecycle and portable service declaration. Host storage location is abstracted; backup/ownership still must be planned. Volume ID/name, Compose labels, mount destination, backup/restore proof.
Bind mount Direct host-file visibility; useful for editable source/config in development. Host paths, permissions, SELinux/AppArmor/platform semantics can couple the workload to one machine. Exact host path, read/write mode, ownership/label assumptions.
Container writable layer Automatic ephemeral runtime state. Not a durable data strategy; disappears with container removal. Container ID, docker diff, storage driver context.

A good rule: application data that must survive replacement needs an explicit durability design. Named volumes are convenient locally, but “named volume” is not synonymous with “backed up.”

4. env_file, environment, and interpolation solve different problems

Interpolation parameterizes the Compose model before it is sent to the Engine. environment and env_file populate the container environment. The shell, --env-file, default .env, Compose attributes, CLI runtime overrides, and image ENV participate in different precedence chains.

For non-secret configuration, keep source ownership obvious: use required-variable syntax for values that must be supplied and docker compose config --environment to verify interpolation. For secrets, use a secrets mechanism appropriate to the runtime; do not commit secrets into .env simply because Compose can read it.

5. Project naming: deterministic isolation versus accidental collisions

Explicit project naming is useful in CI, parallel feature branches, classroom labs, and shared developer hosts. If two invocations unintentionally resolve to the same project name, one can reconcile or tear down resources the other operator thought were separate.

# Highest-precedence explicit choice for one invocation:
docker compose -p da-compose15-a config --quiet

# Another isolated instance from the same Compose source:
docker compose -p da-compose15-b config --quiet

Record the project name in evidence. Do not diagnose a “missing service” until you have proved you are targeting the expected project and Docker context.

6. Foreground versus detached workflow

Foreground docker compose up streams attached logs and is excellent for small development/test runs where terminal lifetime is intentional. Detached up -d returns control to the operator and requires explicit ps, logs, health, and application probes. Neither mode makes the application more production-ready by itself.

7. Decision table: choose based on state ownership

Scenario Recommended starting choice Prerequisites Observable justification
Developer editing source rapidly Local build:, default network, source bind mount only if needed, named data volume. Trusted local source; documented host-path semantics. Build trace, normalized model, mount inspection, project labels.
Release candidate validation Digest-pinned image:, named volume or disposable data, explicit project name. Published verified image digest. Image digest, project identity, health/application evidence.
Two parallel CI jobs on one Engine Unique -p value per job; avoid shared writable volumes. Authorized shared runner with resource limits. Project labels prove isolation; no cross-project resource names.
Service must not be host-accessible No ports:; use project/internal network only. Consumer runs on an attached network. No host port binding; successful service-name request.
Data must be edited directly by host tools Bind mount only when host coupling is accepted. Stable host path/permissions/security labeling. Exact source path and mount mode in inspect evidence.

8. Worked scenario: a test environment must be repeatable and auditable

Assume CI receives a release image digest and must execute integration tests. Prefer an explicit project name derived from a non-secret job ID, the release image by digest, a project-local default network, and a disposable named volume if persistence is needed only within the job. Save docker compose config and compose ps --format json. Tear down the project after evidence capture; delete the exact test volume only if the job's data-retention policy says it is disposable.

This design avoids rebuilding a release during promotion, avoids mutable-tag ambiguity, and makes project ownership visible through labels.

9. Keep state layers separate while evaluating a Compose choice

  • Host/client/context: Docker context, filesystem paths, CLI and Compose versions.
  • Build/image: source revision, builder, image ID/digest, cache.
  • Container/process: command, PID, exit state, health.
  • Network: network attachment, DNS, published ports, application listener.
  • Storage: writable layer, named volume, bind source, backup.
  • Identity/trust: registry authentication/authorization and artifact verification.

A single Compose file references all of these layers, but it does not collapse them into one state.

Next lesson

Next: Diagnostics, Failure Modes, Security, and Performance

Use the design framework to diagnose failures without deleting evidence or broad resources. The next lesson engineers safe failures around project identity, interpolation, readiness, service discovery, volumes, and image/build state.

Knowledge check

When is build: preferable to a digest-pinned image:?

Why can an explicit project name improve CI reliability?

Does using a named volume automatically solve backup/recovery?

What is the difference between interpolation and environment?

Does omitting ports: prevent services on the same Compose network from communicating?

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.