01Why Environment Variables Are Risky
Environment variables are the default way to pass configuration to containers, but they have security issues:
**Problems:**
• Visible in docker inspect output
• Logged in many debugging scenarios
• Inherited by child processes
• Often accidentally committed to version control
• Visible in process listings on some systems
**Better approach:**
Mount secrets as files. Many applications support reading sensitive values from files using the _FILE suffix convention.
Anyone with access to docker inspect or the Docker socket can read all environment variables from running containers.
02The _FILE Suffix Pattern
Many official images support a _FILE suffix variant of their environment variables. Instead of passing the secret directly, you pass a path to a file containing the secret.
1services: 2 # Traditional (insecure) approach3 database-insecure: 4 image: postgres:165 environment: 6 - POSTGRES_PASSWORD=supersecretpassword # Visible everywhere!78 # Secure approach using _FILE suffix9 database-secure: 10 image: postgres:1611 environment: 12 - POSTGRES_PASSWORD_FILE=/run/secrets/db_password13 volumes: 14 - ./secrets/db_password:/run/secrets/db_password:ro1516# Create the secret file17# echo "supersecretpassword" > ./secrets/db_password18# chmod 600 ./secrets/db_passwordCheck the image documentation for _FILE suffix support. Most official database images support it.
03Using Docker Compose Secrets
Docker Compose has built-in secrets support that works without Swarm mode. Secrets are mounted as files in /run/secrets/.
1services: 2 database: 3 image: postgres:164 environment: 5 - POSTGRES_PASSWORD_FILE=/run/secrets/db_password6 secrets: 7 - db_password89 app: 10 image: myapp:latest11 environment: 12 - DATABASE_PASSWORD_FILE=/run/secrets/db_password13 - API_KEY_FILE=/run/secrets/api_key14 secrets: 15 - db_password16 - api_key1718secrets: 19 db_password: 20 file: ./secrets/db_password.txt21 api_key: 22 file: ./secrets/api_key.txt2324# Secrets are mounted at /run/secrets/<secret_name>25# They're read-only with restricted permissions04Creating and Managing Secret Files
Set up your secrets directory with proper permissions and git-ignore the files.
1# Create secrets directory2mkdir -p secrets3chmod 700 secrets45# Create secret files6echo "your-db-password" > secrets/db_password.txt7echo "your-api-key" > secrets/api_key.txt89# Set restrictive permissions10chmod 600 secrets/*1112# Add to .gitignore13echo "secrets/" >> .gitignore1415# Verify files aren't tracked16git status1718# For production, consider using:19# - Password managers with CLI (1Password, Bitwarden)20# - Cloud secret managers (AWS Secrets Manager, etc.)21# - Ansible vault, SOPS, etc.Never commit secret files to version control. Use .gitignore and verify with git status before committing.
05Reading Secrets in Your Application
If your image doesn't support _FILE suffix, you can read secrets in your application code or entrypoint script.
1# Custom entrypoint script (entrypoint.sh)2#!/bin/sh34# Read secrets from files into environment variables5if [ -f /run/secrets/db_password ]; then6 export DATABASE_PASSWORD=$(cat /run/secrets/db_password)7fi89if [ -f /run/secrets/api_key ]; then10 export API_KEY=$(cat /run/secrets/api_key)11fi1213# Run the actual command14exec "$@"06Using Secrets in Compose File
Here's a complete example using the entrypoint approach.
1# docker-compose.yml2services: 3 app: 4 build: .5 entrypoint: ["/app/entrypoint.sh"]6 command: ["python", "app.py"]7 secrets: 8 - db_password9 - api_key1011secrets: 12 db_password: 13 file: ./secrets/db_password.txt14 api_key: 15 file: ./secrets/api_key.txt1617---18# Dockerfile19FROM python: 3.12-slim20WORKDIR /app21COPY requirements.txt .22RUN pip install -r requirements.txt23COPY . .24RUN chmod +x entrypoint.sh25# Don't set entrypoint here - let compose override itThe entrypoint pattern works with any image, even if it doesn't support _FILE suffix natively.
07Using .env Files (Less Secure Alternative)
The .env file approach is better than hardcoding but less secure than proper secrets. Use it for development only.
1# docker-compose.yml2services: 3 database: 4 image: postgres:165 environment: 6 - POSTGRES_PASSWORD=${DB_PASSWORD}78 app: 9 image: myapp:latest10 environment: 11 - DATABASE_URL=postgres://user:${DB_PASSWORD}@database:5432/mydb12 - API_KEY=${API_KEY}1314# .env file (git-ignored!)15# DB_PASSWORD=your-password-here16# API_KEY=your-api-key-here.env files are still environment variables under the hood. They're visible in docker inspect and can leak. Use proper secrets for production.