Chapter 15Lesson 01~100 minutes

Docker Compose Foundations: Services, Images, Builds, Networks, Volumes, Environment, and Project Lifecycle: Concepts, Architecture, and Mental Model

Compose is a declarative application model that resolves configuration into a project-scoped set of containers, networks, volumes, labels, and lifecycle operations. This lesson builds that model before changing runtime state.

Compose modelProjectsServicesNetworksVolumes

Learning objectives

  • Explain how compose.yaml, interpolation inputs, and the active Compose CLI become one normalized application model.
  • Distinguish a Compose project, service definition, service container, network, named volume, build/image input, and host-published port.
  • Identify project-scoped resource names and canonical com.docker.compose.* labels before changing state.
  • Separate Compose model resolution from Engine runtime state and separate “started” from “ready”.
  • Use read-only commands to establish version, context, project, configuration, image, network, volume, and lifecycle evidence.
Chapter 15 principle. Compose is a model-and-lifecycle client for Docker resources. It does not make mutable tags immutable, make data durable automatically, prove an application is ready, or turn a single Engine into a production orchestrator. The useful unit of reasoning is: resolved model → project identity → concrete Engine resources → observed application state.

1. The problem: a multi-container application has more state than a list of docker run commands

By Chapter 14 you can build, identify, distribute, and retrieve images. Real applications often need several related containers plus networks, persistent storage, environment-specific configuration, health observations, and repeatable lifecycle commands. Hand-writing independent docker run commands makes those relationships easy to drift.

Compose solves the declaration problem. A Compose file describes desired application components; the Compose CLI resolves interpolation and file rules into a normalized model, chooses a project identity, and asks the active Docker Engine to create or reconcile concrete resources. The project name is not cosmetic: it groups and isolates resources and appears in canonical Compose labels.

2. Mental model: file + interpolation → normalized model → project → Engine resources → observed state

Compose request and evidence chain
flowchart TD
  A[compose.yaml + optional override files] --> B[Interpolation inputs shell / --env-file / .env]
  B --> C[docker compose config normalized model]
  C --> D[Project identity -p / COMPOSE_PROJECT_NAME / name]
  D --> E[Services]
  D --> F[Project networks]
  D --> G[Project volumes]
  E --> H[Image pull or build]
  H --> I[Service containers]
  F --> I
  G --> I
  I --> J[Runtime evidence ps logs inspect health ports]
            

The Compose file is declarative input, not runtime evidence. docker compose config proves what Compose resolved. Engine inspection proves what exists. Application probes and health evidence prove what the workload can actually do.

3. Define the objects before operating them

Concept Meaning Best evidence Do not confuse with
Compose file Source declaration, normally compose.yaml. File path/revision plus docker compose config. A guarantee that resources already exist.
Project One deployment instance of the Compose model. Project name plus canonical project labels. A service or container name.
Service Reusable container configuration in the model. Normalized service config. One specific container instance.
Image/build Input artifact or build recipe used for a service. Image digest/ID and build trace. The running service process.
Network Connectivity boundary. Compose creates a default project network unless configured otherwise. Network ID/name, labels, attached endpoints. Host port publication.
Named volume Engine-managed persistent storage object. Volume name/ID, labels, mount target. Container writable layer.
Environment Two separate concerns: values used to interpolate the Compose model and values injected into containers. config --environment plus resolved service environment. Secret storage.
Lifecycle state Created/running/exited/health state of concrete containers. compose ps, logs, inspect, health. Readiness merely because a container started.

4. Project naming is an isolation control

Current Compose project-name precedence is: -p on the CLI, then COMPOSE_PROJECT_NAME, then top-level name:, then the project-directory basename, then the current-directory basename when no file is specified. The same Compose model can therefore create two isolated application instances by using two project names.

Compose uses the project identity to scope default resource names and sets canonical labels such as com.docker.compose.project. Network and volume resources also receive Compose resource labels. These labels are often safer diagnostic selectors than guessing generated names.

docker compose version
docker compose config --services
docker compose config --networks
docker compose config --volumes

# After a project exists, inspect by canonical label rather than by name guessing:
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

5. Interpolation environment is not the same as the container environment

Compose can substitute variables into YAML values before creating resources. Current Docker Compose gives shell variables higher interpolation precedence than values from --env-file, which in turn override the default project .env path. You can inspect interpolation inputs with docker compose config --environment.

Container environment precedence is a separate problem. CLI run -e overrides relevant Compose-file values; Compose environment generally outranks env_file; image ENV is lower when Compose provides a value. A file named .env is therefore configuration input, not a secret manager. Chapter 15 deliberately uses only synthetic non-secret values.

6. Default networking: service-name discovery, not fixed container IPs

For ordinary local development, Compose creates one project default bridge network. Containers attached to that network are discoverable by service name through Docker's internal DNS. Recreated containers can receive new IP addresses, so application configuration should prefer stable service names rather than persisted IPs.

A host-published port is a separate path. Service-to-service traffic normally targets the container port through the Compose network; host users reach the published host address/port. Publishing 127.0.0.1:8080:80 intentionally bounds the lab listener to loopback.

7. Named volumes outlive ordinary container replacement

A named volume is an Engine storage object attached to a service container. docker compose down removes project containers and ordinary project networks by default, but it does not remove named volumes unless volume deletion is explicitly requested. This is a crucial lifecycle boundary: replacing a container is not the same as deleting its persistent data.

Bind mounts instead couple the service to a host path. They can be appropriate for editable source code or explicit host integration, but they reduce portability because the host path and permissions become part of runtime correctness.

8. Read-only preflight: prove client, context, Compose, and model state first

docker version
docker info
docker context show
docker compose version

# In a Compose project directory, before creating anything:
docker compose config --quiet
docker compose config --environment
docker compose config --services
docker compose config --networks
docker compose config --volumes

# If a project already exists:
docker compose ps -a
docker compose images

docker compose config is particularly valuable because it merges files, interpolates variables, expands short syntax, and renders the model that Compose intends to apply. It does not prove the Engine accepted or is currently running that model; follow it with runtime evidence.

9. Compose is not a magical production orchestrator

Compose gives a portable application model and excellent local/single-Engine lifecycle ergonomics. It does not, by itself, add a scheduler across hosts, distributed consensus, automatic multi-node rescheduling, or a complete secrets/identity/observability platform. Some Compose specification attributes can map to richer platforms, but the local Docker Engine execution path remains a concrete single-platform implementation.

This course therefore treats Compose as a declarative application model whose outputs must still be verified at the image, container, network, storage, security, and application layers.

10. Common misconceptions to reject early

  • “Service name equals container hostname everywhere.” Service-name discovery is network-scoped; custom network modes, aliases, multiple replicas, and hostname settings change details.
  • “.env is a secret vault.” It is configuration/interpolation input and can be accidentally committed or exposed.
  • “up means ready.” It proves Compose created/started resources, not that an application dependency is healthy.
  • “down deletes all data.” Named volumes are preserved by default; explicit volume deletion is a different action.
  • “Directory name is harmless.” It can become the project name and therefore affect which resources a command targets.
Next lesson

Next: Guided Hands-On Workflow and Core Operations

Apply the model to a disposable three-service application. You will normalize the config first, then create project resources, verify service DNS and persistent data, rebuild one service, and tear down without deleting the named volume.

Knowledge check

What does docker compose config prove?

Why is the project name operationally significant?

Does a running service container prove the application is ready?

What normally happens to a named volume on docker compose down?

Why should service-to-service connections normally use the service name rather than a recorded IP?

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.