How to use Docker build secrets to securely pass sensitive data during builds
Docker build secrets are a feature in Docker BuildKit specifically designed for developers to safely pass sensitive data, such as SSH keys, API keys, and passwords, into Docker builds without having to bake it into the final image.
Before secret support, developers had to figure out how to keep private information private. Secrets had a high chance of appearing in the finished product or during production because they must always be available during the development process.
But the days of passing secrets through environment variables or hardcoding them into Dockerfiles are over. Thanks to docker build secrets, you can now keep your private data safe from exposure.
This guide will cover how to make the best out of Docker build secrets and keep your sensitive data secure during builds.
How to create and pass Docker build secrets
Install Docker and enable Docker BuildKit
Prerequisites
- Docker
- Docker BuildKit
If you want to use Docker BuildKit and build secrets, your first step will be installing Docker. Make sure to follow specific instructions depending on your operating system. In this example, we are using Docker on Ubuntu.
If you’re hosting your container on a service, choosing a pre-made environment can save you time. With Hostinger, you can use the VPS Ubuntu template, which comes with a fully rootable and out-of-the-box Ubuntu setup. You can install Docker and set up BuildKit immediately, without worrying about additional configuration steps.

Next, you’ll need to check that Docker BuildKit is enabled in your Docker setup. You can choose whether to enable BuildKit for one single build or permanently.
To enable BuildKit temporarily, you should navigate to your project directory where your Dockerfile is and run this in your terminal:
DOCKER_BUILDKIT=1 docker build .
To enable BuildKit permanently, go to your directory and add “buildkit”: true to your ~/.docker/config.json file:
{ "features": { "buildkit": true } }
Create your secret file
A secret file is where you will keep the sensitive data you want to safeguard as you use it during your build.
For this example, we will use a file to store a database password. However, you could store an API key, service credentials, custom license keys, or any other secret you may need throughout your development process.
In your terminal, create a text file for your secret:
echo "mydb_password" > mydb-password.txt
If you have multiple secrets to store, it is advisable to create multiple files:
- echo "my_api_key" > api-key.txt - echo "my_ssh_key" > ssh-key.txt - echo "my_license_key" > license-key.txt
This will also allow each secret to be baked into the container individually, maintaining a separate identity. For the same reason, when building your image later, you should mention each secret:
docker build \ --secret id=mydb_password,src=mydb-password.txt \ --secret id=my_api_key,src=api-key.txt \ … -t my-image .
Passing the secret with the –secret flag
To pass your secret file into the build, you will need to use a –secret flag.
Here is a breakdown of this command:
- –secret: the flag BuildKit needs to recognize your build secrets file.
- id=mydb_password: this is the identifier for my database password used inside the Dockerfile.
- src=/path/to/mydb-password.txt: this should be the file where you previously stored your secrets.
Together, it should look like this:
--secret id=mydb_password,src=/path/to/mydb-password.txt \ .
We will use this after referencing the secrets in the Dockerfile to build the image.
Consuming secrets in Dockerfiles
Reference your secret in your Dockerfile
To use the secrets in your build, you will need to update your Dockerfile so that your secret is referenced.
We will be using RUN –mount=type=secret,id=mydb_password, which allows your secret to be mounted into the build container.
For this tutorial’s purpose, we will also add –no-cache –progress=plain to the build command to ensure we can clearly see the progress.
Here is an example of our Dockerfile mounting our database password as a secret:
# Use Ubuntu base image FROM ubuntu:22.04 # Mount a secret and check if it’s there, but don’t print the secret value RUN --mount=type=secret,id=mydb_password \ ls -l /run/secrets/ && \ echo "Secret is available and correctly mounted!" || echo "No secret found"
Build your image
To build your image, run the following:
docker build --no-cache --progress=plain --secret id=mydb_password,src=/path/to/mydb-password.txt \ -t my-image .
If everything is working as intended, you should see something like this on your terminal:

Accessing secrets within build steps
Since secrets are mounted inside the build container for the duration of a specific RUN step, they are only available at the mount location for that step.
Once mounted, secrets are read-only, meaning that if you want to edit or update them in any way, you will have to do so starting from the secret .txt file. Once the RUN command is done, the secret is not stored in the image and is not available in the container.
You can access and view your secrets by printing them during the build. However, be cautious about doing so liberally. If forgotten and left in the build, it could lead to a significant security breach.
Therefore, updating your Dockerfile as follows:
# Mount a secret and access the secret, else get "No secret found" RUN --mount=type=secret,id=mydb_password \ ls -l /run/secrets/ && cat /run/secrets/mydb_password || echo "No secret found"
And then running your build:
docker build --no-cache --progress=plain --secret \ id=mydb_password,src=mydb-password.txt \ -t my-image .
Would expose your secret plain for all to see. Good for testing, but not good for production.

