How to Keep Docker Secrets Secure
Secret values such as API keys, passwords, and certificates need to be safely handled throughout the software development process and your app’s runtime. Exposure of secrets can be catastrophic, as unauthorized actors could use the credentials to perform privileged interactions with your services. Secrets are often encountered when you’re working with Docker containers. It can be challenging to handle container secrets correctly because Docker historically lacked a built-in secrets management system. In this article, you’ll learn how to use secrets securely when you’re building Docker images and starting containers. What are Docker secrets? Docker secrets provide a secure way to manage sensitive data your applications need to function, such as passwords, API keys, and certificates, without embedding them directly into your Docker images or exposing them in your container configurations. They’re specifically designed for use within a Docker Swarm cluster, although alternative solutions exist for standalone Docker environments. Secrets are managed using the docker secret command and are only accessible to services explicitly granted access. Unlike traditional methods such as environment variables, Docker secrets are encrypted at rest and transmitted securely within the Swarm cluster. Docker mounts secrets as files in the /run/secrets/ directory by default within the container. Key features of Docker secrets Here are some of the key features of Docker Secrets: Runtime-only access - By default, secrets are mounted only by the containers that need them in the /run/secrets path and automatically unmounted when the containers stop. Secret rotation - Secrets have unique identifiers, and they can be rotated without any downtime. Native integrations - Docker secrets integrate natively with Docker Compose and Docker Swarm. Encryption - Secrets are encrypted both at rest and in transit. Why do you need secret management in Docker? Safe secrets management is a crucial component of software supply chain security. In recent years, leaks of API keys and authentication tokens --- including ones discovered in Docker images --- have contributed to major data breaches. If a value is useful to an attacker, it needs to be treated as a secret. Unfortunately, container workflows often lack support for secrets management. Developers tend to configure containers using environment variables, but these are visible in plaintext from outside the container: $ docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=foobar mysql:8.0 96c995563df92a2d1341564cf635d284bc70fe1aa9ba4bba8da371f765243a35 The value of MYSQL_ROOT_PASSWORD should be kept secret. If it's exposed, the recipient could log in to the MySQL database as root. However, the use of an environment variable means the password can be retrieved easily using docker inspect: $ docker inspect mysql [ { "Config": { "Env": [ "MYSQL_ROOT_PASSWORD=foobar" ] } } ] Similarly, software within the container can see the environment variable and its value. This makes it easier for a malicious process to steal the value after the container is compromised: $ docker exec -it mysql bash bash-4.4# echo $MYSQL_ROOT_PASSWORD foobar Finally, secrets configured as environment variables also tend to be committed accidentally to your repositories. They tend to turn up in docker-compose.yml, for example, either as forgotten test values or because developers fail to recognize the risks of hardcoded secrets. version: "3" services: mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD=foobar Let's explore how to avoid these risks and handle secrets securely with Docker. You can also read more about Container Security Best Practices & Solutions. Docker secrets vs environment variables Docker Secrets store sensitive data like passwords securely and are encrypted, managed via Docker Swarm or Kubernetes, and accessed as files (e.g., in /run/secrets). Environment variables are plain text, suitable for non-sensitive configurations, accessible in the container's environment, but prone to leaks. In short, secrets are more suitable for sensitive data and environmental variables in general settings. How to use secrets in Docker Docker includes a secrets management solution, but it doesn't work with standalone containers. You can supply secrets to your containers when you're using either Docker Compose or Docker Swarm. There's no alternative for containers created manually with a plain docker run command. Let's explore how to use the two available methods to securely set secrets in containers. How to use secrets with Docker Compose Compose's secrets system is the most accessible for everyday use. It's also your only option if you want to manage Docker secrets without Swarm. Secrets are defined in Compose files within the top-level secrets field. Each named sec

