Docker Compose secrets provide a secure way to pass sensitive data (passwords, API keys, certificates) to containers without embedding them in images, environment variables, or Compose files. Secrets are mounted as files inside containers and are never exposed in logs or inspect output. This guide covers secrets management for both Swarm mode and standalone Compose.
Secrets in Docker Compose (Standalone)
# docker-compose.yml
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
app:
build: .
secrets:
- db_password
- api_key
- source: tls_cert
target: /etc/ssl/app/cert.pem
mode: 0444
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
tls_cert:
file: ./secrets/tls_cert.pem
Reading Secrets in Applications
# Secrets are mounted at /run/secrets/
# Bash
DB_PASSWORD=$(cat /run/secrets/db_password)
# Python
with open('/run/secrets/db_password') as f:
db_password = f.read().strip()
# Node.js
const fs = require('fs');
const dbPassword = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();
# PHP
$dbPassword = trim(file_get_contents('/run/secrets/db_password'));
# Many official images support _FILE suffix for environment variables
# PostgreSQL, MySQL, MariaDB all support this pattern:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
# Instead of: POSTGRES_PASSWORD: plaintext_password
Docker Swarm Secrets
# Create secrets in Swarm
echo "MyDBPassword" | docker secret create db_password -
docker secret create tls_key /path/to/key.pem
# List secrets
docker secret ls
# Use in stack
secrets:
db_password:
external: true # References existing Swarm secret
# Rotate a secret
echo "NewPassword" | docker secret create db_password_v2 -
docker service update --secret-rm db_password --secret-add db_password_v2 myservice
Environment Variables vs Secrets
# BAD: Sensitive data in environment variables
services:
app:
environment:
DB_PASSWORD: "plaintext_visible_in_inspect_and_logs"
# Visible in: docker inspect, docker compose config, process listing
# GOOD: Sensitive data as secrets
services:
app:
secrets:
- db_password
# Only accessible via /run/secrets/db_password inside container
# Not visible in inspect, logs, or process listing
Secret File Management
# Project structure
project/
├── docker-compose.yml
├── secrets/
│ ├── .gitignore # Contains: *
│ ├── db_password.txt
│ ├── api_key.txt
│ └── tls_cert.pem
└── ...
# secrets/.gitignore
*
!.gitignore
# Never commit secrets to version control
# Use .env files for non-sensitive configuration only
Advanced: HashiCorp Vault Integration
# Use Vault to populate secrets at deployment time
#!/bin/bash
# deploy.sh
vault kv get -field=password secret/myapp/db > secrets/db_password.txt
vault kv get -field=key secret/myapp/api > secrets/api_key.txt
docker compose up -d
# Clean up plaintext files after containers read them
rm -f secrets/db_password.txt secrets/api_key.txt
Best Practices
- Never store secrets in environment variables, Dockerfiles, or image layers
- Use the
_FILEsuffix pattern supported by official database images - Add
secrets/to.gitignoreto prevent accidental commits - Set restrictive file permissions on secret files (
mode: 0400) - In Swarm mode, use external secrets for production deployments
- Rotate secrets regularly and update services to use new versions
- Use a secrets manager (Vault, AWS Secrets Manager) for enterprise deployments