Chapter 16 • Production, Optimization & Security

Environment Variables & Secrets Management

Following The Twelve-Factor App methodology, an application's codebase should be strictly decoupled from its runtime configuration. In this chapter, explore the mechanisms for injecting environment variables, understand the critical distinction between build-time and runtime parameters, and safeguard sensitive credentials using BuildKit Secret Mounts and Docker Secrets.

1. Runtime Environment Injection via CLI

The Docker Engine allows you to pass environment variables into the container's PID 1 process environment at boot:

# 1. Inline Key-Value Pair (-e)
docker run -d -e PORT=8080 -e NODE_ENV=production my-api:latest

# 2. Inheriting an existing host variable without exposing the value in terminal history:
export STRIPE_API_KEY="sk_live_12345"
docker run -d -e STRIPE_API_KEY my-api:latest

# 3. Loading variables from a centralized .env file (--env-file):
docker run -d --env-file .env.production -p 8080:8080 my-api:latest

2. The Fatal Security Trap: ARG vs ENV

Never Pass Secrets via ARG or ENV!

Developers often write ARG GITHUB_TOKEN or ENV DB_PASS=secret inside a Dockerfile.Any variable declared in ARG or ENV is permanently recorded in plain text in the image metadata!

# An attacker or automated scanner can easily extract all build-time ARGs and ENVs:
docker history --no-trunc my-image:latest
docker inspect my-image:latest

The Solution: BuildKit Secret Mounts

BuildKit allows you to mount secrets temporarily during a specific RUN instruction without committing those bytes to the resulting image layers:

# Dockerfile: Mount secret as an ephemeral in-memory file during npm install
# syntax=docker/dockerfile:1.4
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./

# The secret file /run/secrets/npm_token is only visible to this specific RUN layer!
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci --omit=dev

COPY . .
CMD ["node", "server.js"]

Pass the secret file securely during the build command:

docker build --secret id=npm_token,src=./.npmrc.secret -t my-secure-app .

3. Compose Variable Interpolation & Validation

Docker Compose parses .env files automatically and supports Bash-style parameter expansions insidecompose.yaml:

SyntaxMeaning & BehaviorExample
${VARIABLE}Direct variable substitution. Empty string if unset.image: "node:${NODE_VERSION}"
${VARIABLE:-default}Provides a default fallback value if the variable is unset.PORT: "${APP_PORT:-3000}"
${VARIABLE:?error_msg}Fails build/boot with fatal error if variable is missing.PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required!}"

4. Enterprise Docker Secrets (File-Based Convention)

Environment variables can leak through error trace dumps, APM monitoring agents, and docker inspect. The enterprise standard is file-based secret mounting:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app_admin
      # Notice the _FILE suffix convention used by official database images:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
Security Benefit: The secret is mounted into the container as an in-memory file at/run/secrets/db_password. It never appears in the output of docker inspect or docker stats!

Next Up: Docker in CI/CD Pipelines

In Chapter 17, integrate Docker into automated GitHub Actions and GitLab CI workflows with distributed layer caching.

Chapter 17: Docker in CI/CD Pipelines →

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools