SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-10-19

Docker Compose: .env, env_file und environment erklärt

Erfahren Sie, wie .env, env_file und environment in Docker Compose funktionieren, welche Einstellung gewinnt und warum Passwörter in Secrets statt Umgebungsvariablen gehören.

Die drei Dinge, die als Env-Datei bezeichnet werden

Docker Compose verfügt über drei separate Mechanismen mit verwirrend ähnlichen Namen. Die Datei .env ersetzt ${VARIABLE}-Platzhalter direkt in compose.yaml, nachdem Compose die YAML-Datei eingelesen hat und bevor es sie validiert oder etwas ausführt. Das Attribut env_file: lädt eine Datei mit Schlüssel-Wert-Paaren in die Umgebung des Containers. Das Attribut environment: setzt Variablen direkt im Container, die in der Compose-Datei eingetragen werden. Diese Mechanismen sind nicht austauschbar. Wenn zwei davon denselben Schlüssel setzen, entscheidet eine dokumentierte Prioritätsreihenfolge.

Dieser Leitfaden zeigt jeden Mechanismus in der Praxis. Anschließend wird die Priorität mit einem ausführbaren Befehl nachgewiesen. Danach geht es um den wichtigeren Punkt: Umgebungsvariablen können von jedem gelesen werden, der docker inspect ausführen darf. Passwörter gehören daher nicht in Umgebungsvariablen. Wenn Sie mit Compose-Dateien allgemein noch nicht vertraut sind, beginnen Sie mit Docker-Compose-Grundlagen auf einem VPS und kehren Sie anschließend für die Konfiguration hierher zurück.

Die .env-Datei gilt für die Compose-Datei, nicht für den Container

Erstellen Sie ein Verzeichnis und legen Sie zwei Dateien darin ab.

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

Fragen Sie jetzt Compose ab, was tatsächlich geparst wurde.

docker compose config

Die Ausgabe zeigt image: alpine:3.20. Der Platzhalter ist verschwunden, weil die Interpolation beim Laden der Datei durch Compose erfolgt ist. Compose sucht im Projektverzeichnis nach .env: Das ist das mit --project-directory angegebene Verzeichnis, falls Sie es festgelegt haben, andernfalls das Verzeichnis der ersten mit -f übergebenen Datei und andernfalls das aktuelle Verzeichnis. Compose ersetzt die gefundenen ${NAME}-Platzhalter in YAML-Werten. Schlüssel werden niemals interpoliert. Ein doppeltes $$ erzeugt ein wörtliches Dollarzeichen statt einer Ersetzung.

Starten Sie anschließend den Dienst.

docker compose run --rm demo

printenv ALPINE_TAG wird mit Status 1 beendet und gibt nichts aus. Die Variable ist im Container nicht vorhanden. Das ist das häufigste Missverständnis: .env konfiguriert die Compose-Datei, nicht den Prozess. Eine .env-Datei mit POSTGRES_PASSWORD=hunter2 bewirkt für Ihre Datenbank überhaupt nichts, solange kein Teil der Compose-Datei auf sie verweist.

${NAME:-default} stellt einen Ersatzwert bereit, wenn die Variable nicht gesetzt oder leer ist. ${NAME:?message} verhindert, dass Compose startet, und gibt Ihre Meldung aus. Das ist die richtige Wahl für einen Wert ohne sicheren Standardwert.

env_file lädt Variablen in den Container

Das Attribut env_file: benennt eine oder mehrere Dateien, deren Inhalte zu Umgebungsvariablen des Containers werden.

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

Dies gibt from_env_file aus. Das Dateiformat besteht aus einfachen KEY=value-Zeilen, eine pro Zeile, wobei # einen Kommentar einleitet. Es handelt sich nicht um eine Shell, obwohl Compose die Datei mit einigen shell-ähnlichen Regeln parst. Umschließende Anführungszeichen werden entfernt, daher liefern KEY="value" und KEY=value das gleiche Ergebnis. Bei Werten in doppelten Anführungszeichen oder ohne Anführungszeichen werden ${...}-Platzhalter interpoliert, während Werte in einfachen Anführungszeichen wörtlich übernommen werden. Ein export-Präfix wird akzeptiert, ist aber nicht erforderlich. Leerzeichen um das =-Zeichen werden entfernt. Achten Sie auf Inline-Kommentare: Bei einem Wert ohne Anführungszeichen leitet ein Leerzeichen gefolgt von # einen Kommentar ein; setzen Sie daher jeden Wert, der # enthält, in Anführungszeichen.

Ein fehlender env_file-Pfad führt zu einem Fehler und Compose bricht ab. Markieren Sie die Datei als optional, falls sie berechtigterweise fehlen darf:

    env_file:
      - path: ./app.env
        required: false

environment setzt Variablen inline

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

