Umgebungsvariablen in Docker Compose - env_file und environment richtig einsetzen

Umgebungsvariablen in Docker Compose - env_file und environment richtig einsetzen

Docker Compose bietet zwei Wege, Umgebungsvariablen an Container zu übergeben — und einen dritten, der oft verwechselt wird.

Variante 1: environment (inline)

Direkt im Compose-File, gut für wenige, nicht-geheime Werte:

services:
  app:
    image: myapp
    environment:
      APP_ENV: production
      APP_PORT: 8080
      DB_HOST: db

Oder als Liste:

environment:
  - APP_ENV=production
  - APP_PORT=8080

Variable ohne Wert übernimmt den Wert aus der Shell-Umgebung:

environment:
  - SECRET_KEY    # Wert kommt vom aufrufenden System

Variante 2: env_file (externe Datei)

Für viele Variablen oder wenn Secrets nicht ins Compose-File gehören:

services:
  app:
    image: myapp
    env_file:
      - .env
      - .env.production

Die .env-Datei hat ein simples Format:

APP_ENV=production
DB_HOST=db
DB_PASSWORD=supersecret
# Kommentare sind erlaubt

Mehrere Dateien werden der Reihe nach geladen — spätere Dateien überschreiben frühere.

Die auto-geladene .env-Datei

Hier liegt die häufigste Verwechslung: Eine .env-Datei im selben Verzeichnis wie das Compose-File wird automatisch geladen — aber nicht als env_file: für Container, sondern für Variable Substitution im Compose-File selbst:

# docker-compose.yml
services:
  db:
    image: postgres:${PG_VERSION}    # Wird aus .env ersetzt
    ports:
      - "${DB_PORT}:5432"
# .env
PG_VERSION=16
DB_PORT=5432

Das ist praktisch für Versions-Pins und Ports — aber diese Variablen landen nicht automatisch im Container.

Priorität: Wer gewinnt?

Von höchster zu niedrigster Priorität:

  1. Shell-Umgebungsvariablen des aufrufenden Systems
  2. Werte in .env (für Compose-Substitution)
  3. Defaultwerte im Compose-File: ${VAR:-default}
environment:
  DB_HOST: ${DB_HOST:-localhost}   # Fallback auf localhost wenn nicht gesetzt

.env.example als Konvention

Niemals .env committen. Stattdessen eine .env.example mit allen Keys aber ohne echte Werte:

# .env.example — in Git versioniert
APP_ENV=
DB_HOST=
DB_PASSWORD=
SECRET_KEY=

In .gitignore:

.env
.env.production
.env.local

Neue Entwickler kopieren .env.example zu .env und füllen die Werte aus. So ist klar, welche Variablen erwartet werden, ohne dass Secrets in Git landen.

Zusammenfassung

| | environment: | env_file: | auto .env |
|---|---|---|---|
| Werte sichtbar im Compose-File | Ja | Nein | Nein |
| Geht in Container | Ja | Ja | Nein (nur Substitution) |
| Gut für Secrets | Nein | Ja | Nein |

Die Faustregel: Ports, Feature-Flags und nicht-geheime Konfiguration inline via environment:, Datenbankpasswörter und API-Keys in einer env_file:-Datei, die nicht versioniert wird.