Direkt zum Inhalt

Docker Build Secrets Guide: Secure Container Image Development

Learn how to use Docker build secrets to handle sensitive data securely during image builds. Master secret mounts, SSH authentication, and CI/CD integration.
16. Dez. 2025  · 11 Min. lesen

I've seen countless Docker images accidentally exposed with hardcoded API keys, database passwords, and authentication tokens embedded in their layers. These security breaches often occur during the build process when developers need temporary access to private resources but inadvertently bake credentials into the final image.

The challenge is that traditional approaches to handling credentials during builds, like environment variables or build arguments, aren't designed for ephemeral use. They leave permanent traces in image metadata or layers, creating security vulnerabilities that persist long after the build completes. This creates a dilemma: how do you authenticate to private resources during builds without compromising security?

Docker build secrets solve this critical security challenge by providing a mechanism to use sensitive data during image builds without leaving traces in the final artifact. 

In this tutorial, I'll walk you through implementing build secrets effectively, from basic concepts to advanced CI/CD integration, ensuring your container builds remain secure.

If you are new to Docker, I recommend taking one of our courses, such as Introduction to Docker, Containerization and Virtualization with Docker and Kubernetes, or Intermediate Docker

What Are Docker Build Secrets?

Let's start by understanding what makes build secrets different from other credential management approaches. Docker build secrets are ephemeral pieces of sensitive information that are made available during the build process but never stored in the image layers or filesystem. They exist only in memory during specific build steps and disappear once those steps are complete.

The necessity of build secrets stems from modern application development workflows. 

You often need to authenticate to private package repositories, clone private Git repositories, download licensed software, or access internal APIs during builds. 

Without build secrets, developers resort to dangerous workarounds, like hardcoding credentials in Dockerfiles, passing secrets as build arguments (which persist in image metadata), or creating overly permissive base images with embedded credentials.

The risks of inadequate secrets handling are substantial. Exposed credentials can lead to unauthorized access to production systems, data breaches, compliance violations, and significant reputational damage. Common scenarios where secrets leak include API keys for package managers, SSH keys for Git operations, authentication tokens for internal services, and database credentials for build-time migrations.

Docker BuildKit: The Foundation of Secure Build Secrets

Before diving into implementation, you need to understand BuildKit, the modern build engine that makes secure secrets possible. BuildKit is Docker's next-generation builder that replaced the classic build engine, offering significant improvements in performance, caching, and security.

BuildKit uses a graph-based execution model that tracks dependencies between build steps. This architecture allows BuildKit to mount secrets as temporary file systems that exist only during specific RUN instructions, ensuring they never become part of the image layers. 

Unlike classic Docker builds, where everything in the build context becomes part of the build cache, BuildKit treats secrets as special resources that bypass the layer cache entirely.

Enabling BuildKit is straightforward. For Docker 23.0 and later, BuildKit is the default builder. For earlier versions, set the environment variable DOCKER_BUILDKIT=1 before running build commands:

export DOCKER_BUILDKIT=1
docker build -t myapp .

Alternatively, enable it permanently by configuring the Docker daemon or using Docker Buildx, which always uses BuildKit. The key difference between classic builds and BuildKit-enabled builds is that secrets in classic builds would persist in intermediate layers if not carefully managed, while BuildKit guarantees secrets are ephemeral and never written to disk as part of the image.

With BuildKit's secure foundation in place, let's explore the different types of secrets it enables and how to implement each one effectively.

Types and Implementation of Docker Build Secrets

BuildKit supports three primary mechanisms for handling secrets during builds, each designed for specific use cases. Understanding when to use each type ensures optimal security and functionality.

Secret mounts: general-purpose sensitive data

Secret mounts provide the most flexible approach for handling sensitive data during builds. They allow you to mount secrets as files at specified paths during RUN instructions, making credentials available to build commands without persisting them in layers.

The process involves two steps: passing the secret to the build command and mounting it in the Dockerfile. First, create a secret file locally or use an environment variable. Then reference it during the build:

docker build --secret id=api_key,src=./api_key.txt -t myapp .

