Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevOpsDevelopmentSaaS and CloudTutorial

Docker Compose environment variables and secrets: interpolation, file delivery and rotation

Trace Docker Compose environment variables from interpolation to the running application. Use explicit secret-file consumption, protected build credentials and reviewed rotation.

Nilesh Kushvaha

6 min read

Docker artwork with a configuration workflow separating interpolation, runtime environment, secret files and credential rotation.
Image: Cloudsoft Zone layout; dotCloud, Inc. / Apache License 2.0
In this article

A secret is added to a Compose .env file, but the application still reports a missing setting. Another team mounts a password file and assumes Laravel understands the _FILE suffix. A third changes the file, yet the existing database rejects the new password.

Docker Compose environment variables do two different jobs: substitution in the file and delivery to containers. The guide compares a Docker Compose env file with a Docker Compose secrets file granted to one service, then covers rotation.

Docker Compose environment variables operate across separate stages: model interpolation, runtime delivery and application consumption. Secrets add a file-delivery and lifecycle contract; they do not erase those distinctions. This guide follows a value through the actual consumer without printing it.

Our NestJS production API guide provides related application validation. Fail startup clearly when required configuration is missing rather than silently inventing a production credential.

Compose interpolation, container environment delivery and application consumption are distinct configuration stages.
A project .env file does not automatically populate every container environment variable.

Step 1: Separate substitution from delivery

Compose can use host and selected environment-file inputs to resolve placeholders in its model. That does not mean every input becomes an environment variable in every service.

Use environment or env_file where the running container needs a value. Review documented precedence, including command-line overrides, rather than assuming the nearest .env wins.

Stage Purpose Typical mistake
Model interpolation Resolves selected Compose fields Assuming all inputs enter the container
Container environment Supplies process configuration An override silently wins over the intended value
Secret mount Delivers an explicit file to a service The application never reads the file
Build secret Temporarily supplies a build credential The build command copies it into output
Application cache Stores effective framework configuration An old cached value survives a new environment

Step 2: Validate required non-secret selectors

Require the intended image and environment selectors rather than falling back to an arbitrary latest artifact. This example illustrates Compose substitution without embedding credentials:

YAML
services:
  api:
    image: "registry.example.com/acme/api:${APP_RELEASE:?APP_RELEASE is required}"
    environment:
      NODE_ENV: production
      PORT: "3000"

The release selector identifies a real reviewed artifact; it does not prove its integrity by itself. Record the approved digest in the release process.

Validate the selected file set without printing all resolved values:

Shell
docker compose --project-name acme-prod --file compose.yaml config --quiet

Do not paste a full resolved configuration into an issue or shared build log. Interpolation can expose credentials even when the original YAML contains only placeholders.

Step 3: Grant a secret to the intended service

This fragment mounts one protected file only into api:

YAML
services:
  api:
    image: registry.example.com/acme/api:reviewed-release
    environment:
      API_TOKEN_FILE: /run/secrets/api_token
    secrets: [api_token]
secrets:
  api_token:
    file: /etc/acme/secrets/api-token

API_TOKEN_FILE is an application-specific contract in this example. It will do nothing unless the image or application implements a reader for that path. Do not rename a Laravel variable with _FILE and assume it has gained that behavior.

Protect the source file on the host and review the effective mounted permissions. Local file-backed Compose secrets are not automatically an encrypted centralized secret store. The Docker host and authorized runtime consumers remain part of the trust boundary.

A Compose secret moves from a protected source to selected services and requires an explicit image/application reader.
The _FILE convention exists only where the selected image or application implements it.

Step 4: Implement an explicit reader safely

For a Node.js service whose configuration module supports the example contract, a small server-side reader can load the selected file. Keep this in the application's existing configuration owner, not a parallel secret subsystem.

JavaScript
import { readFileSync } from 'node:fs';

