Docker Compose Secrets halten Passwörter, Tokens und Zertifikate aus dem Compose-Text heraus und stellen sie einem Dienst gezielt als Datei unter /run/secrets/ bereit. Dieser Leitfaden zeigt eine nachvollziehbare Struktur, die Rechte-Fallen und die Prüfung, damit ein Secret nur dort landet, wo es wirklich gebraucht wird.
Stand: 23.10.2026
Auf einen Blick
- Ein Compose-Secret wird oben definiert und je Dienst ausdrücklich freigegeben.
- Der Standardpfad im Container lautet
/run/secrets/<secret_name>. - Dateien sind für lokale Compose-Projekte der robuste Standard;
environmenthat eine wichtige Swarm-Einschränkung. - Images müssen die Datei auch unterstützen, häufig über eine Variable mit dem Suffix
_FILE. docker compose configprüft das aufgelöste Modell vor dem Start.
Teil 1 – Grundlagen und Ziel
1. Die Ausgangslage und das Ziel
Ein Passwort in environment:, einer .env-Datei oder direkt im Repository ist schnell eingerichtet, aber schwer sauber zu kontrollieren. Die Docker-Dokumentation weist darauf hin, dass Umgebungsvariablen oft allen Prozessen verfügbar sind und beim Debuggen unbemerkt in Logs erscheinen können. Docker Compose Secrets trennen deshalb die geheime Nutzlast von der Service-Konfiguration.
Das Ziel ist nicht, jedes Konfigurationsproblem als Secret zu behandeln. Ein Datenbankname oder ein Hostname ist normalerweise keine geheime Information. Ein Datenbankpasswort, ein API-Token oder ein Zertifikat dagegen schon. Nach diesem Leitfaden existieren zwei Secrets als lokale Dateien, die Datenbank erhält beide, und WordPress erhält ausschließlich das Datenbankpasswort.
Damit ergänzt der Aufbau den Artikel zu Docker-Containern sicher betreiben. Für die grundlegende Dateistruktur hilft außerdem der Einstieg Docker Compose Grundlagen mit Beispiel. Die eigentliche Anwendung bleibt dadurch austauschbar: Das Muster funktioniert für viele Images, sofern deren Dokumentation einen Dateipfad oder eine _FILE-Variable vorsieht.
Warum ein Secret keine magische Verschlüsselung ist
Ein Secret reduziert vor allem die Verbreitung im Compose-Modell und erlaubt eine Freigabe pro Dienst. Laut Docker wird es bei Compose als einzelne Datei in den Container eingebunden. Wer Root-Zugriff auf den Host oder Zugriff auf einen berechtigten Container hat, kann daraus nicht automatisch ausgeschlossen werden. Deshalb gehören auch die Host-Dateirechte, der Zugriff auf das Deployment-Verzeichnis und die Dienstfreigaben in das Sicherheitsmodell.
Der entscheidende Unterschied lautet: Ein oben definiertes Secret verleiht noch keinem Dienst Zugriff. Erst der jeweilige secrets:-Eintrag am Service ist die ausdrückliche Berechtigung. Genau diese Trennung verhindert, dass ein Webdienst aus Bequemlichkeit auch das Root-Passwort der Datenbank sieht.

