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.
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.
| 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 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
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
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.
# 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
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 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
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 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
- 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.
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.