Docker Compose Secrets: What Works, What Doesn't, and Secure Alternatives

Learn what Docker Compose “secrets” actually do today, when Swarm secrets are required, how to wire secret files safely, and which alternatives to use in production.

Docker Compose Secrets: What Works, What Doesn't, and Secure Alternatives

Docker Compose is a great way to define and run multi-container apps. But the moment you need database passwords, API keys, or private keys, the question becomes: how do you inject secrets without leaking them into Git, image layers, logs, or environment variables?

Important clarification up front:

  • “Docker secrets” (encrypted at rest/in transit) are a Swarm feature. You get the real security properties when running services in Docker Swarm.
  • Docker Compose (non‑Swarm) can still mount secret data as files, but it’s not the same security model as Swarm secrets. It’s primarily a convenient file mount mechanism and still depends on your host filesystem security.

This guide shows both approaches:

  1. true Docker secrets in Swarm + how to use them from a Compose file, and
  2. Compose-friendly “secret files” + best practices and safer alternatives for production.

Understanding “Secrets” in Docker: Compose vs Swarm

What are Docker Secrets (Swarm secrets)?

Docker secrets are a Swarm feature used to manage sensitive data securely (passwords, API keys, TLS keys, etc.).

When you run Swarm services, secrets are:

  • Encrypted at rest in the Swarm Raft log
  • Encrypted in transit between Swarm nodes
  • Exposed to containers as files (typically under /run/secrets/<name>)
  • Scoped to services that explicitly request them

This is the “real” Docker secrets security model.

What are “secrets” in Docker Compose (non‑Swarm)?

In plain Docker Compose (without Swarm), a secrets: entry is effectively a convenient way to mount a file into the container (similar to a bind mount, but with standardized path + permissions). It does not automatically give you Swarm’s encrypted secret store.

So:

  • Use Swarm secrets when you need strong security guarantees inside Docker itself.
  • Use Compose secret files for local dev / small self-hosted setups, and protect them like any sensitive file.

Benefits (and what they depend on)

If you use Swarm secrets:

  1. Strong security properties: encrypted at rest and in transit.
  2. Service-scoped access: only services that declare the secret can read it.
  3. Runtime injection: not baked into image layers.
  4. Rotation workflow: create a new secret, update service, remove old secret.

If you use Compose secret files (non‑Swarm):

  1. Keeps secrets out of images (still injected at runtime as a file).
  2. Cleaner app config: apps read /run/secrets/... rather than hardcoded values.
  3. Version-control friendly: the Compose file references secret files, and you keep those secret files out of Git.

Just don’t overstate it: Compose secret files are only as secure as your host and your operational practices.

Option A (Production-grade): Docker Swarm Secrets

Docker secrets require Swarm. If you’re not running Swarm, skip to Option B.

1) Initialize Swarm (one-time)

docker swarm init

2) Create a secret

Avoid echo "secret" > file when possible (shell history and tooling can leak). Prefer reading from stdin:

printf '%s' 'mysupersecretpassword' | docker secret create my_db_password -

Or from a file:

docker secret create my_db_password db_password.txt
chmod 400 db_password.txt

List secrets:

docker secret ls

Common commands

CommandDescription
docker secret createCreates a new secret
docker secret lsLists all secrets
docker secret inspectShows secret metadata (not value)
docker secret rm <secret>Removes a secret

Note: Docker does not let you “read the secret value back” via CLI. That’s by design.

Using Swarm secrets from a Compose file

If you deploy your stack to Swarm (for example via docker stack deploy), you can reference an external Swarm secret:

version: "3.8"

services:
  myapp:
    image: myapp:latest
    secrets:
      - my_db_password
    environment:
      DB_PASSWORD_FILE: /run/secrets/my_db_password

secrets:
  my_db_password:
    external: true

Accessing secrets in containers

Secrets are mounted as files under /run/secrets/<secret_name>:

with open('/run/secrets/my_db_password', 'r', encoding='utf-8') as f:
    db_password = f.read().strip()

This “read from file” pattern is recommended across languages because it avoids leaking secrets through process listings and environment dumps.

Option B (Compose-friendly): Secret files mounted into containers

If you’re running plain Docker Compose (no Swarm), you can still use the secrets: key to mount sensitive files into containers.

This is best for:

  • local development
  • single-host self-hosting
  • hobby/prototype deployments

Step 1: Create a local secrets directory (and ignore it in Git)

Create files like:

  • ./secrets/db_password.txt
  • ./secrets/api_key.txt

