Secrets: Never Bake Them In

Build-time ARG and ENV values are stored in image layers forever; use BuildKit secret mounts for build secrets and runtime injection for the rest.

The most common Docker security mistake is putting a secret in the Dockerfile. Anything in an ARG, ENV, or a RUN that echoes a token is permanently visible in the image history — anyone with the image can run docker history and read it.

Build-time secrets: use BuildKit mounts

When you genuinely need a secret during the build (a private registry token, an SSH key to pull a private repo), use RUN --mount=type=secret. The secret is mounted into that one command's filesystem and never written to any layer.

Runtime secrets: inject, don't bake

  • Pass them at docker run time via --env-file or your orchestrator's secret store.
  • In Compose and Swarm, use the secrets: block, which mounts them at /run/secrets/.
  • In Kubernetes, use Secrets or an external manager (Vault, cloud secret managers).

Example

Example · dockerfile
# syntax=docker/dockerfile:1
FROM alpine:3.20

# The secret is mounted only for THIS command and never stored in a layer
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) \
    && echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc \
    && npm ci \
    && rm ~/.npmrc

# Build it by passing the secret from a file or env var:
# docker build --secret id=npm_token,src=./npm_token.txt -t myapp .
# docker build --secret id=npm_token,env=NPM_TOKEN -t myapp .

When to use it

  • A developer uses a BuildKit secret mount to pass an npm token to npm install without storing it in any image layer.
  • A CI pipeline injects SSH keys via BuildKit ssh agent forwarding so private Git dependencies can be cloned during build without baking keys.
  • A production team uses Docker Swarm secrets to provide a TLS private key to a container as a file, never as an environment variable.

More examples

BuildKit secret mount for npm token

Mounts the npm token as a BuildKit secret that is available only during this RUN step and never stored in the image.

Example · dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) \
    npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN \
 && npm ci --omit=dev

Pass secret during docker build

Injects an environment variable as a BuildKit build secret, making it available to RUN --mount=type=secret steps.

Example · bash
# Pass the secret from an environment variable
docker buildx build \
  --secret id=npm_token,env=NPM_TOKEN \
  -t myapp:latest .

SSH agent forwarding during build

Forwards the host SSH agent into the build so private Git repos can be cloned without copying any key files.

Example · bash
# Ensure the ssh-agent is running with a key loaded
ssh-add ~/.ssh/id_rsa

docker buildx build \
  --ssh default=$SSH_AUTH_SOCK \
  -t myapp:latest .

Discussion

  • Be the first to comment on this lesson.