Safeguard
Developer Tools•

How to pass secrets to a Docker build without leaking them

ARG and ENV values persist in the final image. Use build secret mounts instead: docker build --secret and RUN --mount=type=secret. Commands and pitfalls.

Safeguard Security Team
3 min read

Do not pass secrets to a Docker build with ARG or ENV, because their values persist in the final image. Use a secret mount instead: pass the secret with the build command, read it in a single RUN step through a mount, and it never lands in an image layer.

Why ARG and ENV leak

Docker's documentation says ARG and ENV values persist in the final image, so anything you pass that way, such as a package registry token, can be recovered by someone who pulls the image. Deleting the file in a later step does not help, because earlier layers still contain it.

If an image with a token has already been pushed, treat the token as exposed and rotate it.

The two steps for a secret mount

Step one: give the build the secret.

docker build --secret id=npmtoken,src=./npmtoken.txt .

You can use src for a file or env for an environment variable. The type is detected automatically, or you can set it.

Step two: consume it in the Dockerfile.

RUN --mount=type=secret,id=npmtoken \
    NPM_TOKEN=$(cat /run/secrets/npmtoken) npm ci

By default the secret appears as a file at /run/secrets followed by the id, inside that build step only. You can change the path with target, or expose it as an environment variable with env. That variable exists only for the step.

Be careful not to write the secret into a file that stays in the image, such as a config file in the project directory. Use it in the command and let it vanish.

SSH mounts for private repositories

For cloning private Git repositories, use an SSH mount instead of copying a key into the image.

RUN --mount=type=ssh git clone git@example.com:org/repo.git

Pass the agent socket at build time with the buildx ssh option. The key stays on your machine.

Check what you shipped

  1. Search Dockerfiles for ARG or ENV names that contain token, password, key or secret.
  2. Inspect image history for build arguments.
  3. Scan images for embedded credentials before release.
  4. Use short-lived tokens for builds so a leak has a short life.

CI platforms have their own secret stores that feed the build command, so the secret never sits in the repository.

The free scan at gold.safeguard.sh/scan covers dependencies, and Safeguard's SBOM and dependency scanning can run in the pipeline. Secret detection in images is a separate check, so confirm your tooling covers it.

Handling a leak you already shipped

If an image containing a credential was pushed to a registry, assume anyone with pull access has seen it. Revoke the credential first. Then rebuild without it, publish a new tag, and remove the old tag where your registry allows. Deleting a tag does not prove nobody copied the image, which is why rotation comes first.

After the incident, add a build check that fails on ARG or ENV names that look like credentials, so the same mistake is caught in review next time.

Quick check

Is it safe if I delete the file in the next RUN step?

No. The earlier layer still contains it. Use a secret mount so it never enters a layer.

Where does the secret live during the build?

As a file under /run/secrets by default, available only to the RUN step that mounts it.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.