Fix Docker BuildKit Secret Mount: Secure Build-Time Credentials

Topic: docker-buildkit-secret-env-mountUpdated 7/27/2026

Quick Answer

  • Conclusion: Use --mount=type=secret with BuildKit to pass sensitive data (API tokens, passwords, SSH keys) during Docker builds without persisting them to image layers or metadata.
  • First checks: Enable BuildKit with DOCKER_BUILDKIT=1 or use docker buildx; verify the id in --secret matches the id in --mount=type=secret exactly; ensure file paths are absolute and readable by the Docker daemon.
  • Minimal command: docker build --secret id=my_token,env=MY_TOKEN -t my-image . with RUN --mount=type=secret,id=my_token,env=MY_TOKEN in Dockerfile.
  • Version boundary: Requires Docker Engine 18.09+ with BuildKit enabled; docker buildx is recommended for multi-platform builds.

What Problem It Solves

Traditional Docker builds using ARG or ENV for secrets (API tokens, passwords, SSH keys) leave those values visible in image history (docker history) and final image layers. Anyone with image access can extract them. BuildKit secret mounts solve this by making secrets available only during RUN instruction execution, then automatically unmounting them—never writing to any layer or metadata.

When This Error or Setup Appears

You need BuildKit secret mounts when:

  • Cloning private Git repositories during build
  • Installing packages from private npm, pip, or Maven registries
  • Authenticating with external APIs during build steps
  • Injecting AWS/GCP credentials for build-time resource access
  • Using SSH agent forwarding for private dependency fetching
  • Running multi-stage builds where intermediate stages need temporary credentials

Common in CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins) and local development with private dependencies.

Minimal Working Configuration

Enable BuildKit (one of these):

BASH
export DOCKER_BUILDKIT=1
# or
docker buildx create --use

Dockerfile (Dockerfile):

DOCKERFILE
FROM alpine:latest

# Mount secret as file (default path: /run/secrets/<id>)
RUN --mount=type=secret,id=my_token \
    cat /run/secrets/my_token

# Mount secret as environment variable
RUN --mount=type=secret,id=db_password,env=DB_PASSWORD \
    echo "Password length: ${#DB_PASSWORD}"

# Mount SSH agent socket
RUN --mount=type=ssh \
    ssh -o StrictHostKeyChecking=no git@github.com

Build command:

BASH
# Secret from environment variable
docker build --secret id=my_token,env=MY_TOKEN -t my-image .

# Secret from file
docker build --secret id=db_password,src=/path/to/password.txt -t my-image .

# SSH agent forwarding
docker build --ssh default -t my-image .

Parameters and Environment Variables

Build-time Secrets (--secret)

ParameterRequiredDescription
idYesIdentifier matching --mount=type=secret,id=<id> in Dockerfile
srcNoFile path (absolute) containing secret value
envNoEnvironment variable name containing secret value

If both src and env are omitted, BuildKit looks for the id as an environment variable name.

Pre-defined Git Authentication Secrets

These secrets are automatically available when using BuildKit with Git contexts:

Secret NameDescription
GIT_AUTH_TOKENHTTP Basic auth with username x-access-token
GIT_AUTH_HEADERRaw Authorization header value

Usage:

BASH
docker build --secret id=GIT_AUTH_TOKEN,env=GITHUB_TOKEN \
    https://github.com/user/private-repo.git#main

HTTP Authentication for COPY/ADD

Secret PatternDescription
HTTP_AUTH_TOKEN_<host>Adds Authorization: Bearer <token> header
HTTP_AUTH_HEADER_<host>Adds custom Authorization header

Example:

BASH
docker build \
    --secret id=HTTP_AUTH_TOKEN_artifacts.example.com,env=ARTIFACT_TOKEN \
    -t my-image .

SSH Agent Forwarding (--ssh)

ParameterDescription
defaultForward the default SSH agent socket
id=<key>Forward a specific SSH key file

Root Cause Analysis

The fundamental issue is Docker image layer immutability. Each RUN instruction creates a new layer. With ARG or ENV, the value is baked into that layer's metadata and content. Even if you delete the secret file in a later RUN, the previous layer still contains it.

BuildKit solves this by:

  1. Mounting secrets as tmpfs filesystems during RUN execution
  2. Unmounting them immediately after the instruction completes
  3. Never recording the secret in layer diffs or image history

The secret is only accessible within the specific RUN instruction that uses --mount=type=secret.

Common Errors and Fixes

ErrorSolution
secret not found: id=my_secretEnsure id matches exactly between --secret and --mount=type=secret. Verify the source (src or env) exists and is accessible.
file not found: /path/to/secretUse absolute paths only. In CI, check working directory. Use $(pwd)/secret.txt for relative paths.
process did not complete successfully: exit code 1Debug by adding RUN ls /run/secrets/ before the failing command to verify secret mounting.
failed to mount secret: permission deniedSet file permissions to 600 or 644: chmod 600 /path/to/secret. Ensure Docker daemon user can read the file.

Production Notes and Security Checks

  1. File permissions: Set secret files to 600 (chmod 600 /path/to/secret). Avoid world-readable secrets.

  2. Avoid secret leakage in logs: Never echo or print secret values in Dockerfile RUN commands. Use --quiet flags where possible.

  3. CI/CD best practices: Store secrets in CI secret managers (GitHub Secrets, GitLab CI Variables). Pass via environment variables:

    BASH
    docker build --secret id=my_token,env=MY_TOKEN .
    
  4. Remote builds: When using docker buildx build --push, secrets traverse the network. Use TLS-encrypted Docker daemon connections.

  5. Concurrent builds: Avoid sharing secret files between concurrent builds. Use environment variables or per-build temporary files.

  6. Secret size limit: BuildKit typically limits individual secrets to ~1MB. For larger data, consider splitting or alternative approaches.

  7. Multi-stage builds: Secrets are only available in stages that explicitly mount them. This is intentional—secrets don't carry forward between stages.

FAQ

Q: How is BuildKit secret mount different from Docker ARG or ENV?

A: ARG and ENV persist values to image metadata (docker history) and final layers. Anyone with image access can extract them. BuildKit secret mounts use tmpfs that exists only during RUN execution—no trace remains in the image. This is the primary security advantage.

Q: How do I use BuildKit secrets in GitHub Actions?

A: Store secrets in GitHub Secrets, then pass them as environment variables:

YAML
- name: Build Docker image
  run: |
    docker build \
      --secret id=my_token,env=MY_TOKEN \
      -t my-image .
  env:
    MY_TOKEN: ${{ secrets.MY_TOKEN }}

Q: Can I use both file and environment variable sources for the same secret?

A: Yes. In Dockerfile: --mount=type=secret,id=mysecret,target=/tmp/secret,env=MY_SECRET. This makes the secret available both as a file at /tmp/secret and as the environment variable MY_SECRET during that RUN instruction.

Official References

Related Guides