Es werden zwei Syntaxformen akzeptiert: die oben gezeigte Mapping-Form und eine Listenform mit - GREETING=from_environment. Beide verhalten sich identisch. Beide bieten außerdem einen zusätzlichen Mechanismus: Ein Schlüssel ohne Wert übernimmt die Variable aus der Shell, in der Sie docker compose ausgeführt haben. In der Listenform ist das ein allein stehendes - GREETING. In der Mapping-Form ist es GREETING: ohne Inhalt nach dem Doppelpunkt.

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

Damit wird from_my_shell ausgegeben. Wenn Sie den Befehl ausführen, ohne GREETING in der Shell zu setzen, legt Compose keine Variable fest und gibt auch keine Warnung aus. Solche stillen Fehler beim Durchreichen von Variablen sollten Sie kennen. Ein Dienst, der mit einer leeren Passwortvariable startet, läuft häufig erfolgreich und ist dann einfach ungeschützt.

Welche Einstellung gewinnt

Docker dokumentiert die Rangfolge mit der höchsten Priorität zuerst: docker compose run -e in der Befehlszeile, danach environment oder env_file, deren Wert aus Ihrer Shell oder aus einer env-Datei interpoliert wird, danach das einfache environment in der Compose-Datei, danach env_file und anschließend die in das Image integrierte ENV-Direktive.

Die Kurzfassung für die tägliche Arbeit: environment: hat Vorrang vor env_file:, und -e in der Befehlszeile hat Vorrang vor beiden. Beweisen Sie das in einer Datei.

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

Der erste Befehl gibt from_environment aus. Damit hat environment: den Wert in app.env überschrieben. Der zweite Befehl gibt from_cli aus. Nichts in der Compose-Datei überschreibt die Befehlszeile.

Wenn sich ein Container so verhält, als wäre Ihre Konfiguration nie angewendet worden, raten Sie nicht. docker compose config gibt die vollständig aufgelöste Datei aus, und docker compose config --environment gibt die Interpolationsvariablen aus, die Compose verwendet. Bei den meisten Meldungen, dass die env-Datei ignoriert wird, ist ein Wert auf zwei verschiedenen Ebenen doppelt gesetzt.

Warum Umgebungsvariablen offengelegt werden

Legen Sie ein Passwort in environment: fest, wird es in der Containerkonfiguration auf der Festplatte gespeichert. Jeder Benutzer in der Gruppe docker kann es sehen.

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

Die Ausgabe enthält "DB_PASSWORD=hunter2" im Klartext. Drei weitere Pfade legen denselben Wert offen. docker compose config gibt ihn im Terminal aus. Dadurch kann er versehentlich in ein Supportforum kopiert werden. Untergeordnete Prozesse übernehmen die Variable standardmäßig. Das gilt nicht, wenn der übergeordnete Prozess sie gezielt mit einer ersetzten Umgebung startet. Ein Prozess, der als derselbe Benutzer wie PID 1 ausgeführt wird oder über CAP_SYS_PTRACE verfügt, kann sie aus /proc/1/environ lesen. In einem Container, in dem alles als root ausgeführt wird, gilt das für jeden Prozess. Eine Anwendung, die als unprivilegierter Benutzer läuft, während PID 1 als root ausgeführt wird, erhält für diese Datei stattdessen einen Berechtigungsfehler. Die Variable befindet sich jedoch weiterhin in ihrer eigenen Umgebung. Außerdem schreiben Anwendungs-Crash-Handler regelmäßig die gesamte Umgebung in ein Log oder einen Fehlerbericht.

Die Mitgliedschaft in der Gruppe docker entspricht auf dem Host praktisch root-Rechten. Sie ist daher keine Berechtigungsgrenze, auf die Sie sich verlassen können. Der Leitfaden zu Benutzerkonten mit geringsten Rechten auf einem VPS erklärt, warum Sie diese Gruppe auf jedem gemeinsam genutzten System beschränken sollten.

Compose-Secrets halten den Wert in einer Datei

Compose unterstützt dateibasierte Secrets. Der Wert wird als Datei in den Container eingebunden, statt in die Umgebung injiziert zu werden.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

Das Secret wird im Container unter /run/secrets/db_password eingebunden. Der Name nach dem Schrägstrich ist der Secret-Name aus dem Top-Level-Block secrets:.

Das Suffix _FILE ist eine Konvention der Docker Official Images, darunter postgres, mysql und mariadb. Diese EntryPoint-Skripte prüfen auf VARNAME_FILE, lesen die Datei und verwenden ihren Inhalt. Dies ist keine Docker-Funktion. Es funktioniert daher nur, wenn das Image diese Funktion implementiert. Prüfen Sie die Dokumentation des Images, bevor Sie davon ausgehen, dass SOMETHING_FILE berücksichtigt wird. Anwendungen ohne Unterstützung dafür können die Datei beim Start häufig selbst lesen. Alternativ können Sie den Pfad übergeben und dies durch Ihren eigenen EntryPoint erledigen.

Überprüfen Sie dies aus dem laufenden Container heraus:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