In your Dockerfile, mount the secret during the RUN instruction that needs it:

RUN --mount=type=secret,id=api_key \
    API_KEY=$(cat /run/secrets/api_key) && \
    curl -H "Authorization: Bearer $API_KEY" https://api.example.com/package > /app/data.json

By default, secrets mount at /run/secrets/<id>, but you can specify custom targets using the target parameter. 

Best practices include mounting secrets only in the specific RUN commands that need them, never copying secrets to the image filesystem, and using multi-stage builds to separate secret-requiring stages from the final image. 

The source can be a file path, or use env to pass secrets from environment variables: --secret id=token,env=API_TOKEN.

To mount a secret as an environment variable instead of a file, use the env option:

RUN --mount=type=secret,id=db_user,env=DB_USER \
    --mount=type=secret,id=db_password,env=DB_PASSWORD \
    ./setup-database.sh

You can use both target and env options together to mount a secret as both a file and an environment variable. 

Best practices include mounting secrets only in specific RUN commands that need them, never copying secrets to the image filesystem, and using multi-stage builds to separate secret-requiring stages from final images.

SSH mounts: secure access to private resources

SSH mounts specifically handle SSH-based authentication, primarily for accessing private Git repositories during builds. Rather than copying SSH keys into the image (a security nightmare), SSH mounts provide temporary access to your SSH agent during build time.

To use SSH mounts, ensure your SSH agent is running locally with the required keys loaded. Then reference the SSH mount in your Dockerfile:

RUN --mount=type=ssh \
    git clone git@github.com:myorg/private-repo.git /app

Build the image with SSH forwarding enabled:

docker build --ssh default -t myapp .

For multiple hosts with different keys, you can specify named SSH mounts:

RUN --mount=type=ssh,id=github \
    git clone git@github.com:myorg/repo.git

Then provide specific keys during the build:

docker build --ssh github=$HOME/.ssh/github_key -t myapp .

You’ll find that this approach maintains security by never exposing private keys in the image while enabling authenticated access to private repositories during the build process.

Git authentication for remote contexts

When your Docker build needs to access private Git repositories, either as the build context itself or when fetching dependencies during the build, you need a way to authenticate without embedding credentials. This is particularly common when building from a private repository URL or using ADD instructions to fetch private code.

BuildKit provides two pre-defined secrets for Git authentication: GIT_AUTH_TOKEN and GIT_AUTH_HEADER. These are "pre-flight" secrets that authenticate the builder before any Dockerfile instructions execute, securing the initial repository fetch.

The most common pattern uses GIT_AUTH_TOKEN for token-based HTTPS authentication:

GIT_AUTH_TOKEN=$(cat ~/.github-token) docker build \
    --secret id=GIT_AUTH_TOKEN \
    https://github.com/myorg/private-repo.git

This works seamlessly with ADD instructions fetching private repositories:

FROM alpine
ADD https://github.com/myorg/private-configs.git /configs

For ephemeral CI/CD environments, inject tokens from your platform's secret store:

- name: Build from private repo
  env:
    GIT_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    docker build --secret id=GIT_AUTH_TOKEN https://github.com/myorg/app.git

The GIT_AUTH_HEADER secret provides an alternative for custom authentication schemes when standard token authentication isn't sufficient.

How to Use Docker Build Secrets

Now that we've covered the types of build secrets, let's explore how to implement them effectively in real-world scenarios.

Creating and structuring secrets for build time

Proper secret preparation is crucial for security. Store secrets in files outside your Docker build context, ensure they're added to .gitignore and .dockerignore, and use restrictive file permissions (600 or 400).

For multiple secrets, create a dedicated secrets directory:

