Docker secrets: A guide to secure container secrets management

Equipo de expertos de Wiz

What are Docker secrets and why do they matter?

A secret is any sensitive data your application needs at runtime, such as database passwords, API tokens, TLS certificates, and SSH keys. Many teams embed these values in Dockerfiles or pass them via environment variables, but both of these practices are risky: Environment variables appear in process listings and logs, and build arguments (ARG, ENV) become part of the final image. In other words, hard-coding credentials in code or images means anyone with access can retrieve them.

Docker secrets reduce exposure risk by avoiding image-layer persistence because they’re encrypted at rest, their access is restricted to only the container that needs them, and they’re mounted at runtime on a memory-backed filesystem (tmpfs) and removed when no longer needed. 

The big takeaway? Using secrets helps to prevent credentials from being embedded into image layers, where they could be extracted long after deployment. Yet secrets alone aren’t enough. A broader hardening strategy that also includes secret scanning and runtime monitoring is the only way to keep your sensitive information safe. 

Secrets Security Cheat Sheet: From Sprawl to Control

In this cheat sheet, you’ll learn how the best practices to discover, validate, and protect secrets across your SDLC

How Docker secrets work under the hood

In Swarm mode or Compose, an administrator creates a secret using docker secret create. The secret is sent to the swarm manager over mutual TLS (mTLS) and stored in an encrypted Raft log. Swarm managers replicate this log so that secrets remain available if a manager fails. 

The secret stays encrypted until a container that references it starts. At that moment, the manager decrypts the secret and mounts it inside the container’s memory at /run/secret/<example>. Only containers that declare the secret in their service definition can access it, and the file is unmounted and removed when the task stops. When rotating a secret, creating a new secret, and updating the service to reference it, Docker updates running services via rolling task replacement, allowing secrets to be rotated without application downtime when configured correctly.

There are some limitations to know about: 

  • Secrets are available only in Swarm mode and Compose. 

  • Each secret must be smaller than 500 KB. 

  • There is no built-in versioning – you manage versions manually. 

  • For standalone docker run or Kubernetes workloads, use an external secret manager. (Again, Docker secrets aren’t supported outside Swarm and Compose.) 

Security benefits of Docker secrets over traditional approaches

Encryption and isolation

Docker secrets are encrypted in transit and at rest in the Raft log, then mounted into a memory-backed filesystem and removed on exit. Environment variables, on the other hand, remain accessible to any user who can inspect the process or container.

Short lifespan

Docker secrets’ runtime-only access means that secrets exist in memory only during the container’s lifetime and aren’t embedded in image layers, unlike hard-coded secrets that persist in images and can be extracted later.

Centralized management and rotation

Docker secrets allow administrators to manage sensitive data centrally through the Swarm API, assign secrets to services, and update them without redeploying containers. This is far more efficient and secure than scattering secrets across scripts, environment variables, and .env files. 

Integration with CI/CD secret scanning 

Docker secrets reduce exposure risks by design, and combining secrets with scanning and runtime monitoring provides a layered defense.

With Docker BuildKit’s --secret mounts, credentials are temporarily mounted for a specific build step (for example, a single RUN instruction), making them available only during that step and preventing them from being written into the final image. Secret scanning tools, such as Wiz Code, inspect Dockerfiles, Git commits, and images to catch hard-coded tokens before they reach production. 

Implementing Docker secrets across different environments

Swarm and Docker Compose secrets

To use a secret in Docker Swarm, first create it: 

printf "mysecret" | docker secret create db_password -

Next, reference it when creating a swarm service: 

docker service create --name backend --secret db_password app:latest

After creating the secret, you can reference the same Docker secret in a docker-compose.yml file. Docker Compose doesn’t create the secret itself; instead, it references an existing Docker Swarm secret and attaches it to the required services: 

services:

  backend:

    image: app:latest

    secrets:

      - db_password

secrets:

  db_password:

    external: true

Compose can reference external secrets or mount secret files for development, but these approaches don’t provide the same security guarantees as Docker Swarm secrets. 

CI/CD pipelines and GitHub Actions

In CI/CD pipelines, you often need credentials to pull private dependencies or push images. BuildKit lets you inject credentials securely: When you run a build with this command, BuildKit mounts the secret file inside /run/secrets during the relevant RUN instruction and removes it afterwards: 

docker build --secret id=mysecret,src=/secretspath 

Remember: You should avoid using ARG or ENV to pass sensitive data, since those mechanisms embed values in the image layers.

On GitHub Actions, you can safely provide repository secrets to BuildKit using the docker/build-push-action and its secrets input. The workflow provides a registry password stored in your repository’s encrypted secrets to BuildKit, allowing the build to authenticate with a private registry or other protected services when pulling base images and, if enabled, pushing the final image.

jobs:

  build:

    steps:

      - uses: docker/setup-buildx-action@v3

      - uses: docker/build-push-action@v6

        with:

          secrets: |

            "docker_password=${{ secrets.DOCKER_PASSWORD }}"

During the build, BuildKit mounts docker_password into the container’s /run/secrets/docker_password only for build steps that explicitly request it and automatically removes it when the build completes. This approach prevents credentials from being baked into image layers while still enabling secure, automated builds.

Comparison with Kubernetes Secrets

While Docker Secrets offer encryption by default within the Swarm Raft log, Kubernetes Secrets are often misunderstood. By default, Kubernetes stores secrets in etcd as Base64-encoded strings, which provides no cryptographic protection – Base64 is encoding, not encryption, and is trivially reversible.