2. Voraussetzungen und Projektstruktur
Du brauchst Docker Engine mit dem Compose-Plugin auf einem Linux-Container-Host. Die offizielle Dokumentation nennt Linux-Container als Voraussetzung, weil Compose Secrets per Bind-Mount einer einzelnen Datei liefert; Windows-Container können nur Verzeichnisse bind-mounten. Lege das Projekt in ein Verzeichnis, das nur die Personen verwalten können, die die Secrets auch sehen dürfen.
Die folgende Struktur trennt Konfiguration, persistente Daten und Geheimnisse. Zuerst erzeugst du die Verzeichnisse; die beiden Passwortdateien bleiben bewusst außerhalb des Compose-Textes. Ersetze die Platzhalternamen später durch echte, ausreichend starke Werte, ohne diese in die Shell-Historie oder in Screenshots zu kopieren.
mkdir -p wordpress-secrets/secrets wordpress-secrets/db_data
cd wordpress-secretsDanach liegt das Arbeitsverzeichnis vor. Prüfe vor dem Fortfahren mit pwd, ob Du tatsächlich im vorgesehenen Projektordner arbeitest.
Datei oder Umgebungsvariable als Quelle entscheiden
Die oberste secrets:-Sektion kann laut Referenz den Inhalt aus file oder environment beziehen. Für ein lokales, nachvollziehbares Compose-Projekt verwendet dieser Leitfaden file. Das vermeidet, dass ein Secret zunächst in einer Host-Umgebungsvariablen liegt, und passt zu den Beispielen der Docker-Dokumentation.
| Quelle | Wann passend | Wichtige Grenze |
|---|---|---|
file | Lokale, kontrollierte Secret-Datei | Die Datei muss geschützt, gesichert und getrennt vom Repository gehalten werden. |
environment | Ein Wert wird während des Compose-Laufs vom Host geliefert | Docker Compose unterstützt dies, docker stack deploy jedoch nicht. |
external | Ein bereits auf der Plattform verwaltetes Secret | Die Plattform muss das Secret bereits bereitstellen. |
Die Tabelle ist eine Entscheidungshilfe, keine Aufforderung, Geheimnisse in Umgebungsvariablen zu drucken. Wenn Du später auf Swarm wechselst, prüfe den gewählten Secret-Typ erneut, denn die Referenz schließt environment bei docker stack deploy ausdrücklich aus.
Teil 2 – Secrets definieren und Dienste beschränken
3. Die Secret-Dateien anlegen
Schreibe jeweils genau einen Wert in eine Datei. Die Docker-Beispiele verwenden für WordPress und MySQL die Dateien db_password.txt und db_root_password.txt. Dieses Beispiel übernimmt das Prinzip mit einem Unterordner, damit Geheimnisse nicht zwischen Compose-Dateien und Dokumentation liegen.
printf '%s' '<DEIN-DATENBANKPASSWORT>' > secrets/db_password.txt
printf '%s' '<DEIN-MYSQL-ROOT-PASSWORT>' > secrets/db_root_password.txtDie beiden Dateien enthalten danach die jeweiligen Werte ohne zusätzlichen Zeilenumbruch. Öffne sie nicht mit einem Befehl, der ihren Inhalt auf dem Terminal ausgibt.
Beschränke anschließend den Host-Zugriff. Das ist keine Compose-Funktion, sondern eine nötige Ergänzung: Wer die Datei auf dem Host lesen darf, kann ihr Geheimnis kennen, unabhängig davon, welche Services Compose freigibt.
chmod 600 secrets/db_password.txt secrets/db_root_password.txtErfolg bedeutet, dass nur der Eigentümer lesen und schreiben darf. Den Besitzer änderst Du nur dann, wenn das Deployment bewusst unter einem anderen Host-Konto läuft.
4. Die Compose-Datei schreiben
Die Compose-Datei bildet zwei getrennte Berechtigungsgrenzen ab. Der Dienst db benötigt das Root-Passwort zur Initialisierung und das reguläre Passwort für den WordPress-Nutzer. Der Dienst wordpress bekommt dagegen nur db_password. Das folgt dem Mehrdienst-Beispiel aus der Docker-Dokumentation.
Lege diese Datei als compose.yaml ab. Das Bild nutzt die offizielle Compose-Struktur mit MYSQL_ROOT_PASSWORD_FILE, MYSQL_PASSWORD_FILE und WORDPRESS_DB_PASSWORD_FILE. Diese _FILE-Variablen sind laut Docker eine Konvention, die unter anderem offizielle MySQL- und Postgres-Images verwenden; prüfe sie bei jedem anderen Image in dessen eigener Dokumentation.
services:
db:
image: mysql:latest
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_root_password
- db_password
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: secrets/db_password.txt
db_root_password:
file: secrets/db_root_password.txt
volumes:
db_data:Die Datei ist korrekt strukturiert, wenn jeder in einem Service referenzierte Secret-Name auch oben definiert ist. Compose meldet laut Referenz einen Fehler, wenn ein Secret weder auf der Plattform existiert noch in der obersten secrets:-Sektion definiert wurde.
Was im Container tatsächlich ankommt
Die Kurzschreibweise unter einem Dienst verwendet nur den Namen. Compose bindet ihn dann schreibgeschützt nach /run/secrets/<secret_name> ein. Der Name der Quelle und der Zielpfad sind dabei gleich. Das Passwort wird also nicht als Klartextwert in WORDPRESS_DB_PASSWORD_FILE gesetzt; diese Variable enthält lediglich den Pfad zur Datei.

