
It's finally here:
>> The Road to Membership and Baeldung Pro.
Going into ads, no-ads reading, and bit about how Baeldung works if you're curious :)
Last updated: February 15, 2025
Handling secrets securely during the building of Docker images is crucial. It’s not advisable to hardcode secrets, such as database credentials, tokens, and API keys into Dockerfiles, or embed them in the resulting image.
One common approach to introducing secrets during the build process involves using files. In our case, we’ll explore a less common approach, which involves the introduction of secrets directly from environment variables.
In this tutorial, we’ll explore how to securely and effectively add a secret to a Docker build via environment variable.
During the build process, the docker build command doesn’t provide a straightforward way to pass sensitive information without risking exposure. Therefore, it can become a challenge to manage secrets in Docker builds.
Firstly, embedding secrets in the image makes them accessible to anyone with access to the image, which violates best practices.
Secondly, using build arguments (–build-arg) is convenient, but not secure, since they’re stored in the image metadata.
Additionally, using temporary files can be a complicated approach. For instance, if these files are not properly removed after they’re used, they may remain on the system and expose sensitive information to unauthorized access.
To manage secrets more securely, we can leverage the –secret flag (introduced in Docker 18.09) with BuildKit.
Before we proceed, we need to verify the Docker version, because BuildKit and the –secret flag are only available in Docker versions 18.09 or later. Additionally, we need to enable BuildKit, since it’s not enabled by default in some Docker installations. We also need to ensure the Dockerfile utilizes a syntax that’s compatible with BuildKit.
Now, let’s delve into the steps for passing a secret from an environment variable.
First, let’s store the secret in an environment variable:
$ export MY_SECRET="super-secret-value"
Here, we avoid hardcoding the secret directly into a Dockerfile or a script. Instead, we depend on an environment variable to store the secret, isolating it from the build configuration.
To demonstrate, let’s use a simple directory structure:
$ tree sample_project
sample_project
└── Dockerfile
0 directories, 1 file
Our working directory holds the name sample_project. Let’s edit the Dockerfile to contain:
# syntax=docker/dockerfile:1.4
FROM ubuntu:20.04
# Use the secret during the build process
RUN --mount=type=secret,id=my_secret cat /run/secrets/my_secret > /tmp/secret_output
CMD ["bash"]
Let’s analyze the content of the Dockerfile:
At this point, the Dockerfile setup is complete, and we can proceed to build the image.
In this step, we need to build the image using BuildKit and pass the secret securely using the –secret flag:
$ DOCKER_BUILDKIT=1 docker build \
--secret id=my_secret,env=MY_SECRET \
-t my_image .
Let’s break down the command:
Additionally, the dot (.) sets our current directory (sample_project/) as the build context. Therefore, Docker uses this directory to find the Dockerfile.
In this final step, let’s verify if the secret was correctly used during the build:
$ docker run --rm my_image cat /tmp/secret_output && echo
super-secret-value
The above output proves everything worked correctly, since we can see the secret value printed in the console. To clarify, the && echo command introduces a newline after the output.
Let’s note that the secret isn’t stored in the final image; it’s only available during the build process.
Although the process usually goes smoothly, we may occasionally encounter problems. Let’s look at a few of the most common issues and how to tackle them.
If the secret is unavailable during the build, then Docker can’t access it while building the image.
Now, this issue may arise if BuildKit isn’t enabled. The –secret flag only works with BuildKit, a new and advanced Docker build system. As a consequence, if BuildKit isn’t enabled, Docker fails to recognize the –secret flag, and the secret isn’t passed in. So, we can ensure that we use DOCKER_BUILDKIT=1 before the build command to enable BuildKit.
We may also experience this issue when we store the secret in an environment variable after running the docker build command. So, we need to set the environment variable first and then confirm that it’s set:
$ echo $MY_SECRET
super-secret-value
If the output is empty, the build doesn’t receive the secret.
Also, an incorrect id value can cause this issue. In the build command defined in subsection 3.3, Docker takes the value from the environment variable MY_SECRET and assigns it the label my_secret. Meanwhile, in the Dockerfile defined in sub-section 3.2, we instruct Docker to use the secret with the label my_secret. Therefore, we need to ensure that the id in the Dockerfile matches the id in the build command.
This issue occurs if the secret value is present in the final image.
For this, we can ensure that secrets are only utilized in intermediate build steps. To explain, this means using the secret only in temporary commands that don’t create a new image layer:
RUN echo "$MY_SECRET" > /app/config
So, we need to avoid a command like the one above, because it causes the secret to persist in the image.
Alternatively, we can ensure that secrets are only temporarily accessible and can’t be copied to later layers using –mount=type=secret.
Finally, we can verify that no build steps include commands that explicitly copy a secret file into the image:
COPY secret_file /app/config
Above, if secret_file contains sensitive information, it’s embedded in the image layers, making it retrievable.
In this article, we discussed adding a secret to docker build from an environment variable.
First, we defined the secret as an environment variable. Next, we created a Dockerfile and leveraged Docker BuildKit’s –secret flag to build the Docker image. From this, we managed to introduce our secret into the build process without exposing it in the final image. Additionally, we managed to troubleshoot a few common mistakes, ensuring that secrets are not mistakenly stored in the final image.
It’s essential to pass a secret securely to docker build to maintain an application’s security. Hence, adding this concept to our Docker workflows can be useful.