Make sure they are not committed to version control (add secrets/ to .gitignore).

Step 2: Define secrets in docker-compose.yml

services:
  database:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

Step 3: Access secrets in containers

cat /run/secrets/db_password

Note: this approach is still backed by your host filesystem. Protect the ./secrets/ directory with strict permissions and secure backups.

For a full reverse-proxy example, see: https://www.bitdoze.com/traefik-wildcard-certificate/

External vs file-based secrets (what “external” really means)

You’ll commonly see two patterns:

  • External secrets (external: true): this generally means “the secret already exists in the platform”.
    • In practice, this is most relevant for Swarm secrets.
  • File-based secrets (file: ...): this means “mount this local file into the container as /run/secrets/<name>”.
    • This is what many people use in plain Docker Compose.

External Secrets

External secrets are created and managed outside of the Docker Compose file, typically using the Docker CLI or a secret management system.

Characteristics:

  1. Created independently of the Docker Compose file
  2. Referenced in the Compose file using the external: true attribute
  3. Provide better separation of concerns and enhanced security
  4. Can be shared across multiple services and stacks

Important correction: Compose “secrets from environment variables”

In Docker Compose, secrets are typically sourced from files. Some older guides imply you can define secret values directly inside the Compose file or directly from environment variables—avoid that.

If you need to use environment variables, prefer:

  • .env for non-sensitive configuration
  • a proper secret manager for sensitive values (Vault / cloud secret manager)
  • or generate the secret file during deployment and mount it (never commit it)

You can learn more about general env usage here: https://www.bitdoze.com/docker-env-vars/

Key Differences

  1. Management: External secrets are managed outside the Compose file, while internal secrets are defined within it.
  2. Security: External secrets offer better security as they’re not visible in the Compose file.
  3. Reusability: External secrets can be easily shared across multiple services and stacks.
  4. Deployment: Internal secrets are easier to deploy in development environments but may require additional steps in production.
  5. Versioning: External secrets can be versioned independently of the application code.

When to Use Each Type

  • Use external secrets for:

    • Production environments
    • Sensitive data that needs to be shared across multiple services
    • When you need to manage secrets independently of your application code
  • Use internal secrets for:

    • Development and testing environments
    • Quick prototyping
    • When the secret is specific to a single service and doesn’t need to be shared

By understanding these differences, you can choose the most appropriate method for managing secrets in your Docker Compose projects, balancing security needs with ease of use and deployment considerations.

Best Practices (modern and practical)

When working with secrets, the core goal is always the same: avoid shipping secrets in images, Git, or logs, and minimize the blast radius if something leaks.

Naming conventions

  • Use descriptive names: Choose secret names that clearly indicate their purpose, e.g., prod_db_password instead of secret1.
  • Use consistent prefixes: For example, use prod_ for production secrets and dev_ for development secrets.
  • Avoid including sensitive information in names: The secret name itself shouldn’t reveal any confidential data.

Example:

secrets:
  prod_api_key:
    external: true
  dev_db_password:
    external: true

Version control considerations

  • Never commit secret values to Git.
  • Add your secrets directory to .gitignore (for example secrets/).
  • Consider keeping a secrets.example/ directory with placeholder files, or document required secret filenames in a README.
  • Be careful with .env files: treat them as secrets if they contain passwords/tokens, and don’t commit them.

Example .gitignore entry:

*.env
secrets/

Rotating secrets

  • Rotate secrets regularly to limit exposure.
  • In Swarm, rotation is typically:
    1. Create a new secret (versioned name).
    2. Update the service to add the new secret and switch the app to it.
    3. Remove the old secret after rollout.

Also consider rotating credentials at the source (DB user password, cloud key, etc.), not only inside Docker.

Example:

docker secret create db_password_v2 new_password.txt
docker service update --secret-rm db_password_v1 --secret-add db_password_v2 myservice
docker secret rm db_password_v1

Additional best practices

  • Least privilege: only mount the secrets a service needs.
  • Prefer file-based consumption (*_FILE patterns) over putting secrets in environment variables.
  • Don’t print secrets: avoid logging config objects that include secret paths/values.
  • Lock down your host: if using Compose secret files, secure filesystem permissions and backups.
  • Use a secret manager for production where possible (Vault or a cloud secret manager).

Example Use Cases

Docker secrets can be used in various scenarios to enhance the security of your containerized applications. Here are some common use cases:

Database credentials

