Docker Secrets Management

Secrets management is critical for secure containerized applications. This guide covers Docker Swarm secrets, BuildKit secrets for build-time secrets, environment variable security, and best practices for handling sensitive data.

Swarm Secrets BuildKit Secrets Environment Variables Best Practices
Why Secrets Management Matters

Secrets are sensitive data such as passwords, API keys, TLS certificates, and database credentials. Hardcoding secrets in images, Dockerfiles, or environment variables is a major security risk. Secrets can be exposed through image layers, container inspect commands, logs, or version control.

Docker provides multiple ways to manage secrets securely: Swarm secrets for runtime secrets in production, BuildKit secrets for build-time secrets, and environment variables (with caution). Proper secrets management prevents credential leakage and reduces the impact of container compromises.

Never hardcode secrets in Dockerfiles, images, or environment variables that get committed to version control. Secrets in images persist in layers and can be extracted by anyone with access to the image.
Secret Management Methods Comparison
Method Use Case Security Persistence
Swarm Secrets Production runtime secrets High (encrypted at rest and in transit) Yes (mounted as files)
BuildKit Secrets Build-time secrets (no layer storage) High (not stored in image layers) No (ephemeral)
Environment Variables Development, simple configs Low (visible in inspect, logs) Yes
Docker Compose secrets Development, Swarm-style Medium (Swarm mode only) Yes
Docker Swarm Secrets: Production-Ready Secret Management

Docker Swarm secrets provide secure storage and delivery of sensitive data to services. Secrets are encrypted during transit and at rest, stored in the Swarm's Raft log, and only accessible to services that have been granted access. Secrets are mounted as files in /run/secrets/ inside containers.

# Create secrets from different sources echo "mysecretpassword" | docker secret create db_password - printf "admin123" | docker secret create api_key - docker secret create db_password ./password.txt # List secrets docker secret ls # Inspect secret (only metadata, value hidden) docker secret inspect db_password # Use secret in a service docker service create --name db \ --secret db_password \ -e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \ postgres # Multiple secrets docker service create --name app \ --secret db_password \ --secret api_key \ --secret tls_cert \ myapp # Docker Compose with secrets (Swarm mode) # docker-compose.yml version: '3.8' services: db: image: postgres secrets: - db_password environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password secrets: db_password: external: true # Deploy to Swarm docker stack deploy -c docker-compose.yml myapp # Remove secret docker secret rm db_password
Swarm secrets are only available in Swarm mode. For single-container deployments, consider using external secret managers or bind-mounted secret files with restricted permissions.
BuildKit Secrets: Build-Time Secrets Without Layer Storage

BuildKit secrets allow you to use secrets during image building without storing them in the final image layers. This is perfect for downloading dependencies from private repositories or using npm tokens. Secrets are never persisted in the image and are only available during the RUN command where they're mounted.

# Dockerfile with BuildKit secret # syntax=docker/dockerfile:1.4 FROM node:18-alpine WORKDIR /app # Secret is only available during this RUN command RUN --mount=type=secret,id=npm_token \ NPM_TOKEN=$(cat /run/secrets/npm_token) \ && npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN \ && npm install COPY . . # Build with BuildKit secret DOCKER_BUILDKIT=1 docker build --secret id=npm_token,src=./npm_token.txt -t myapp . # Or using docker-compose # docker-compose.yml services: app: build: context: . secrets: - npm_token secrets: npm_token: file: ./npm_token.txt # Build with compose DOCKER_BUILDKIT=1 docker compose build # Using SSH agent forwarding for Git RUN --mount=type=ssh git clone git@github.com:myorg/private-repo.git
BuildKit secrets are the only secure way to use secrets during image building. They never appear in image history and cannot be extracted from the final image.
Environment Variables: Convenient but Risky

Environment variables are the simplest way to pass configuration, but they are not secure for secrets. Environment variables are visible in docker inspect, docker exec env, logs, and can be leaked through debugging tools. Only use environment variables for non-sensitive configuration, never for passwords or API keys in production.

Environment variables are NOT secure for secrets. Anyone with access to docker inspect, docker exec, or container logs can see them. Use Swarm secrets or external secret managers instead.
# NOT SECURE - Environment variables expose secrets docker run -e DB_PASSWORD=secretpassword postgres # Slightly better - env file (still visible in inspect) docker run --env-file .env postgres # Check how secrets are exposed docker inspect container_name | grep -A 5 Env # For development only - use .env files echo "DB_PASSWORD=devpassword" > .env docker run --env-file .env postgres # Production: Use Swarm secrets or external secret manager # NEVER commit .env files with real secrets to git
External Secret Managers: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault

For enterprise environments, external secret managers provide the highest level of security with features like secret rotation, audit logging, and fine-grained access control. Integrate them with Docker containers using sidecar containers or API calls.