5. Variante mit Langsyntax verstehen
Die Langsyntax brauchst Du, wenn der Dateiname im Container abweichen soll oder wenn die Quelle aus einer Umgebungsvariablen stammt. source benennt das Secret, target legt den Namen unter /run/secrets/ oder einen absoluten Alternativpfad fest. Außerdem sind uid, gid und mode möglich.
Wichtig ist die Einschränkung der Referenz: Bei einer file-Quelle nutzt Compose intern einen Bind-Mount. Daher werden uid, gid und mode dort stillschweigend ignoriert. Verlasse Dich also bei Datei-Secrets nicht darauf, in Compose andere Eigentümer zu erzwingen.
services:
frontend:
image: example/webapp
secrets:
- source: my-token
target: app-token
uid: "103"
gid: "103"
mode: 0o440
secrets:
my-token:
environment: "MY_TOKEN"In diesem von Docker dokumentierten Muster liest Compose den Wert aus MY_TOKEN. Die Rechteangaben sind gerade bei dieser environment-Quelle implementiert. Für den lokalen WordPress-Stack aus dem vorherigen Abschnitt bleibt deshalb die kurze, dateibasierte Variante klarer.
Teil 3 – Validieren, starten und prüfen
6. Das aufgelöste Modell vor dem Start prüfen
Starte nicht blind. docker compose config führt die Compose-Dateien zusammen, löst Variablen auf und erweitert Kurznotationen in das kanonische Format. Damit findest Du Einrückungsfehler, fehlende Dateien und falsch geschriebene Secret-Namen, bevor ein Dienst seine Initialisierung beginnt.
docker compose configBei Erfolg erscheint das normalisierte Compose-Modell und der Befehl endet ohne Fehlermeldung. Prüfe dabei besonders, dass db_password am Dienst wordpress steht, db_root_password dort aber nicht auftaucht.
Wenn Du nur den Status der Konfiguration prüfen willst, ohne sie auszugeben, nutze die dokumentierte Quiet-Option. Sie eignet sich auch für eine spätere CI-Prüfung, gibt aber keinen Ersatz für die fachliche Kontrolle der Dienstfreigaben.
docker compose config --quietErfolg ist hier eine Rückkehr ohne Ausgabe und ohne Fehlercode. Ein Fehler nennt typischerweise den nicht auflösbaren Teil des Compose-Modells; korrigiere ihn in der Datei statt ein Secret testweise in eine Umgebungsvariable zu kopieren.
7. Stack kontrolliert starten
Erst nach der Konfigurationsprüfung startest Du die Dienste. Das dokumentierte Compose-Beispiel arbeitet mit den zwei Services und dem Volume; die folgenden Kommandos nutzen die normale Compose-Bedienung für diesen Stack. Das persistente Volume schützt die Datenbankdaten vor einem bloßen Neuerstellen des Containers, ersetzt aber kein separates Backup.
docker compose up -dErfolg erkennst Du daran, dass Compose die Services erstellt oder startet und in den Hintergrund zurückkehrt. Der WordPress-Port ist im Beispiel absichtlich auf 8000 gelegt; für einen externen Zugriff gehört davor in einem Homelab häufig ein Reverse Proxy. Siehe dazu die Anleitung Traefik im Homelab mit Docker Compose.
Die wichtigsten Stolperfallen
Symptom: Das Image ignoriert die Passwortdatei
Ursache: Nicht jedes Image kennt eine Variable mit dem Suffix _FILE. Die Docker-Dokumentation beschreibt dies ausdrücklich als Konvention einiger Images, nicht als allgemeine Compose-Regel. Lösung: Lies die Umgebungsvariablen des konkreten Images und übergib gegebenenfalls den von diesem Image dokumentierten Dateipfad.
Symptom: Compose meldet ein unbekanntes Secret
Ursache: Der Name unter services.<name>.secrets stimmt nicht mit der oberen Definition überein. Lösung: Gleiche die Namen Zeichen für Zeichen ab und führe danach docker compose config erneut aus.
Symptom: Ein Dienst kann ein Secret lesen, das er nicht braucht
Ursache: Das Secret wurde beim falschen Service eingetragen. Die obere Definition allein berechtigt nicht, aber jeder Eintrag am Service tut es. Lösung: Entferne die Freigabe am unberechtigten Service und behalte nur die minimal notwendige Liste.
Symptom: Rechte aus uid, gid oder mode wirken bei Datei-Secrets nicht
Ursache: Bei file verwendet Compose einen Bind-Mount und ignoriert diese Attribute laut Referenz stillschweigend. Lösung: Sichere die Quelldatei auf dem Host ab oder nutze für die dokumentierte Rechte-Steuerung eine environment-Quelle, sofern Du nicht mit docker stack deploy arbeitest.
Symptom: Der Umstieg auf Swarm bricht mit Environment-Secrets
Ursache: Die environment-Quelle wird von Docker Compose unterstützt, aber nicht von docker stack deploy. Lösung: Verwende für diesen Weg eine Datei- oder externe Secret-Quelle und prüfe das Zielsystem vor der Migration.
Verifikation: So prüfst du, dass alles läuft
Die Prüfung besteht aus drei Ebenen: Compose-Modell, laufende Dienste und die gezielte Bereitstellung im Container. So unterscheidest Du einen Syntax-Erfolg von einer tatsächlich wirksamen Berechtigungsgrenze.
Zuerst listest Du den Stack-Status. Beide Services sollen als laufend erscheinen; ein dauernder Neustart weist dagegen häufig auf eine fehlende Datenbankverbindung oder eine nicht unterstützte Image-Variable hin.
docker compose psErwartet wird eine Tabelle mit den Services db und wordpress sowie ihrem Laufstatus. Prüfe außerdem, dass der Port 8000 am WordPress-Service ausgewiesen wird, sofern die Port-Zeile unverändert blieb.
Danach kontrollierst Du im Webdienst nur die Namen und Rechte der eingebundenen Secret-Dateien. Der Befehl gibt bewusst nicht deren Inhalt aus.
docker compose exec wordpress ls -l /run/secretsErwartet wird dort db_password. db_root_password darf im WordPress-Container nicht erscheinen, weil dieser Dienst es nicht angefordert hat. Gerade dieses negative Ergebnis zeigt, dass die Freigabe pro Service wirkt.
Im Datenbankcontainer dürfen dagegen beide definierten Dateien vorhanden sein. Damit stimmt die Berechtigung mit der Rolle überein: Initialisierung und Nutzerpasswort liegen beim Datenbankdienst, nicht beim Webdienst.
docker compose exec db ls -l /run/secretsErwartet werden db_password und db_root_password. Gib die Dateien nicht mit cat aus; die Existenz und die getrennte Sichtbarkeit reichen für diese Verifikation aus.
8. Rotation und Backup richtig einordnen
Ein Secret zu rotieren heißt, den Wert bei der Quelle zu ändern und den betroffenen Dienst kontrolliert neu zu erzeugen, damit er den neuen Inhalt erhält. Dabei muss die Anwendung selbst den neuen Zugangswert akzeptieren. Bei einer Datenbank ist es daher keine sichere Abkürzung, nur die Passwortdatei zu überschreiben: Ändere das Passwort zuerst nach der für MySQL und die Anwendung vorgesehenen Methode und aktualisiere dann die Secret-Datei abgestimmt.
Auch Backups verdienen eine klare Grenze. Das Datenbank-Volume enthält Anwendungsdaten und braucht eine passende Sicherung. Die Secret-Dateien brauchen ebenfalls einen geschützten Wiederherstellungsweg, dürfen aber nicht versehentlich in ein unverschlüsseltes Projektarchiv geraten. Für containerisierte Volumes beschreibt die Anleitung Restic Docker Backup für Volumes die Backup-Perspektive; für den gesamten Host gilt zusätzlich eine eigene Zugriffskontrolle.
Fazit
Docker Compose Secrets sind kein Ersatz für Host-Härtung, aber sie schaffen eine klare und überprüfbare Grenze zwischen Konfiguration und sensiblen Werten. Definiere die Quelle oben, gib sie nur am benötigten Dienst frei und nutze die Pfad-Schnittstelle, die das jeweilige Image dokumentiert. Mit docker compose config vor dem Start und der Dateilisten-Prüfung in jedem Container belegst Du zudem, dass die beabsichtigte Trennung wirklich umgesetzt ist.
Quellen
- Docker Docs: Manage secrets securely in Docker Compose
- Docker Docs: Compose-Referenz für secrets
- Docker Docs: Service-Attribut secrets
- Docker Docs: docker compose config
Häufige Fragen
Bei der kurzen Syntax bindet Compose ein Secret schreibgeschützt unter /run/secrets/<secret_name> ein. Der Dienst muss das Secret in seiner eigenen secrets-Liste ausdrücklich anfordern.
Ja. Die obere secrets-Definition kann environment verwenden. Diese Quelle wird jedoch nicht von docker stack deploy unterstützt; dort sind file oder external vorgesehen.
Sie begrenzen die Bereitstellung auf ausdrücklich berechtigte Dienste und liefern den Wert als Datei. Umgebungsvariablen können laut Docker für Prozesse breiter sichtbar sein und beim Debuggen in Logs auftauchen.
Variablen mit dem Suffix _FILE sind eine Image-Konvention, keine allgemeine Compose-Funktion. Prüfe daher immer die Dokumentation des verwendeten Images.
Bei file-Quellen ignoriert Docker Compose uid, gid und mode, weil es einen Bind-Mount verwendet. Schütze die Quelldatei auf dem Host und prüfe die tatsächliche Bereitstellung im Container.





Schreibe einen Kommentar