Do not pass secrets to a Docker build with ARG or ENV. Docker's documentation says those values persist in the final image, so anyone who can pull the image can read them. Use a secret mount instead, which makes the secret available only during the RUN instruction that asks for it.
Why ARG and ENV are the wrong tool
It feels harmless to write an access token as a build argument. The token is only needed to fetch a private package, and the build is private. But the image is the thing that gets shared. Docker's secrets documentation warns that build arguments and environment variables persist in the final image, which means the value can be read by anyone with the image.
You can check this yourself on a throwaway image. Build with a dummy value, then run the image history command and look at the output. The instruction that used the value is recorded in the image metadata.
Once the image is pushed to a registry, treat the token as leaked. Rotating it is the only real fix.
How a secret mount works
A secret mount takes a secret from the build client and exposes it inside the build container for the length of one RUN instruction. The workflow has two steps. Pass the secret to the build command, then consume it in the Dockerfile.
docker build --secret id=npmtoken,src=./npmtoken.txt .
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. The documentation also describes options to change the target path or expose it as an environment variable for that one step.
Note that the documentation says the secret is available temporarily during the instruction, in contrast with ARG and ENV. It does not spell out layer storage in detail, so confirm on your own build tool version by inspecting a test image for the secret string.
Habits that keep it safe
- Read the secret inside the same RUN. Do not copy it to another path in the image.
- Do not echo it. Build logs are often kept and shared.
- Use short-lived credentials. A token that expires in an hour limits the damage of any mistake.
- Scope the token. Read-only access to the one registry or repository the build needs.
- Keep the source file out of the context. Add it to the ignore file. See what leaks through the build context.
What about runtime secrets?
Build secrets are for build time. At runtime, the app should receive credentials from the platform, not from the image. Environment variables are visible to anyone who can inspect the container, so many teams prefer mounted files or a secret manager. The OWASP Docker cheat sheet recommends keeping credentials out of images and runtime commands.
If you already leaked one
- Revoke and rotate the credential first.
- Delete the affected image tags from the registry.
- Search other images built from the same Dockerfile.
- Fix the Dockerfile, then add a scanner step that fails builds when secret-looking strings appear.
Safeguard's SBOM and dependency scanning does not replace secret rotation, but policies and gates can help you stop a pipeline when your rules are not met. A free scan is a way to see what a given image contains.
Quick check
Is a private registry enough to make ARG safe?
No. Everyone with pull access can read the value, including build agents, other teams and any system that mirrors the image.
Does deleting the secret in a later RUN remove it?
No. Earlier layers still contain it. See the layer explainer for why.
Sources
Last reviewed: 19 October 2026.