Modern Kubernetes environments have evolved to close this gap. Kubernetes supports encryption at rest for secrets stored in etcd via EncryptionConfiguration, but this must be explicitly enabled – it is not enabled by default. Self-managed clusters require manual setup, while managed Kubernetes services significantly simplify this process.

In managed environments such as Amazon EKS, Google GKE, and Azure AKS, teams can enable envelope encryption using cloud key management services (AWS KMS, Google Cloud KMS, or Azure Key Vault). In this model, a data encryption key (DEK) encrypts the secret, and a key encryption key (KEK), managed by the cloud provider and optionally backed by HSMs, encrypts the DEK.

For more advanced security requirements, teams often adopt the Secrets Store CSI Driver to mount secrets directly from external vaults – such as HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault – into pods as volumes. This approach avoids storing sensitive data in etcd altogether, reducing the attack surface.

Alternatively, the External Secrets Operator syncs secrets from external stores into native Kubernetes Secret objects, maintaining compatibility with existing tooling while centralizing secret management externally.

When migrating from Docker Swarm to Kubernetes, teams should create corresponding Secret objects and ensure encryption at rest is enabled and RBAC policies restrict secret access appropriately – neither is configured securely out of the box.

Docker secrets security best practices

  • Don’t embed secrets in images or code: Avoid hard-coding credentials in images and Git repositories and Dockerfiles. This includes API keys in scripts, credentials in configuration files, and secrets in docker-compose.yml files. A leak discovered after deployment is hard to fix because the secret persists in image layers.  You’ll also want to use secrets or environment variables loaded from external sources. This strategy keeps secrets out of image layers and version control, reducing the risk of long-lived credential exposure.

  • Prefer file-based mounts to plain environment variables: Even when secrets are managed correctly, exposing Docker secrets through environment variables can still lead to accidental leakage through logs or process inspection. Docker secret environment variables are often introduced for convenience, but they weaken isolation guarantees and increase the risk of unintended exposure.

  • Apply least privilege: Provide each service only the secrets it needs. In Compose, specify secrets on a per-service basis so they aren’t exposed to other containers. In Swarm, use the --secret flag to mount secrets only into tasks that need them. Drop unnecessary Linux capabilities and try to avoid running as root when possible.

  • Rotate secrets regularly and update services to use new versions: Regular rotation limits the impact of leaked credentials and ensures services pick up new secrets promptly.

  • Remember that standalone containers aren’t protected by Docker secrets: Docker secrets are limited to Swarm services and Compose stacks, so standalone containers require different approaches, such as external secret managers or build-time secret handling.

  • Automate scanning and monitoring: Wiz can detect secrets in code and images during builds. Setting up automated scans in your CI/CD pipeline prevents leaks from reaching production.

Looking for a practical checklist to put these principles into action? Download our Advanced Container Security Best Practices Cheat Sheet.

Monitoring and detecting secret exposure in containerized environments

Even when secrets are stored correctly, they can still be exposed at runtime through misconfigurations or compromised workloads. That’s why continuous runtime monitoring is a critical layer of defense in containerized environments. 

Runtime security tools based on eBPF monitor kernel events and generate real-time alerts when suspicious behavior occurs, allowing teams to detect behaviors that may indicate secret misuse in real time, rather than relying solely on post-incident analysis.

But real-time alerting isn’t enough. The secret sauce of effective detection is centralized visibility plus context. The best tools provide complete visibility across containers, hosts, and orchestration layers and correlate logs, metrics, and runtime events. With a full understanding of what’s happening as it happens, security teams can identify and stop anomalous access to secrets before there’s a breach.

How Wiz secures container secrets across the development lifecycle

Securing container secrets requires visibility and protection across every stage of the application lifecycle, from development to runtime. Wiz provides unified code-to-cloud visibility that helps teams identify exposed secrets early, understand their real-world impact, and remediate issues quickly across cloud-native environments. Here’s how:

  • Wiz Code scans Dockerfiles, IaC templates, and source code repositories to detect hard-coded secrets before they’re committed or deployed. By integrating with developer workflows, version control systems, and CI/CD pipelines, Wiz enables early detection of exposed credentials and reduces the risk of secrets reaching container images or production environments. All findings are validated to minimize false positives and linked back to the originating code and commit.

  • To extend visibility beyond development, Wiz uses agentless scanning to inspect container images and running containers without impacting performance. This allows teams to identify exposed secrets in images, environment variables, configuration files, and mounted volumes – including in cases where secrets are injected through insecure mechanisms that Docker secrets alone can’t protect against. The Wiz Security Graph correlates exposed secrets with context, such as network paths, vulnerabilities, identities, and permissions, to show what an attacker could realistically access if a secret is compromised. By analyzing environmental context rather than isolated findings, Wiz prioritizes truly exploitable risks, empowering teams to focus on the most impactful issues while reducing alert fatigue.

  • At runtime, Wiz Defend uses a lightweight eBPF sensor to monitor process activity and network connections. Suspicious behavior that may indicate secret misuse is detected in real time and enriched with contextual data from the Wiz Security Graph, enabling faster investigation and accurate prioritization. 

  • When a secret is exposed in production, Wiz traces it back to its origin in code, identifying the relevant commit and developer. This code-to-cloud traceability accelerates remediation and reduces mean time to resolution by helping teams address root causes rather than symptoms. 

Ready to see for yourself how Wiz delivers full-lifecycle secret detection and contextual risk analysis? Request a demo today.

FAQs about Docker secrets