Umgebungsvariablen sind der primaere Mechanismus zur Konfiguration von Containern in Docker Compose. Es gibt jedoch 4 verschiedene Wege, sie zu uebergeben, jeder mit unterschiedlichem Verhalten, Sicherheitsimplikationen und Fallstricken. Dieser Leitfaden behandelt alle Ansaetze.
4 Wege zur Uebergabe von Umgebungsvariablen in Docker Compose
Docker Compose unterstuetzt vier verschiedene Methoden zur Injektion von Umgebungsvariablen in Container.
1. Inline mit der environment-Direktive
Schluessel-Wert-Paare direkt in docker-compose.yml definieren. Am besten fuer nicht-sensible, service-spezifische Konfiguration.
services:
api:
image: node:20-alpine
environment:
NODE_ENV: production
DB_HOST: postgres
DB_PORT: "5432"
LOG_LEVEL: info
# List syntax (also valid)
worker:
image: node:20-alpine
environment:
- NODE_ENV=production
- QUEUE_NAME=tasks
- CONCURRENCY=42. Externe Datei mit env_file
Variablen aus externen Dateien laden. Am besten fuer die Verwaltung zusammengehoeriger Variablen.
services:
api:
image: node:20-alpine
env_file:
- .env # Shared variables
- .env.api # Service-specific variables
- .env.local # Local overrides (gitignored)
db:
image: postgres:16
env_file:
- .env
- .env.db3. Shell-Umgebungsvariablen-Durchreichung
Host-Shell-Variablen an den Container weiterleiten. Am besten fuer CI/CD-Pipelines.
# In your shell:
export API_KEY=sk-abc123
export DEBUG=true
# docker-compose.yml
services:
api:
image: node:20-alpine
environment:
- API_KEY # Passes host $API_KEY into container
- DEBUG # Passes host $DEBUG into container
- CI # Passes host $CI (empty string if unset)4. Variablensubstitution in docker-compose.yml
Host-Umgebungsvariablen direkt in Compose-Dateiwerten interpolieren.
# .env file
POSTGRES_VERSION=16
APP_PORT=3000
REPLICAS=3
# docker-compose.yml
services:
db:
image: postgres:${POSTGRES_VERSION} # Resolved from .env or shell
app:
ports:
- "${APP_PORT}:3000" # Resolved from .env or shell
deploy:
replicas: ${REPLICAS} # Resolved from .env or shellVergleichstabelle
| Methode | Am besten fuer | In VCS? | Secret-sicher? |
|---|---|---|---|
| environment | Non-sensitive defaults | Yes | No |
| env_file | Grouped config, secrets | No (.gitignore) | Partial |
| Shell passthrough | CI/CD, dynamic values | N/A | Partial |
| ${} substitution | Compose file templating | Yes (file), No (.env) | No |
.env-Datei: Syntax, Platzierung und Interpolation
Docker Compose liest automatisch eine .env-Datei im selben Verzeichnis wie docker-compose.yml.
Syntaxregeln
# Comments start with #
# Blank lines are ignored
# Simple key=value (no spaces around =)
DB_HOST=localhost
DB_PORT=5432
# Values with spaces need quotes
APP_NAME="My Docker App"
GREETING='Hello World'
# Multi-line values (double quotes only)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
# Variable expansion (Compose v2.20+)
BASE_URL=https://api.example.com
HEALTH_URL=${BASE_URL}/health
# Export prefix is ignored (compatible with shell source)
export SECRET_KEY=mysecretkey
# Empty values
EMPTY_VAR=
ALSO_EMPTY=''Dateiplatzierung und Prioritaet
Compose sucht .env im Projektverzeichnis. Mit --env-file ueberschreibbar:
# Default: reads .env from project directory
docker compose up
# Override with --env-file
docker compose --env-file .env.staging up
# Multiple --env-file flags (Compose v2.24+)
docker compose --env-file .env --env-file .env.local up
# Project directory structure:
my-project/
docker-compose.yml # Compose file
.env # Auto-loaded by Compose
.env.staging # Loaded with --env-file
.env.production # Loaded with --env-fileInterpolation in docker-compose.yml
Variablen aus .env stehen fuer die Substitution in docker-compose.yml zur Verfuegung, werden aber NICHT automatisch in Container injiziert:
# .env
TAG=3.2.1
EXTERNAL_PORT=8080
# docker-compose.yml
services:
web:
image: myapp:${TAG} # Uses TAG from .env -> myapp:3.2.1
ports:
- "${EXTERNAL_PORT}:80" # Uses EXTERNAL_PORT from .env -> 8080:80
environment:
- TAG # WRONG! This passes host $TAG, not .env TAG
- TAG=${TAG} # RIGHT! Explicitly interpolate .env value
# Key insight:
# .env -> docker-compose.yml interpolation (automatic)
# .env -> container environment (NOT automatic, use env_file)env_file vs environment: Unterschiede und Fallstricke
Diese beiden Direktiven sehen aehnlich aus, verhalten sich aber unterschiedlich.
| Direktive | Verhalten |
|---|---|
| environment: | Inlined in docker-compose.yml. Visible in version control. Supports variable substitution. Overrides env_file. |
| env_file: | Loaded from external file(s). Can be gitignored. No variable substitution inside the file. Lower priority than environment. |
| .env (auto) | Only for Compose file interpolation (${} syntax). NOT injected into containers. Lowest priority. |
Kritische Fallstricke
- 1. Prioritaet: environment-Werte ueberschreiben env_file-Werte fuer denselben Schluessel.
- 2. Die .env-Datei (automatisch geladen) dient nur der Compose-Datei-Interpolation. Sie injiziert KEINE Variablen in Container.
- 3. env_file-Pfade sind relativ zur docker-compose.yml-Datei, nicht zum aktuellen Arbeitsverzeichnis.
- 4. Wenn eine env_file fehlt, schlaegt Compose fehl. Verwenden Sie required: false (Compose v2.24+).
# Precedence demonstration:
# .env.shared
DB_HOST=shared-db
DB_PORT=5432
# docker-compose.yml
services:
api:
env_file:
- .env.shared # DB_HOST=shared-db, DB_PORT=5432
environment:
DB_HOST: override-db # Overrides env_file! DB_HOST=override-db
# DB_PORT not listed here # Keeps env_file value: DB_PORT=5432
# Result inside container:
# DB_HOST=override-db (from environment, overrides env_file)
# DB_PORT=5432 (from env_file)
# Optional env_file (Compose v2.24+):
services:
api:
env_file:
- path: .env.local
required: false # Won't fail if file doesn't existDocker Secrets: Dateibasierte Secrets fuer die Produktion
Docker Secrets bieten eine sicherere Alternative zu Umgebungsvariablen fuer sensible Daten.
Schritt 1: Secret-Dateien erstellen
# Create secret files (don't commit these!)
echo "SuperSecretPassword123" > db_password.txt
echo "sk-prod-abc123xyz789" > api_key.txt
# Add to .gitignore
echo "*.txt" >> .gitignore
echo "secrets/" >> .gitignore
# Alternative: use a secrets directory
mkdir secrets
echo "SuperSecretPassword123" > secrets/db_password
echo "sk-prod-abc123xyz789" > secrets/api_keySchritt 2: Secrets in docker-compose.yml definieren
# docker-compose.yml
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password # Note: _FILE suffix
secrets:
- db_password # Grant access to this secret
api:
image: myapp:latest
secrets:
- db_password
- api_key
# Secrets available at:
# /run/secrets/db_password
# /run/secrets/api_key
# Top-level secrets definition
secrets:
db_password:
file: ./secrets/db_password # From local file
api_key:
file: ./secrets/api_key # From local file
# Alternative: use environment variable as source
# jwt_secret:
# environment: JWT_SECRET_VALUE # From host env var (Compose v2.23+)Schritt 3: Secrets in der Anwendung lesen
# Inside the container, secrets are plain text files:
$ docker compose exec api cat /run/secrets/db_password
SuperSecretPassword123
$ docker compose exec api cat /run/secrets/api_key
sk-prod-abc123xyz789
$ docker compose exec api ls -la /run/secrets/
total 8
-r--r--r-- 1 root root 24 Jan 1 00:00 db_password
-r--r--r-- 1 root root 22 Jan 1 00:00 api_keySchritt 4: Secrets im Code verarbeiten
// Node.js - Read secret from file
const fs = require('fs');
function getSecret(name) {
const secretPath = `/run/secrets/${name}`;
try {
return fs.readFileSync(secretPath, 'utf8').trim();
} catch (err) {
// Fallback to environment variable (for development)
return process.env[name.toUpperCase()];
}
}
const dbPassword = getSecret('db_password');
const apiKey = getSecret('api_key');# Python - Read secret from file
import os
def get_secret(name: str) -> str:
secret_path = f"/run/secrets/{name}"
try:
with open(secret_path) as f:
return f.read().strip()
except FileNotFoundError:
# Fallback to environment variable
return os.environ.get(name.upper(), "")
db_password = get_secret("db_password")
api_key = get_secret("api_key")Warum Secrets statt Umgebungsvariablen?
- Umgebungsvariablen koennen ueber docker inspect, /proc/environ und Fehlerprotokolle lecken
- Secrets sind dateibasiert und nur in /run/secrets/ zugaenglich
- Secrets unterstuetzen granulare Zugriffskontrolle pro Service
- Viele Datenbanken unterstuetzen nativ den _FILE-Suffix fuer Secret-Dateien
Variablensubstitution: ${VAR:-default}-Syntax
Docker Compose unterstuetzt Shell-aehnliche Variablensubstitution mit Standardwerten.
| Syntax | Ergebnis |
|---|---|
| ${VAR} | Value of VAR. Error if unset. |
| ${VAR:-default} | Value of VAR if set, otherwise "default". |
| ${VAR-default} | Value of VAR if set (even if empty), otherwise "default". |
| ${VAR:?error msg} | Value of VAR if set, otherwise exit with "error msg". |
| ${VAR?error msg} | Value of VAR if set (even if empty), otherwise exit with "error msg". |
| ${VAR:+replacement} | "replacement" if VAR is set and non-empty, otherwise empty. |
| ${VAR+replacement} | "replacement" if VAR is set (even if empty), otherwise empty. |
Praktisches Beispiel
# docker-compose.yml with variable substitution
services:
web:
image: nginx:${NGINX_VERSION:-1.25-alpine} # Default: 1.25-alpine
ports:
- "${WEB_PORT:-80}:80" # Default: 80
- "${SSL_PORT:-443}:443" # Default: 443
volumes:
- ${CONFIG_PATH:-./nginx.conf}:/etc/nginx/nginx.conf:ro
api:
image: ${REGISTRY:-docker.io}/myapp:${TAG:?TAG is required}
# ^ Fails if TAG is not set
environment:
NODE_ENV: ${NODE_ENV:-development}
LOG_LEVEL: ${LOG_LEVEL:-info}
DATABASE_URL: postgres://${DB_USER:-postgres}:${DB_PASS:?DB_PASS required}@db:5432/${DB_NAME:-myapp}
db:
image: postgres:${PG_VERSION:-16}
volumes:
- ${DATA_DIR:-./data}/postgres:/var/lib/postgresql/dataMulti-Umgebungs-Setup: Dev/Staging/Production-Muster
Reale Projekte benoetigen unterschiedliche Konfigurationen. Hier sind bewaehrte Muster.
Muster 1: Override-Dateien (empfohlen)
Verwenden Sie eine Basis-docker-compose.yml mit umgebungsspezifischen Override-Dateien.
# File structure:
project/
docker-compose.yml # Base configuration
docker-compose.override.yml # Dev overrides (auto-loaded!)
docker-compose.staging.yml # Staging overrides
docker-compose.prod.yml # Production overrides# docker-compose.yml (base)
services:
api:
image: myapp:${TAG:-latest}
restart: unless-stopped
depends_on:
- db
- redis
db:
image: postgres:16
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pg-data:# docker-compose.override.yml (development - auto-loaded)
services:
api:
build: .
volumes:
- .:/app # Hot reload
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229" # Debugger
environment:
NODE_ENV: development
LOG_LEVEL: debug
DB_HOST: db
db:
ports:
- "5432:5432" # Expose DB for local tools
environment:
POSTGRES_PASSWORD: devpassword # OK for dev only# docker-compose.prod.yml (production)
services:
api:
# No build, no volumes, no debug port
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: warn
env_file:
- .env.production
secrets:
- db_password
- api_key
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
db:
# No exposed ports
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
secrets:
db_password:
file: ./secrets/db_password
api_key:
file: ./secrets/api_key# Usage:
# Development (auto-loads docker-compose.override.yml)
docker compose up
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Validate merged config before deploying
docker compose -f docker-compose.yml -f docker-compose.prod.yml configMuster 2: Umgebungsspezifische .env-Dateien
Verwenden Sie --env-file zum Laden verschiedener Variablensets pro Umgebung.
# .env.development
TAG=latest
APP_PORT=3000
DB_PASSWORD=devpassword
LOG_LEVEL=debug
REPLICAS=1
# .env.staging
TAG=rc-1.2.3
APP_PORT=3000
DB_PASSWORD=staging-secret-pw
LOG_LEVEL=info
REPLICAS=2
# .env.production
TAG=1.2.3
APP_PORT=3000
DB_PASSWORD=prod-ultra-secret-pw
LOG_LEVEL=warn
REPLICAS=3
# Usage:
docker compose --env-file .env.development up # Dev
docker compose --env-file .env.staging up -d # Staging
docker compose --env-file .env.production up -d # ProductionMuster 3: Profiles fuer optionale Services
Verwenden Sie Compose-Profiles zum Ein- oder Ausschliessen von Services pro Umgebung.
# docker-compose.yml
services:
api:
image: myapp:${TAG:-latest}
ports:
- "3000:3000"
db:
image: postgres:16
volumes:
- pg-data:/var/lib/postgresql/data
# Dev-only services
adminer:
image: adminer
ports:
- "8080:8080"
profiles:
- dev # Only starts with --profile dev
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025"
- "8025:8025"
profiles:
- dev # Only starts with --profile dev
# Monitoring (staging + production)
prometheus:
image: prom/prometheus
profiles:
- monitoring # Only starts with --profile monitoring
volumes:
pg-data:
# Usage:
docker compose --profile dev up # Includes adminer + mailhog
docker compose --profile monitoring up -d # Includes prometheus
docker compose --profile dev --profile monitoring up # Both profilesSicherheitscheckliste: Was NIEMALS in Umgebungsvariablen gehoert
Umgebungsvariablen sind praktisch, aber fuer sensible Daten inherent unsicher.
| NIEMALS | Stattdessen |
|---|---|
| Passwoerter in docker-compose.yml hartcodieren | .env-Dateien (gitignored) oder Docker Secrets verwenden |
| .env-Dateien in Versionskontrolle committen | .env zu .gitignore hinzufuegen, .env.example committen |
| Private Schluessel ueber Umgebungsvariablen uebergeben | Als Dateien ueber Volumes oder Docker Secrets mounten |
| Umgebungsvariablen im Anwendungscode protokollieren | Sensible Werte in Logs maskieren |
| .env-Dateien ueber Slack oder E-Mail teilen | Einen Secrets-Manager verwenden (Vault, AWS Secrets Manager) |
| Gleiche Secrets in dev/staging/production verwenden | Einzigartige Secrets pro Umgebung generieren |
# .gitignore â essential entries for Docker projects
.env
.env.*
!.env.example
secrets/
*.pem
*.key
*.crt# .env.example â commit this as a template
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=myapp
DB_PASSWORD=CHANGE_ME
# API Keys
API_KEY=CHANGE_ME
JWT_SECRET=CHANGE_ME
# Application
NODE_ENV=development
LOG_LEVEL=debug
APP_PORT=3000Debugging: Warum laedt meine Umgebungsvariable nicht?
Folgen Sie diesem systematischen Ablauf, wenn eine Umgebungsvariable Ihren Container nicht erreicht.
Schritt 1: Ist die Variable definiert?
Pruefen Sie, ob die Variable in .env, env_file oder environment-Direktive existiert.
Schritt 2: Erreicht sie den Container?
Fuehren Sie docker compose exec <service> env aus, um alle Variablen zu sehen.
Schritt 3: Ist die .env-Datei am richtigen Ort?
Die .env-Datei muss im selben Verzeichnis wie docker-compose.yml sein.
Schritt 4: Gibt es einen Namenskonflikt?
Pruefen Sie Tippfehler, Gross-/Kleinschreibung und Prioritaet.
Schritt 5: Ist die Compose-Datei gueltig?
Fuehren Sie docker compose config aus, um die aufgeloeste Konfiguration zu sehen.
Schritt 6: Wird die Variable ueberschrieben?
Shell-Umgebungsvariablen ueberschreiben .env-Werte. Pruefen Sie mit echo $VAR_NAME.
Nuetzliche Debug-Befehle
# 1. See the fully resolved Compose config (all variables substituted)
docker compose config
# 2. Check environment variables inside a running container
docker compose exec api env
# 3. Check a specific variable
docker compose exec api sh -c 'echo $DATABASE_URL'
# 4. Inspect container config (shows env vars, mounts, etc.)
docker inspect <container_id> --format '{{json .Config.Env}}' | jq .
# 5. Check if .env file is being read
docker compose config --format json | jq '.services.api.environment'
# 6. Verify env_file exists and is readable
docker compose config 2>&1 | grep -i "env_file"
# 7. Run a one-off container to test environment
docker compose run --rm api env | sort
# 8. Check for shell variable conflicts
env | grep -i "DB_"
# 9. Validate Compose file syntax
docker compose config --quiet && echo "Valid" || echo "Invalid"
# 10. Show variable substitution warnings
docker compose --verbose up 2>&1 | grep -i "variable"Haeufig gestellte Fragen
Was ist der Unterschied zwischen .env und env_file in Docker Compose?
Die .env-Datei wird automatisch fuer die Variablensubstitution in docker-compose.yml gelesen. Die env_file-Direktive laedt Variablen direkt in die Container-Umgebung.
Wie uebergebe ich Host-Umgebungsvariablen an einen Container?
Listen Sie den Variablennamen ohne Wert in der environment-Direktive auf oder verwenden Sie ${MY_VAR} in docker-compose.yml.
Sind Docker-Umgebungsvariablen sicher?
Nein. Sie sind ueber docker inspect sichtbar und erscheinen in /proc/environ. Verwenden Sie Docker Secrets fuer sensible Daten.
Welche Prioritaetsreihenfolge haben Umgebungsvariablen?
Von hoch nach niedrig: 1) docker compose run -e, 2) Host-Shell-Variablen, 3) environment-Direktive, 4) env_file-Direktive, 5) Dockerfile ENV.
Wie verwende ich verschiedene Variablen fuer Dev und Production?
Override-Dateien verwenden: Basis docker-compose.yml + docker-compose.override.yml fuer Dev + docker-compose.prod.yml fuer Production.
Was ist der Unterschied zwischen Docker Secrets und Umgebungsvariablen?
Secrets werden als Dateien in /run/secrets/ gemountet statt als Umgebungsvariablen gesetzt. Sie sind sicherer und unterstuetzen den _FILE-Suffix.