Securely manage database passwords without hardcoding them in your application or Docker files.

version: '3.8'
services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

  app:
    image: myapp
    secrets:
      - db_password
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    external: true

API keys

Safely use API keys in your services without exposing them in your code or configuration files.

version: '3.8'
services:
  api_service:
    image: api_service
    secrets:
      - api_key
    environment:
      API_KEY_FILE: /run/secrets/api_key

secrets:
  api_key:
    external: true

SSL certificates

Manage SSL certificates securely for services that require HTTPS.

version: '3.8'
services:
  web:
    image: nginx
    secrets:
      - site_certificate
      - site_key
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

secrets:
  site_certificate:
    file: ./certs/site.crt
  site_key:
    file: ./certs/site.key

JWT signing keys

Securely manage keys used for signing JSON Web Tokens (JWTs) in authentication services.

version: '3.8'
services:
  auth_service:
    image: auth_service
    secrets:
      - jwt_private_key
      - jwt_public_key
    environment:
      JWT_PRIVATE_KEY_FILE: /run/secrets/jwt_private_key
      JWT_PUBLIC_KEY_FILE: /run/secrets/jwt_public_key

secrets:
  jwt_private_key:
    external: true
  jwt_public_key:
    external: true

By leveraging Docker secrets in these scenarios, you can significantly enhance the security of your containerized applications while maintaining flexibility and ease of management.

Limitations and Alternatives (updated)

Swarm mode requirement (for real Docker secrets)

True Docker secrets require Swarm. If you are using plain Docker Compose, you don’t get Swarm’s encrypted secret store—only file mounts.

Implications:

  • If you want Docker-managed secret encryption/scoping, you need Swarm (or another orchestrator).
  • For Kubernetes, use Kubernetes Secrets (ideally with encryption-at-rest and an external secret manager integration).

Other options for managing sensitive data

Given the limitations, here are better-aligned options depending on your environment:

  1. Secret files mounted via Compose

    • Pros: simple, keeps secrets out of image layers, works everywhere
    • Cons: security depends on host filesystem, backups, and ops discipline
  2. Environment variables (use with care)

    • Pros: simple and widely supported
    • Cons: easy to leak via process inspection, crash dumps, logs, or “show config” tooling

    Example (avoid for highly sensitive secrets if you can):

    services:
      app:
        image: myapp
        environment:
          DB_PASSWORD: mysecretpassword
  3. Cloud secret managers

    • AWS Secrets Manager / SSM Parameter Store, Google Secret Manager, Azure Key Vault
    • Pros: strong security posture, IAM-based access, rotation
    • Cons: platform coupling and potential cost
  4. HashiCorp Vault

    • Pros: strong, platform-agnostic, dynamic secrets, auditability
    • Cons: more operational complexity
  5. Kubernetes Secrets (+ external secret operators)

    • Pros: first-class in k8s ecosystems; integrates well with secret stores
    • Cons: requires k8s; base k8s secrets are not encrypted unless configured
  6. Config Management Tools:

    • Options: Ansible, Puppet, Chef
    • Pros: Powerful, flexible, can manage configurations across multiple servers
    • Cons: Require additional setup and knowledge
  7. Cloud Provider Secret Management Services:

    • Options: AWS Secrets Manager, Google Cloud Secret Manager, Azure Key Vault
    • Pros: Highly secure, integrated with cloud ecosystems
    • Cons: Vendor lock-in, potential cost implications
  8. HashiCorp Vault:

    • Pros: Highly secure, platform-agnostic, supports dynamic secrets
    • Cons: Complex setup, requires additional infrastructure

    Example integration:

    services:
      app:
        image: myapp
        environment:
          - VAULT_ADDR=http://vault:8200
        entrypoint: ["vault-agent", "-config=/vault-agent-config.hcl"]
  9. Docker Config:

    • Similar to Docker secrets but for non-sensitive configuration data
    • Can be used in conjunction with secrets for a comprehensive configuration management strategy

When choosing an alternative, consider factors such as your specific security requirements, existing infrastructure, team expertise, and scalability needs.

Conclusion

Docker Compose can help you wire secrets into containers as files, but the strong “Docker secrets” security properties come from Swarm.

Use this decision rule:

  • If you want Docker-managed encryption + service scoping: use Swarm secrets.
  • If you’re on plain Compose: use secret files, lock them down, and consider a real secret manager for production deployments.

Either way, prefer applications reading secrets from /run/secrets/... and keep secret values out of images, Git, and logs.