Course outline

Docker / Compose and reliable runtime contracts / Lesson 8

Configuration, interpolation, and secret boundaries

Separate values used to render a model, runtime configuration, and build-only credentials.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain .env, environment, env_file, runtime secrets, and build secrets.
  • Prevent credentials from entering layers, logs, or public configuration output.

A value belongs to a particular stage

Compose interpolation substitutes variables while constructing the model. A .env file can supply interpolation values, but its presence does not automatically inject every variable into a container. environment and env_file define runtime environment variables through their documented precedence rules. Build arguments are a separate build-time channel. Name each value's consumer and lifetime before choosing a mechanism; otherwise a missing runtime value can be misdiagnosed as a networking or image problem.

Keep ordinary configuration explicit

The following model places a nonsecret mode value into the runtime environment and supplies a loopback host-port default. The current fixture does not read PARCEL_MODE; the example teaches value delivery rather than inventing a feature. Quote mappings that contain colons and distinguish a default substitution from a required variable. Review the resolved result with a synthetic environment before relying on an operator's shell history.

yaml Nonsecret model interpolation and runtime delivery
services:
  api:
    image: parcel-api:lab
    environment:
      PARCEL_MODE: "${PARCEL_MODE:-demo}"
    ports:
      - "127.0.0.1:${HOST_PORT:-18080}:8080"
Expected output
PARCEL_MODE=demo
Host publication: 127.0.0.1:18080 -> container 8080

When both host variables are absent, the model supplies mode demo and host port 18080. This is a logical substitution result, not a claimed Compose run. A variable present in the operator environment may change the outcome, so record inputs or require explicit values for important deployment choices.

Do not bake credentials into images

ARG and ENV are unsuitable for secrets because values can persist in image metadata, history, provenance, or resulting files. BuildKit secret mounts expose a supplied credential only to a selected RUN instruction. The process using it must still avoid copying it into output, logs, or artifacts. Secret mount availability is not permission to transmit credentials to arbitrary endpoints; credential scope and destination trust remain part of the build policy.

dockerfile A build-only secret presence check
# syntax=docker/dockerfile:1
FROM python:3.13-slim
RUN --mount=type=secret,id=repo_token,required=true \
    test -s /run/secrets/repo_token

This standalone instructional Dockerfile requires a nonempty secret named repo_token supplied through BuildKit. The test produces no normal output and does not copy the file into an image layer. It does not fetch dependencies or reveal a token. Missing input should fail rather than fall back to an embedded credential.

Runtime secrets need a delivery contract

Compose secrets can grant selected services access to file-backed material, commonly under /run/secrets. Ordinary local Compose secret files remain host-managed files; this does not imply an encrypted external secret manager or identical Swarm behavior. The application must read the supplied path, permissions must allow its runtime identity, and rotation needs a documented reload or replacement behavior. Do not assume changing a host file causes every application to reload credentials safely.

Review observability and caches for leaks

Rendered configuration, shell tracing, exception messages, image history, and build logs can all expose secrets. A successful secret mount prevents one persistence path, not every leak. Cache keys do not automatically track secret-value changes, so a credential rotation may require a separate nonsecret cache invalidation input when the build result should change. Redact diagnostic evidence and rotate any credential whose exposure cannot be ruled out through the established incident process.

Common errors and fixes

A variable exists in .env but is absent from the container.

Why it happens
Interpolation input was mistaken for automatic runtime environment injection.
How to fix it
Declare the intended environment or env_file mapping explicitly.
Prevent it next time
Document each variable's consumer, stage, and precedence.

A credential appears in image history.

Why it happens
It was passed through a build argument, environment field, or copied file.
How to fix it
Rotate the exposed credential and rebuild through a nonpersisting secret channel.
Prevent it next time
Review history, logs, and artifact contents using synthetic secret canaries.

A rotated secret does not change the cached build output.

Why it happens
Secret contents are not automatically a cache-invalidation signal.
How to fix it
Use a separate appropriate nonsecret revision input when output must change.
Prevent it next time
Separate credential identity from dependency versioning and cache policy.

Check your understanding

1. Where should a private package token go during image construction?

Show worked solution

Use an authorized BuildKit secret mount only for the instruction that needs it. Configure the package tool to read that file without echoing the value or leaving credential files in copied artifacts. ARG or ENV may expose the token beyond that one build step and should not be used as a substitute.

2. What does the first model resolve to when neither variable exists?

Show worked solution

The mode becomes demo and host port becomes 18080, still bound to 127.0.0.1. That follows the explicit default expressions. It does not prove parcel-api understands the mode or that the host port is available; configuration delivery and application behavior require separate checks.

Key takeaways

  • Interpolation, runtime environment, and build secrets are different channels.
  • Prevent secret persistence and review secondary leak paths.

Official references