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

Mar 21, 2025 - 13:05
 0
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:

  1. 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.
  2. Secret rotation - Secrets have unique identifiers, and they can be rotated without any downtime.
  3. Native integrations - Docker secrets integrate natively with Docker Compose and Docker Swarm.
  4. 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 called mysql_root_password.
  • The secret's value is read from password.txt in your working directory.
  • The mysql service references the secret within its own secrets 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.