WSS
On this page
Deployment & DeliveryRequiredUpdated

Docker Compose specification

The Compose specification defines a developer-centric model for multi-container applications. It standardizes how services, networks, and volumes are declared in YAML.

Introduction

The Compose specification defines a declarative format for defining multi-container applications. A Compose file (typically compose.yaml) describes the services that make up an application, along with the networks they use to communicate and the volumes they use to persist data. This standardizes the definition across different container engines and orchestration platforms.

Mental Model

Think of a Compose file as a blueprint for an office building. The container engine builds the physical rooms (services), runs the cables between them (networks), and provides the filing cabinets (volumes). The specification ensures that handing this blueprint to any compatible builder produces an identical office layout.

Algorithms/Resolution

When evaluating a Compose file, the implementation resolves paths relative to the directory containing the Compose file itself, not the directory from which the user ran the command.

If multiple Compose files are provided (e.g., compose.yaml and compose.override.yaml), the implementation merges them. Lists are appended. Dictionaries are merged. Scalar values in the latter file override the former.

Environment variables declared in the shell or an .env file are interpolated before the document is parsed as YAML. If an environment variable is referenced but not set, it evaluates to an empty string unless a default is explicitly provided (e.g., ${VAR:-default}).

Semantics

A Compose document contains top-level mappings: services, networks, volumes, configs, and secrets.

The services mapping defines the computing resources. Each service specifies either a build context to construct an image from source, or an image reference to pull a pre-built artifact. Services do not guarantee startup order by default; the depends_on mapping enforces a sequence, particularly when combined with condition: service_healthy.

The networks mapping defines the communication topology. Services placed on the same custom network can resolve each other by service name via internal DNS. Services on different networks cannot communicate unless explicitly bridged.

Failure Modes

Relative paths break when invoked from external directories. If a service mounts ./data:/app/data, and the user runs the deployment command from a parent directory without specifying the project directory flag, the engine mounts the wrong host path.

Port collisions prevent startup. If two services map host port 8080 to their respective container ports, the second service fails to start because the engine cannot bind the host port twice.

Orphaned containers persist after configuration changes. If you rename a service in compose.yaml and reapply it, the implementation starts the new service but leaves the old container running unless explicitly instructed to remove orphans.

Security

Running containers with privileged: true disables major security boundaries, giving the container near-root access to the host. The specification supports this, but operational practices prohibit it unless strictly necessary for nested virtualization or kernel module loading.

Mounting the host’s Docker socket (/var/run/docker.sock) into a container grants that container full control over the host engine. If compromised, the container can start privileged containers and mount the host filesystem.

Secrets should not be passed via environment variables, as they leak in process lists and error logs. Use the secrets mapping, which mounts sensitive data into the container’s tmpfs filesystem, preventing it from touching disk.

Operational Guidance

Use explicit image tags. Specifying image: nginx:latest guarantees drift, as latest changes over time. Specify the exact version (e.g., image: nginx:1.25.3) to ensure reproducible deployments.

Define health checks within the Compose file. The engine uses healthcheck to determine if a service is ready to handle requests. This allows dependent services to wait for databases or caches to initialize before starting.

Version the Compose file alongside the application code. This treats the infrastructure definition as an integral part of the application state.

Diagnostics

Validate the interpolated configuration. Implementations provide a command to parse the file, resolve environment variables, apply overrides, and print the final YAML. This isolates syntax and interpolation errors.

Inspect the network topology. Container engines can list the created networks and show which service endpoints are attached to them, verifying that isolation boundaries match the Compose specification.

Check service logs. If a service exits immediately, the container logs reveal whether the failure was an application crash or an engine-level error such as a missing volume mount.

Related topics

Sources & further reading