Do not pass the token as a build argument or an environment variable, because Docker's docs say both persist in the final image. Use a build secret mount so the token exists only during one build step, or skip downloading at build time and fetch the model at run time with a scoped token.
Why the obvious way leaks
Gated or private models need a token to download. The quick fix in a Dockerfile is an ARG or ENV line holding the token, then a download command. It works, and it leaves the token behind.
Docker's documentation on build secrets says plainly that build arguments and environment variables are inappropriate for secrets, because they persist in the final image. Anyone who can pull the image can read layer metadata and recover the value. Images get pushed to registries, copied to laptops and attached to bug reports, so the secret spreads with them.
Option 1: a build secret mount
Docker describes secret mounts as the way to expose sensitive data to a build step safely. The secret is made available temporarily to a single instruction and is not written into the image layers.
# syntax=docker/dockerfile:1
FROM python:3.12-slim
RUN pip install huggingface_hub
RUN --mount=type=secret,id=hf_token \
HF_TOKEN="$(cat /run/secrets/hf_token)" \
python -c "from huggingface_hub import snapshot_download; snapshot_download('ORG/MODEL', revision='COMMIT_HASH')"
Build with the secret from a file or environment variable on the build client:
docker build --secret id=hf_token,src=./hf_token.txt -t my-model-image .
Check the Docker docs for the exact syntax for your version, and confirm with a history inspection that the token does not appear.
Option 2: do not bake the model in at all
Baking weights into an image makes images huge and ties the licence and token handling to your build. Often a better pattern is a small image that downloads or mounts the model when it starts.
- Store the model in an internal registry or bucket, pinned to a known revision.
- Give the running service read access to that location through its workload identity.
- If it must call the Hub directly, supply a fine-grained read token at run time from a secret store, not from the image.
Token hygiene either way
| Practice | Why |
|---|---|
| Read-only scope, limited to the needed repos | A leak cannot publish or delete |
| Separate tokens per environment | Easy to rotate without breaking others |
| Short lifetimes where supported | A stolen token expires |
| Never commit it, never echo it in CI logs | Logs are widely readable |
If a token has already been baked into an image, treat it as leaked. Revoke it, rebuild without it, and delete the old tags. Separate posts on this blog cover a leaked token response checklist and fine-grained tokens.
Verify before you push
Run a history check on the built image and search the layers for the token string. Add secret scanning to CI so a token committed in a Dockerfile or script is caught on the pull request.
docker history --no-trunc my-model-image | grep -i token
No output is the goal, but a clean history does not prove the file system is clean, so also scan the final image.
Try it yourself
Click through it at your own pace, or open it full screen. Look for how embedded secrets and package issues surface in an image scan.
Quick check
Is deleting the token in a later layer enough?
No. Earlier layers still contain it. Use a secret mount from the start, or rebuild the image.
Is a private registry safe enough for an image with a token inside?
It narrows exposure but does not remove it. Treat any token that was baked in as compromised and rotate it.
Where Safeguard fits
Safeguard scans container images and dependencies and applies policies and gates in CI, and its checks can catch problems before an image is pushed. A free scan is available for repositories.
Sources
- Docker build secrets, read 19 October 2026.
Last reviewed: 19 October 2026.