Secret values such as API keys, passwords, and certificates need to be safely handled throughout the software development process and your app’s runtime. Exposure of secrets can be catastrophic, as unauthorized actors could use the credentials to perform privileged interactions with your services.
Secrets are often encountered when you’re working with Docker containers. It can be challenging to handle container secrets correctly because Docker historically lacked a built-in secrets management system. In this article, you’ll learn how to use secrets securely when you’re building Docker images and starting containers.
What are Docker secrets?
Docker secrets provide a secure way to manage sensitive data your applications need to function, such as passwords, API keys, and certificates, without embedding them directly into your Docker images or exposing them in your container configurations. They’re specifically designed for use within a Docker Swarm cluster, although alternative solutions exist for standalone Docker environments.
Secrets are managed using the docker secret command and are only accessible to services explicitly granted access. Unlike traditional methods such as environment variables, Docker secrets are encrypted at rest and transmitted securely within the Swarm cluster.
Docker mounts secrets as files in the /run/secrets/ directory by default within the container.
Key features of Docker secrets
Here are some of the key features of Docker Secrets:
- Runtime-only access - By default, secrets are mounted only by the containers that need them in the /run/secrets path and automatically unmounted when the containers stop.
- Secret rotation - Secrets have unique identifiers, and they can be rotated without any downtime.
- Native integrations - Docker secrets integrate natively with Docker Compose and Docker Swarm.
- Encryption - Secrets are encrypted both at rest and in transit.
Why do you need secret management in Docker?
Safe secrets management is a crucial component of software supply chain security. In recent years, leaks of API keys and authentication tokens --- including ones discovered in Docker images --- have contributed to major data breaches. If a value is useful to an attacker, it needs to be treated as a secret.
Unfortunately, container workflows often lack support for secrets management. Developers tend to configure containers using environment variables, but these are visible in plaintext from outside the container:
$ docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=foobar mysql:8.0
96c995563df92a2d1341564cf635d284bc70fe1aa9ba4bba8da371f765243a35
The value of MYSQL_ROOT_PASSWORD
should be kept secret. If it's exposed, the recipient could log in to the MySQL database as root
. However, the use of an environment variable means the password can be retrieved easily using docker inspect
:
$ docker inspect mysql
[
{
"Config": {
"Env": [
"MYSQL_ROOT_PASSWORD=foobar"
]
}
}
]
Similarly, software within the container can see the environment variable and its value. This makes it easier for a malicious process to steal the value after the container is compromised:
$ docker exec -it mysql bash
bash-4.4# echo $MYSQL_ROOT_PASSWORD
foobar
Finally, secrets configured as environment variables also tend to be committed accidentally to your repositories. They tend to turn up in docker-compose.yml
, for example, either as forgotten test values or because developers fail to recognize the risks of hardcoded secrets.
version: "3"
services:
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=foobar
Let's explore how to avoid these risks and handle secrets securely with Docker.
You can also read more about Container Security Best Practices & Solutions.
Docker secrets vs environment variables
Docker Secrets store sensitive data like passwords securely and are encrypted, managed via Docker Swarm or Kubernetes, and accessed as files (e.g., in /run/secrets). Environment variables are plain text, suitable for non-sensitive configurations, accessible in the container's environment, but prone to leaks. In short, secrets are more suitable for sensitive data and environmental variables in general settings.
How to use secrets in Docker
Docker includes a secrets management solution, but it doesn't work with standalone containers. You can supply secrets to your containers when you're using either Docker Compose or Docker Swarm. There's no alternative for containers created manually with a plain docker run
command.
Let's explore how to use the two available methods to securely set secrets in containers.
How to use secrets with Docker Compose
Compose's secrets system is the most accessible for everyday use. It's also your only option if you want to manage Docker secrets without Swarm.
Secrets are defined in Compose files within the top-level secrets
field. Each named secret references a file in your working directory. When you run docker compose up
, Compose will automatically mount that file into the container.
Secrets are mounted to a predictable container path: /run/secrets/
. You should configure your containerized application to read the secret's value from that path.
Docker compose secrets example
The following example uses Compose to securely configure the root user's password for a MySQL container:
version: "3"
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
secrets:
- mysql_root_password
secrets:
mysql_root_password:
file: password.txt
Let's analyze what's happening in this file:
- The
secrets
section defines a single secret calledmysql_root_password
. - The secret's value is read from
password.txt
in your working directory. - The
mysql
service references the secret within its ownsecrets
field. - When the container starts, the contents of
password.txt
will be read and mounted to/run/secrets/mysql_root_password
(the name of the secret) inside the container. - The
MYSQL_ROOT_PASSWORD_FILE
environment variable instructs the official MySQL Docker image to read its password from the mounted file.
Testing Docker Compose secrets
To test this example, first create the password.txt
file in your working directory:
$ echo foobar > password.txt
You can now use Docker Compose to bring up your container:
$ docker compose up -d
Inspecting the /run/secrets
directory inside the container will confirm the secret's existence:
$ docker compose exec -it mysql bash
bash-4.4# ls /run/secrets
mysql_root_password
bash-4.4# cat /run/secrets/mysql_root_password
foobar
The value can't be accessed directly from outside the container. The output from docker inspect
will show that password.txt
is mounted to /run/secrets/mysql_root_password
, but its content won't be displayed:
[
{
"Mounts": [
{
"Type": "bind",
"Source": "/home/james/@scratch/the-complete-guide-to-docker-secrets/password.txt",
"Destination": "/run/secrets/mysql_root_password",
"Mode": "",
"RW": false,
"Propagation": "rprivate"
}
]
}
}
]
This output demonstrates how Compose implements its secrets management functionality. Secrets are injected into the container using a bind mount from the file in your working directory.