How are secrets kept secure?
The great value of Docker secret builds is that sensitive data is never saved in the final image, avoiding all risks of exposure.
What differentiates Docker secret builds from previous approaches is that:
- Access to the secret is temporary during the build step. The secret is no longer available in the build context or the container after that step is finished.
- Your private data, once passed as a secret, is never permanent in the image. Docker mounts secret files temporarily into the container only during the build step where they are needed. They are then removed. Secrets are not even visible when scouring the image’s history because they are not part of any image layers.
- Docker ensures that the directory /run/secrets/, where secrets are mounted, is only accessible during the build process. During the build process, secret files cannot be copied, written to, or altered in any other location.
Using Docker Compose with build secrets
Docker Compose allows developers to define and manage multi-container applications using a single Docker-compose.yml configuration file. With Docker Compose, you can handle complex environments with simple commands and guarantee consistency, trusting the same Compose file from local development on your machine all the way through testing and deployment.
Define your secrets
To build secrets using Docker Compose, you will need to update your configuration file so that they are referenced appropriately. Secrets are declared in the secrets section of the docker-compose.yml file.
version: "3.8" services: db: image: postgres environment: POSTGRES_PASSWORD_FILE: /run/secrets/mydb_password secrets: - mydb_password secrets: mydb_password: file: ./secret/mydb-password.txt
In the above file, we are using a db service to run a PostgreSQL container, and the POSTGRES_PASSWORD_FILE environment variable is referencing the mydb_password secret stored at /run/secrets/mydb_password.
In the secrets section, the mydb_password secret is defined and linked to the mydb-password.txt file.
Run docker-compose up to start your container.
This command will:
- Build and start the db service.
- Mount the secret into the container at /run/secrets/mydb_password.
- PostgreSQL will automatically use the password stored in this secret.
Managing secrets in a Compose environment: best practices
For sensitive data to be safe while still being available to containers during runtime, properly managing secrets in a Docker Compose environment is essential. Keep in mind that secrets are supported in Docker Compose version 3.1 and above. Make sure to check that your version is up to date.
For extra safety, you can consider using external services such as HashiCorp Vault or Docker Swarm‘s built-in secret management. While these are not natively integrated with Docker Compose, you can use API calls to retrieve secrets dynamically during container startup or sync secrets manually to the file system before running docker-compose up.
These tools offer more sophisticated security features like automatic secret rotation, secure access controls, and encryption while at rest.
Here are some tips to manage Docker build secrets in your Compose environment.
- Don’t hardcode secrets: Hardcoding secrets into your configuration file docker-compose.yml or Dockerfiles defeats the purpose of using Docker build secrets. Always use external files to store your private data.
- Permissions on secrets: For an extra layer of security, verify that the host machine’s secret files are appropriately protected with the right file permissions. Restrict access to these files to those who need it and refrain from granting needless write or read access.
chmod 600 ./secrets/mydb_password.txt # Only the owner can read and/or write
- Periodically rotate secrets: Rotating secrets is a good habit to ensure safety. Secret rotation can be automated by external secret managers, such as HashiCorp Vault.
- Encrypt secrets before storing them: Another good practice is to encrypt secrets before storing them in secrets in files or secret management systems. Use strong encryption such as AES-256 or RSA.
Using SSH mounts for secure access
By using SSH mounts in Docker, you can safely send SSH keys to the build process without embedding them in the final image or Dockerfile. This feature is part of Docker BuildKit and is especially helpful for private resource access during the build phase.
Common use cases include:
- Fetching private Git repositories: SSH mounts let you safely clone private repositories during the build process and access their code.
- Deploying to remote servers: SSH mounts enable your container to access SSH keys for successful authentication on a remote server and to securely copy files.
- Continuous Integration/Continuous Deployment (CI/CD) pipelines: When dealing with CI/CD pipelines, SSH mounts allow protected access to private repositories, servers, or APIs during the build and deployment phases.
Using the –ssh flag in the docker build command
During a build, using the –ssh flag allows Docker to send the SSH key or keys from your local SSH agent to the container. The key is only momentarily accessible during the build step. Sensitive information and private keys are this way safeguarded, as they are never written to disk or incorporated into the final image layers.
To use it, you must declare it when enabling your BuildKit.
You can do so temporarily:
DOCKER_BUILDKIT=1 docker build --ssh default .
Or permanently in your ~/.docker/config.json file:
{ "features": { "buildkit": true } }
This assumes your SSH agent is running and has your private key already loaded.
Check if your SSH agent is working correctly by running
ssh-add -l
If you see an error indicating a lack of connection instead of a list of your SSH keys, it means that the agent is not running.
Start your SSH agent and set environment variables to make sure your terminal knows how to talk to the agent with the following command:
eval $(ssh-agent)
When the agent is running, add your private key to the agent:
ssh-add ~/.ssh/id_rsa
If your key is passphrase-protected, consider creating a new one (without passphrase protection) for Docker operations only.
Your key is now loaded into memory and available for Docker BuildKit.
Example of Dockerfile using SSH mounts
This is what part of your Dockerfile should look like when using SSH mounts to clone a private Git repository.
FROM ubuntu:22.04 # Install dependencies RUN apt-get update && apt-get install -y git # Add GitHub's SSH key to known_hosts (this avoids host verification errors) # This is a simplified approach for testing purposes RUN mkdir -p ~/.ssh && \ ssh-keyscan github.com >> ~/.ssh/known_hosts # Clone a private Git repository with SSH RUN --mount=type=ssh git clone git@github.com:your-org/your-private-repo.git /myproject # Continue with the build… …
In the RUN step, the –mount=type=ssh directive is used to mount the SSH key during the git clone operation during the build process. Just like with build secrets, the SSH key is only accessible during the build and is not stored in the final image.
You can now build as usual using:
DOCKER_BUILDKIT=1 docker build --ssh default -t my-image .
If BuildKit is permanently enabled, use this code instead:
docker build --ssh default -t my-image .
Best practices for managing Docker build secrets
Here are some handy best practices for managing Docker build secrets and protecting sensitive data throughout the development process with Docker containers.
- Never use ARG or ENV instructions for sensitive data: These commands make values visible through the docker history and the layers of the finished image.
- Have separate secrets for each environment: Avoid sharing credentials between environments and use distinct secrets for development, staging, and production.
- Secrets directory in .gitignore: Make sure secrets are excluded from Git.
- Make use of CI/CD secret management features: Instead of baking secrets into files, use the secure methods offered by the majority of CI/CD platforms (such as GitHub Actions or Jenkins) for injecting secrets at runtime.
- Never hardcode your secrets: As mentioned before, never hardcode your secrets into Dockerfiles, source code, or configuration files. Use secure mounts instead.
- Audit Dockerfiles: Check Dockerfiles frequently for dangerous practices that might have crept into the codebase, such as ENV PASSWORD= and ARG TOKEN=.
- Use third-party tools to monitor for accidental exposure: Examine your repositories for hardcoded secrets using tools like Snyk, GitGuardian, GitLeaks, and TruffleHog before they become a liability.
Conclusion
During Docker builds, disclosing private data like credentials, tokens, and API keys can result in major security flaws. Adopting safe practices when handling sensitive data during builds will save you headaches throughout the development process and avoid creating vulnerabilities in the future.
Maintaining secure management is a continuous process that should be integrated into your production and development workflows. Make sure to make good use of the best practices mentioned in this article, and you’ll be able to protect your data from potential exploitation and prying eyes.
Docker build secrets FAQ
What are the types of build secrets?
Docker supports two types of build secrets:
– Secret mounts: General-purpose secrets, such as credentials or API keys, are stored in secret mounts.
– SSH mounts: During builds, SSH keys are used to access private repositories or services.
What happens if I accidentally expose a build secret?
A build secret being exposed may result in unauthorized access or security breaches. The exposed secret should be invalidated right away, and you should check your system for possible compromise to reduce risks.
Can build secrets be reused across different Docker builds?
It is possible to reuse build secrets for different Docker builds. You can pass the same secret to different builds by using the –secret flag.
Are there size limitations for secrets passed during a build?
Yes, Docker limits the size of secrets passed during a build to 500 KB. To prevent mistakes during the build process, be aware of this restriction when handling large secrets.