Der erste Befehl gibt das Passwort aus. Der zweite gibt nichts aus, weil der Wert nie in die Umgebung gelangt ist. Genau das ist der Zweck: docker inspect dieses Containers zeigt nur den unkritischen Pfad an.

Schützen Sie die Quelldatei auf dem Host, da das Secret nur so vertraulich ist wie die dahinterliegende Datei:

chmod 600 db_password.txt

Der praktikable Mittelweg auf einem VPS

Viele selbst gehostete Images unterstützen keine _FILE-Variablen. Daher sind Umgebungsvariablen der einzige Weg. Bei einem VPS mit einem einzelnen Administrator besteht das realistische Ziel darin, zu verhindern, dass die Werte in einer für alle lesbaren Datei im Projektverzeichnis liegen. Außerdem dürfen sie nicht in Git landen.

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

install -m 600 erstellt die Datei bereits mit den richtigen Berechtigungen. Dadurch gibt es kein Zeitfenster, in dem sie für alle lesbar ist. Root ist der Eigentümer. Ein Benutzer ohne Root-Rechte auf dem System kann die Datei daher nicht lesen. Jeder, der docker ausführen darf, kann den Wert jedoch weiterhin aus dem Container auslesen. Fügen Sie *.env und .env zu .gitignore hinzu. Committen Sie stattdessen eine app.env.example, die die Schlüsselnamen mit leeren Werten enthält. Ein committetes Passwort muss geändert werden.

Das Ändern eines Werts bedeutet, den Dienst neu zu starten. Umgebungsvariablen werden einmalig beim Start des Containerprozesses eingelesen. Eine Änderung der Datei hat daher erst Wirkung, wenn Sie docker compose up -d --force-recreate db ausführen. Dasselbe Muster wird im Leitfaden n8n hinter HTTPS auf einem VPS verwendet. Dort liegt der Verschlüsselungsschlüssel außerhalb der Compose-Datei.

Konfiguration nach Umgebung aufteilen

Compose liest .env standardmäßig aus dem Projektverzeichnis. Mit --env-file legen Sie einen anderen Speicherort fest.

docker compose --env-file .env.staging config

Mehrere Dateien werden in der angegebenen Reihenfolge gelesen. Später gelesene Dateien überschreiben frühere. Speichern Sie die nicht geheimen Standardwerte in einer versionierten Datei und die Geheimnisse in einer Datei, die den Server nie verlässt. Dasselbe gilt für env_file:: Bei einem doppelten Schlüssel gilt der Wert aus der zuletzt aufgeführten Datei.

FAQ

Warum wird meine .env-Datei im Container ignoriert?

Sie wird nicht ignoriert. Die .env-Datei ersetzt ${NAME}-Platzhalter in der Compose-Datei. Außerdem kann sie eigene Compose-Variablen wie COMPOSE_PROJECT_NAME oder COMPOSE_FILE setzen. Variablen im Container werden dadurch nie gesetzt. Damit der Wert im Container verfügbar ist, müssen Sie darauf verweisen: environment: { KEY: "${NAME}" }. Alternativ können Sie env_file: ./that-file.env verwenden.

Überschreibt environment env_file oder umgekehrt?

environment: hat Vorrang. Die von Docker dokumentierte Reihenfolge ordnet das Attribut environment über dem Attribut env_file ein. Beide stehen unter docker compose run -e in der Befehlszeile. Wenn ein Schlüssel an beiden Stellen gesetzt ist, wird der Wert in env_file stillschweigend nicht verwendet.

Wie sehe ich den endgültigen Wert, den Compose verwendet?

Führen Sie docker compose config aus, um die vollständig aufgelöste Compose-Datei mit angewendeter Interpolation auszugeben. Für einen bereits laufenden Container zeigt docker inspect <container> --format '{{json .Config.Env}}' genau, was sein Prozess erhalten hat.

Werden Compose-Secrets verschlüsselt?

Nein. Ein dateibasiertes Secret wird als unverschlüsselte Datei unter /run/secrets/<name> in den Container eingebunden. Die Quelldatei liegt unverschlüsselt auf der Festplatte des Hosts. Der Vorteil besteht in der Begrenzung des Gültigkeitsbereichs, nicht in der Verschlüsselung: Der Wert bleibt außerhalb der Container-Umgebung, außerhalb der Ausgabe von docker inspect und außerhalb von Crash-Dumps, die die Umgebung ausgeben.

Kann ich Anführungszeichen und Leerzeichen in einer Env-Datei verwenden?

Ja. Compose entfernt die umschließenden Anführungszeichen. Daher ergeben KEY="value with spaces" und KEY=value with spaces beide den Wert value with spaces. Verwenden Sie einfache Anführungszeichen, wenn der Wert ein $ enthält, das nicht interpoliert werden darf. Setzen Sie außerdem jeden Wert in Anführungszeichen, der # enthält, weil dies ohne Anführungszeichen einen Kommentar einleitet. Leerzeichen um = werden entfernt. Daher setzt KEY = value weiterhin KEY.

War dieser Artikel hilfreich?