mkdir -p .secrets
echo "my-api-key" > .secrets/api_key
chmod 600 .secrets/*

Then reference them during builds:

docker build \
    --secret id=api_key,src=.secrets/api_key \
    --secret id=db_password,src=.secrets/db_password \
    -t myapp .

For environment variable-based secrets common in CI/CD:

docker build \
    --secret id=api_key,env=API_KEY \
    --secret id=db_pass,env=DB_PASSWORD \
    -t myapp .

Integration with multi-stage builds

Multi-stage builds are powerful when combined with build secrets. Use secrets in early stages for fetching dependencies, then copy only necessary artifacts to the final stage:

# Build stage - secrets used here
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=secret,id=api_key \
    API_KEY=$(cat /run/secrets/api_key) && \
    curl -H "Authorization: Bearer $API_KEY" https://api.company.com/data.json -o data.json && \
    pip install -r requirements.txt

# Final stage - no secrets present
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app/data.json ./data.json
COPY . .
CMD ["python", "app.py"]

This ensures secrets exist only during the builder stage and never appear in the final image.

Multi-stage builds elegantly handle single secrets, but real-world applications often require more complexity. 

Let's examine how to manage scenarios involving multiple secrets or intricate build requirements

Handling multiple secrets and complex scenarios

Complex builds often require multiple secrets within the same RUN instruction. BuildKit supports mounting multiple secrets simultaneously:

RUN --mount=type=secret,id=api_key \
    --mount=type=secret,id=db_password \
    API_KEY=$(cat /run/secrets/api_key) && \
    DB_PASS=$(cat /run/secrets/db_password) && \
    ./configure.sh

For scenarios requiring secrets in multiple commands, combine commands in a single RUN instruction to minimize secret mounts while maintaining security boundaries.

Docker Build Secrets Security Best Practices

Implementing build secrets correctly requires following established security practices. Let me share the patterns I've found most effective for maintaining security throughout the build process.

Preventing secret exposure and leakage

After building an image, verify secrets haven't leaked by inspecting the image layers:

docker history myapp:latest
docker save myapp:latest | tar -x

Examine the extracted layers for any sensitive data. Additionally, distinguish between build-time secrets (temporary, used during builds) and runtime secrets (needed when containers run). Never use build secrets for runtime credentials. Use Docker secrets, Kubernetes secrets, or environment variables injected at runtime instead.

Always add secret files to .gitignore:

# .gitignore
.secrets/
*.key

And to .dockerignore to prevent accidental inclusion in build contexts:

# .dockerignore
.secrets/
*.key
.env

Managing secrets in CI/CD environments

CI/CD platforms provide native secret management that integrates seamlessly with Docker build secrets. In GitHub Actions:

- name: Build Docker image
  env:
    API_KEY: ${{ secrets.API_KEY }}
  run: |
    docker build \
      --secret id=api_key,env=API_KEY \
      -t myapp .

The key principle is leveraging platform-provided secret stores rather than storing secrets in repositories. CI/CD secrets are injected as environment variables, which BuildKit can consume directly through the env source type.

Secret permissions and access control

When building as non-root users, ensure secret files have appropriate read permissions. If your Dockerfile uses USER to switch to a non-root user, mount secrets in locations accessible to that user:

FROM python:3.11-slim
RUN useradd -m appuser
USER appuser

RUN --mount=type=secret,id=token,uid=1000 \
    cat /run/secrets/token > /dev/null

For enhanced security compliance, implement secret rotation strategies. Use short-lived tokens when possible, implement automated rotation in CI/CD pipelines, and maintain audit logs of secret usage. 

Consider secrets with expiration dates and automated renewal processes to minimize exposure windows.

So far, we've focused on single-container builds, but modern applications often consist of multiple services. Let's see how Docker Compose extends build secret capabilities to multi-service architectures.

Integrating Build Secrets With Docker Compose

Docker Compose simplifies managing build secrets across multi-service applications. In your docker-compose.yml, define secrets at the top level and reference them in service build configurations:

services:
  app:
    build:
      context: .
      secrets:
        - api_key
        - db_password
  
secrets:
  api_key:
    file: ./.secrets/api_key
  db_password:
    environment: DB_PASSWORD

Then in your Dockerfile, mount the secrets as usual:

RUN --mount=type=secret,id=api_key \
    --mount=type=secret,id=db_password \
    ./setup.sh

Build with Compose:

docker-compose build

This pattern scales well for applications with multiple services requiring different secrets, maintaining centralized secret management while preserving security boundaries between services.

As you implement build secrets across your projects, you'll inevitably encounter issues. Let's address the most common challenges and their solutions to help you troubleshoot effectively.

Common Challenges and Troubleshooting

Even with proper implementation, you'll encounter challenges. Here are the most common issues and solutions.

Syntax errors and mount declaration issues

The --mount syntax is strict. Common mistakes include missing commas between parameters, incorrect parameter names, and wrong mount types. The correct syntax is:

RUN --mount=type=secret,id=secret_name,target=/path \
    command

If builds fail with "secret not found," verify BuildKit is enabled, and the Dockerfile ID matches the --secret id in your build command.

Secrets not available or not found

When secrets aren't accessible, verify the source file exists and is readable:

cat .secrets/api_key
docker build --secret id=api_key,src=.secrets/api_key .

For environment variables, confirm they're set:

echo $API_KEY
docker build --secret id=api_key,env=API_KEY .

Environment variable vs. file confusion

With build secrets, secrets are always mounted as files by default at /run/secrets/<id>. To use them as environment variables, read the file content:

RUN --mount=type=secret,id=token \
    export TOKEN=$(cat /run/secrets/token) && \
    curl -H "Authorization: Bearer $TOKEN" https://api.example.com

Never use build arguments (ARG) for secrets, as they persist in image metadata.

Persistence and layer caching issues

Secrets don't persist between RUN instructions by design. If multiple RUN commands need the same secret, either combine them or remount the secret:

RUN --mount=type=secret,id=token \
    command1

RUN --mount=type=secret,id=token \
    command2

BuildKit ensures secrets never enter the layer cache.

Once you've mastered the fundamentals and common troubleshooting, you may encounter scenarios requiring more sophisticated approaches. Let's explore advanced use cases and edge cases that go beyond standard implementations.

Docker Build Secrets Advanced Uses

For complex deployment scenarios, you'll need to handle additional considerations beyond basic implementation.

Secrets in complex CI/CD pipelines

Multi-stage CI/CD pipelines often require secrets across different stages. Implement stage-specific secrets rather than sharing credentials. Use CI/CD platform features for secret scoping, limiting which pipeline stages can access which secrets. Automate secret cleanup after builds to minimize exposure windows.

Secrets with non-Docker builders

Alternative builders like Buildah and Kaniko have varying support for BuildKit's secret syntax. Buildah supports similar secret mounting through --secret flags. Kaniko, designed for Kubernetes environments, uses different mechanisms, typically Kubernetes secrets mounted as volumes. When using non-Docker builders, consult their specific documentation, as implementations differ significantly.

Secrets and security compliance

Build secrets align with security compliance frameworks by providing auditable, ephemeral credential access. 

For GDPR compliance, ensure secrets don't leak personally identifiable information into layers. SOC2 requirements benefit from building secret audit trails—log when secrets are used and by whom. Maintain records of secret access for compliance audits.

Rotating and updating secrets

Establish processes for secret rotation without disrupting builds. Use versioned secret files or environment variable naming conventions that allow switching between versions. 

Implement automated rotation in CI/CD pipelines where secrets are refreshed from central stores. For tokens with expiration dates, monitor expiration and automate renewal processes.

Conclusion: Best Practices and Recommendations

Docker build secrets represent a fundamental security improvement for containerized application development. Throughout this tutorial, we've explored how to implement secrets securely, from basic secret mounts to complex CI/CD integration.

The key takeaways are straightforward: always use BuildKit's secret mounting mechanisms rather than build arguments or hardcoded credentials, implement multi-stage builds to isolate secret-using stages from final images, leverage CI/CD platform secret management for automated workflows, and regularly audit images to verify secrets haven't leaked into layers.

For organizations adopting these practices, start with a secret management policy defining which secrets require build-time access, establish automated rotation schedules, implement comprehensive testing to verify secrets don't persist in images, and train development teams on proper secret handling patterns. By treating build secrets as a critical security component, you'll significantly reduce your attack surface and build more secure containerized applications.

To keep learning, I recommend checking out the following resources:

Docker Build Secrets FAQs

What are Docker build secrets?

Docker build secrets are ephemeral pieces of sensitive information made available during the build process but never stored in image layers, existing only in memory during specific build steps.

What types of build secrets does Docker support?

Docker supports three types: secret mounts for general-purpose sensitive data, SSH mounts for secure Git repository access, and Git authentication secrets (GIT_AUTH_TOKEN and GIT_AUTH_HEADER) for private remote contexts.

How do I prevent secrets from appearing in my Docker images?

 Use BuildKit's --mount=type=secret syntax in RUN instructions combined with multi-stage builds, never use build arguments or environment variables for secrets, and regularly audit images using docker history to verify no leakage.

Can I use Docker build secrets with Docker Compose?

Yes, Docker Compose supports build secrets through the secrets section in your compose file, allowing you to define secrets at the top level and reference them in service build configurations.

How do build secrets work in CI/CD pipelines?

CI/CD platforms inject secrets as environment variables that can be passed to Docker builds using --secret id=name,env=VARIABLE_NAME, enabling automated workflows without storing credentials in repositories.


Benito Martin's photo
Author
Benito Martin
LinkedIn

As the Founder of Martin Data Solutions and a Freelance Data Scientist, ML and AI Engineer, I bring a diverse portfolio in Regression, Classification, NLP, LLM, RAG, Neural Networks, Ensemble Methods, and Computer Vision.

  • Successfully developed several end-to-end ML projects, including data cleaning, analytics, modeling, and deployment on AWS and GCP, delivering impactful and scalable solutions.
  • Built interactive and scalable web applications using Streamlit and Gradio for diverse industry use cases.
  • Taught and mentored students in data science and analytics, fostering their professional growth through personalized learning approaches.
  • Designed course content for retrieval-augmented generation (RAG) applications tailored to enterprise requirements.
  • Authored high-impact AI & ML technical blogs, covering topics like MLOps, vector databases, and LLMs, achieving significant engagement.

In each project I take on, I make sure to apply up-to-date practices in software engineering and DevOps, like CI/CD, code linting, formatting, model monitoring, experiment tracking, and robust error handling. I’m committed to delivering complete solutions, turning data insights into practical strategies that help businesses grow and make the most out of data science, machine learning, and AI.

Themen

Top DataCamp Courses

Lernpfad

Containerisierung und Virtualisierung mit Docker und Kubernetes

13 Std.
In diesem interaktiven Track lernst du die Leistungsfähigkeit von Docker und Kubernetes kennen und kannst Anwendungen in modernen Umgebungen entwickeln und einsetzen.
Details anzeigenRight Arrow
Kurs starten
Mehr anzeigenRight Arrow
Verwandt

Blog

Top 18 Docker Commands to Build, Run, and Manage Containers

This guide breaks down essential Docker commands—from container basics to volumes and networking—so you can confidently manage applications across environments!
Laiba Siddiqui's photo

Laiba Siddiqui

15 Min.

Blog

How to Learn Docker from Scratch: A Guide for Data Professionals

This guide teaches you how to learn Docker from scratch. Discover practical tips, resources, and a step-by-step plan to accelerate your learning!
Joel Wembo's photo

Joel Wembo

14 Min.

Tutorial

Docker Build Args: The Ultimate Guide for Data Professionals

Learn how to supercharge your containerization workflow with Docker build arguments for flexible, secure, and consistent environments.
Dario Radečić's photo

Dario Radečić

Tutorial

Docker Compose Guide: Simplify Multi-Container Development

Master Docker Compose for efficient multi-container application development. Learn best practices, scaling, orchestration, and real-world examples.
Derrick Mwiti's photo

Derrick Mwiti

Tutorial

Docker for Beginners: A Practical Guide to Containers

This beginner-friendly tutorial covers the essentials of containerization, helping you build, run, and manage containers with hands-on examples.
Moez Ali's photo

Moez Ali

Tutorial

Run a Docker Image as a Container: A Practical Beginner’s Guide

This tutorial teaches you how to confidently run Docker images using docker run, with clear examples, practical tips, and troubleshooting advice.
Srujana Maddula's photo

Srujana Maddula

Mehr anzeigenMehr anzeigen