export function readApiToken() {
  const path = process.env.API_TOKEN_FILE;
  if (!path) throw new Error('API token file is required');
  const raw = readFileSync(path, 'utf8');
  const value = raw.replace(/\r?\n$/, '');
  if (!value) throw new Error('API token must not be empty');
  return value;
}

This example assumes a text token whose file writer appends at most one line ending. Do not use indiscriminate trimming for secrets whose whitespace is significant. Validate the actual credential format and define maximum file size in the production configuration boundary.

Do not log the returned token. Map file access or validation failures into a clear non-secret startup error under the existing logging policy. A cached token also needs an explicit refresh or process-replacement contract during rotation.

Step 5: Keep build credentials out of layers

Use supported build-secret mounts for private registries and similar build-only authentication. ARG and ENV are not safe substitutes for secret delivery, and deleting a copied credential in a later layer does not erase the earlier layer.

The invoked build command must avoid writing the secret into a dependency cache, generated file or log. A temporary secret mount controls availability; it cannot make an arbitrary script trustworthy.

Keep runtime credentials out of browser-visible Next.js variables and public assets. A browser bundle cannot protect a server credential regardless of how carefully Compose delivered it to the build.

Step 6: Rotate through the authoritative service

Create or activate the new credential at the database or provider first using its supported process. Transition web processes, workers, scheduled tasks and operating tools according to a controlled overlap policy.

MySQL image initialization values apply when initializing an empty data directory. Replacing MYSQL_PASSWORD_FILE is not a database-account rotation procedure after that directory already contains retained data.

Verify a harmless authenticated operation with every intended consumer. Retire the old credential only after the transition and recovery decision are complete. Record identifiers and versions rather than secret values in the audit trail.

Credential rotation updates the authoritative provider, transitions all consumers and then retires the old credential.
Changing a MySQL initialization variable does not rotate an already initialized database account.

Step 7: Rehearse precedence and failure cases

Use isolated credentials and the exact production file set. Check presence and behavior without printing values.

Acceptance case Expected outcome
Required selector absent Clear validation failure before unintended artifact selection
Secret mount absent or unreadable Bounded startup failure without value disclosure
Runtime override supplied Documented precedence matches the deployment intent
Credential rotated Every active consumer adopts the accepted version
Old credential revoked No hidden worker or scheduler still relies on it
Logs and artifacts inspected No credential copied into tracked files or public output

For Laravel, include configuration-cache behavior in that review. A newly delivered environment does not repair an incorrectly built or stale config cache by itself.

Step 8: Keep recovery and access ownership explicit

Document who can supply, rotate and revoke each credential. A rollback image can still require the current credential version; restoring a revoked old token is not always a valid recovery path.

Our Laravel webhook idempotency guide covers an application boundary where provider credentials and durable effects meet. For deployment configuration and secret lifecycle review, contact Cloudsoft Zone.

Featured image: Cloudsoft Zone technical layout with docker artwork by dotCloud, Inc. (Apache License 2.0). Product mark resized proportionally for identification; no endorsement implied. Layout: CC BY-SA 4.0.

Frequently asked questions

  • Does the Compose .env file automatically become the container environment?

    No. It is commonly an interpolation input. Runtime values need explicit environment or env_file configuration, subject to the documented precedence rules.

  • Are local Compose secret files encrypted just because they are called secrets?

    Do not assume that. Local file-backed secrets depend on the protection of the host source and runtime delivery. They are not automatically equivalent to an encrypted centralized secret manager.

  • Can I rotate a MySQL password by replacing MYSQL_PASSWORD_FILE alone?

    Not for an already initialized database account. Update the authoritative database credential through its supported rotation procedure and transition application consumers consistently.

Sources

  1. Compose variable interpolation — Docker
  2. Container environment precedence — Docker
  3. Compose secret grants and files — Docker
  4. Build-secret mounts — Docker
  5. Secret lifecycle and access controls — OWASP
  6. MySQL official image initialization — Docker Official Images

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.