# HashiCorp Vault example # Start Vault dev server docker run -d --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=root' vault # Read secret from Vault docker run --rm -e VAULT_TOKEN=root vault vault kv get secret/database # Using vault-agent sidecar (recommended) # vault-agent retrieves secrets and writes to shared volume # Application reads from /secrets/database # AWS Secrets Manager aws secretsmanager get-secret-value --secret-id mysecret # Azure Key Vault az keyvault secret show --vault-name myvault --name mysecret # Google Secret Manager gcloud secrets versions access latest --secret=mysecret
Docker Compose Secrets (Development/Testing)

Docker Compose supports secrets in Swarm mode, but for local development, you can use bind-mounted secret files. This provides a consistent development experience while maintaining security best practices.

# docker-compose.yml (development) version: '3.8' services: app: image: myapp secrets: - db_password environment: DB_PASSWORD_FILE: /run/secrets/db_password # For local development without Swarm volumes: - ./secrets/db_password.txt:/run/secrets/db_password:ro secrets: db_password: file: ./secrets/db_password.txt # .gitignore secrets echo "secrets/" >> .gitignore # Create secret file (not committed) mkdir secrets echo "devpassword123" > secrets/db_password.txt chmod 600 secrets/db_password.txt # Run with Compose docker compose up
Reading Secrets in Your Application Code

Regardless of how you provide secrets to containers, your application needs to read them. The standard pattern is to read secret files from /run/secrets/ (for Swarm secrets) or from environment variables (fallback).

# Python example import os def get_secret(secret_name): # Try file-based secret first (Swarm style) file_path = f'/run/secrets/{secret_name}' if os.path.exists(file_path): with open(file_path, 'r') as f: return f.read().strip() # Fallback to environment variable (dev) return os.environ.get(secret_name.upper()) db_password = get_secret('db_password') # Node.js example const fs = require('fs'); const path = '/run/secrets/db_password'; let dbPassword; if (fs.existsSync(path)) { dbPassword = fs.readFileSync(path, 'utf8').trim(); } else { dbPassword = process.env.DB_PASSWORD; } # Bash script #!/bin/bash if [ -f /run/secrets/db_password ]; then DB_PASSWORD=$(cat /run/secrets/db_password) else DB_PASSWORD=$DB_PASSWORD fi
Secret Rotation: Updating Secrets Without Downtime

Secret rotation is critical for security. For Swarm secrets, you can update secrets by creating a new secret version and updating services. For external secret managers, applications can poll for updates.

# Swarm secret rotation # Create new secret version echo "newpassword" | docker secret create db_password_v2 - # Update service to use new secret docker service update --secret-rm db_password --secret-add db_password_v2 db # Remove old secret docker secret rm db_password # For applications that need to reload secrets without restart # Implement signal handling (SIGHUP) or periodic polling
Secrets Management Best Practices
  • Never hardcode secrets - Never put secrets in Dockerfiles, images, or version control.
  • Use Swarm secrets for production - They provide encryption and access control.
  • Use BuildKit secrets for build-time secrets - Prevents secrets from leaking into image layers.
  • Don't use environment variables for secrets - They're visible in inspect and logs.
  • Limit secret permissions - Use chmod 600 for secret files.
  • Rotate secrets regularly - Automate secret rotation where possible.
  • Use external secret managers for enterprise - Vault, AWS Secrets Manager, Azure Key Vault.
  • Audit secret access - Monitor who accesses which secrets.
  • Use .gitignore for secret files - Prevent accidental commits.
  • Implement secret reloading - Allow applications to reload secrets without restart.
Frequently Asked Questions
What's the difference between secrets and environment variables?
Secrets are encrypted, stored in Swarm's Raft log, mounted as files, and not visible in docker inspect. Environment variables are plain text, visible in inspect, and can be exposed in logs. Always use secrets for sensitive data in production.
Can I use Swarm secrets without Swarm mode?
No, Swarm secrets require Swarm mode. For single-container deployments, use external secret managers or bind-mounted secret files with restricted permissions.
Are BuildKit secrets safe for production builds?
Yes! BuildKit secrets are never stored in image layers and are only available during the specific RUN command where they're mounted. They're the secure way to use secrets during image building.
How do I view a secret's value after creation?
You cannot. Swarm secrets are designed to be unreadable after creation (security feature). To rotate a secret, create a new one and update the service.
Can I use Docker Compose secrets without Swarm?
Docker Compose secrets (the secrets key) only work in Swarm mode. For local development, use bind-mounted secret files or environment variables (development only).
How do I pass secrets to Docker build without BuildKit?
You can't securely. Without BuildKit, any secret used in a build will be stored in image layers. Enable BuildKit with DOCKER_BUILDKIT=1 and use --mount=type=secret.
What's the best secret management solution for Kubernetes?
Kubernetes has built-in Secrets (base64 encoded, not encrypted by default). For production, use external secret managers like HashiCorp Vault with the CSI driver, or cloud-specific solutions (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager).
Can I store secrets in .env files safely?
For development only. Never commit .env files with real secrets to version control. Use .env.example files with placeholder values instead. Add .env to .gitignore.
Previous: Container Image Scanning Next: Rootless Docker

Proper secrets management is essential for container security. Use Swarm secrets for production, BuildKit secrets for builds, and never rely on environment variables for sensitive data.