sops und age: .env-Dateien verschlüsselt in Git
Passwörter und API-Schlüssel aus .env-Dateien mit sops und age verschlüsselt ins Git-Repository legen und auf dem Server direkt mit Docker Compose nutzen.
Von Nathan Base
Jedes Projekt mit Docker Compose hat irgendwann eine .env-Datei mit Datenbank-Passwörtern, API-Schlüsseln und Tokens. Sie landet zu Recht in der .gitignore, denn im Klartext hat sie in einem Repository nichts verloren. Das Problem: Damit ist sie auch nicht versioniert. Auf dem Server liegt eine Version, auf dem Mac eine andere, und wenn der Server ausfällt, rekonstruierst Du die Passwörter aus dem Gedächtnis und alten Chatverläufen.
sops löst das elegant. Es verschlüsselt die Werte in der .env-Datei, lässt aber die Namen der Variablen lesbar. Die verschlüsselte Datei kann gefahrlos ins Repository, und in jedem Commit siehst Du, welche Variable sich geändert hat, ohne ihren Inhalt zu sehen. Entschlüsseln kann nur, wer einen passenden Schlüssel hat. Als Schlüssel nimmst Du age, ein kleines, modernes Verschlüsselungswerkzeug, das deutlich einfacher ist als PGP.
Diese Anleitung richtet sops und age auf dem Mac und auf einem Linux-Server ein, verschlüsselt eine .env-Datei und startet damit Docker Compose. Stand ist sops 3.13 und age 1.3. Grundlage ist die offizielle Dokumentation von sops zu age.
Wie das Prinzip funktioniert
Jeder, der die Datei entschlüsseln darf, hat ein eigenes Schlüsselpaar aus age. Der private Schlüssel bleibt auf seinem Gerät, der öffentliche beginnt mit age1 und kommt in eine Konfigurationsdatei im Repository. sops verschlüsselt die Werte so, dass jeder dieser privaten Schlüssel sie öffnen kann.
In dieser Anleitung sind das zwei Empfänger: Dein Mac und Dein Server. Kommt später ein Kollege oder ein zweiter Server dazu, ergänzt Du dessen öffentlichen Schlüssel.
sops und age installieren
Auf dem Mac installierst Du beide Programme mit Homebrew:
brew install sops ageAuf einem Server mit Debian oder Ubuntu gibt es age in den Paketquellen. sops lädst Du als fertiges Programm von der Release-Seite auf GitHub. Für einen Server mit Intel- oder AMD-Prozessor sieht das so aus:
sudo apt install age
curl -LO https://github.com/getsops/sops/releases/download/v3.13.3/sops-v3.13.3.linux.amd64
sudo install -m 755 sops-v3.13.3.linux.amd64 /usr/local/bin/sopsAuf einem Raspberry Pi oder einem anderen ARM-Server ersetzt Du in beiden Zeilen amd64 durch arm64. Prüfe mit sops --version, ob es geklappt hat.
Schlüssel erzeugen
sops sucht den privaten age-Schlüssel an einem festen Ort, und dieser Ort ist auf Mac und Linux unterschiedlich. Genau daran scheitern viele beim ersten Versuch.
Auf dem Mac liegt er laut sops-Doku unter ~/Library/Application Support/sops/age/keys.txt:
mkdir -p "$HOME/Library/Application Support/sops/age"
age-keygen -o "$HOME/Library/Application Support/sops/age/keys.txt"Auf dem Linux-Server liegt er unter ~/.config/sops/age/keys.txt:
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txtage-keygen gibt jeweils eine Zeile mit Public key: age1… aus. Notiere Dir beide öffentlichen Schlüssel, vom Mac und vom Server. Den öffentlichen Schlüssel zeigt Dir auch später jederzeit age-keygen -y mit dem Pfad zur Schlüsseldatei an.
Sichere die beiden Dateien keys.txt sofort in Deinem Passwortmanager. Gehen alle privaten Schlüssel verloren, sind die verschlüsselten Dateien nicht mehr zu öffnen.
Die Regeln im Repository festlegen
Lege im Hauptordner Deines Repositorys die Datei .sops.yaml an. Sie sagt sops, welche Dateien mit welchen Schlüsseln verschlüsselt werden:
creation_rules:
- path_regex: \.env$
age: >-
age1macschluessel0000000000000000000000000000000000000000000,
age1serverschluessel000000000000000000000000000000000000000Ersetze die beiden Platzhalter durch Deine öffentlichen Schlüssel. path_regex ist ein regulärer Ausdruck, der hier auf alle Dateien passt, deren Name auf .env endet. Die Datei enthält nur öffentliche Schlüssel und darf ins Repository.
Die .env-Datei verschlüsseln
Angenommen, Deine .env sieht so aus:
POSTGRES_PASSWORD=sehr-geheim-123
SMTP_PASSWORD=noch-geheimer-456
APP_URL=https://app.example.comVerschlüssle sie in eine neue Datei:
sops encrypt .env > secrets.enc.envsops erkennt das Format an der Endung .env, findet über .sops.yaml die Empfänger und schreibt das Ergebnis in secrets.enc.env. Der Inhalt sieht danach etwa so aus, gekürzt:
POSTGRES_PASSWORD=ENC[AES256_GCM,data:k3Jv…,type:str]
SMTP_PASSWORD=ENC[AES256_GCM,data:9xQa…,type:str]
APP_URL=ENC[AES256_GCM,data:Hc0p…,type:str]
sops_age__list_0__map_recipient=age1mac…Die Variablennamen bleiben lesbar, die Werte sind verschlüsselt, und am Ende stehen Angaben zu den Empfängern. Sorge jetzt dafür, dass die Klartext-Datei nie im Repository landet, und committe die verschlüsselte:
echo ".env" >> .gitignore
git add .gitignore .sops.yaml secrets.enc.env
git commit -m "Verschlüsselte Umgebungsvariablen"Werte ändern
Zum Bearbeiten musst Du die Datei nicht von Hand entschlüsseln. sops öffnet sie entschlüsselt in Deinem Editor und verschlüsselt sie beim Speichern wieder:
EDITOR=nano sops edit secrets.enc.envÄndere einen Wert, speichere und schließe den Editor. Im Git-Diff siehst Du danach, dass sich genau diese eine Zeile geändert hat, aber nicht, wie der neue Wert lautet.
Auf dem Server mit Docker Compose nutzen
Auf dem Server klonst Du das Repository wie gewohnt. In Deiner compose.yml verwendest Du die Variablen mit der üblichen Schreibweise:
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}Zum Starten lässt Du sops die Werte entschlüsseln und direkt an Docker Compose übergeben:
sops exec-env secrets.enc.env 'docker compose up -d'exec-env entschlüsselt die Datei im Arbeitsspeicher, setzt die Werte als Umgebungsvariablen und startet damit den angegebenen Befehl. Auf der Festplatte des Servers liegt zu keinem Zeitpunkt eine Klartext-Datei. Prüfe mit docker compose config, ob die Werte ankommen. Rufe auch diesen Befehl über sops exec-env auf, sonst sind die Variablen leer.
Brauchst Du doch eine echte .env-Datei, weil ein Programm sie zwingend einliest, entschlüsselst Du sie und schützt sie anschließend:
sops decrypt secrets.enc.env > .env
chmod 600 .envNutzt Du Coolify, verwaltet Coolify die Umgebungsvariablen in seiner eigenen Oberfläche. Das verschlüsselte Repository ist dann Deine versionierte Quelle, aus der Du Werte bei Bedarf überträgst.
Einen weiteren Schlüssel hinzufügen
Kommt ein neuer Server oder ein Kollege dazu, erzeugt er sich ein Schlüsselpaar und schickt Dir seinen öffentlichen Schlüssel. Ergänze ihn in .sops.yaml mit einem Komma in der Liste und aktualisiere dann die verschlüsselte Datei:
sops updatekeys secrets.enc.envsops zeigt Dir die Änderungen an und fragt nach einer Bestätigung. Um jemandem den Zugang zu entziehen, entfernst Du seinen Schlüssel auf dieselbe Weise. Wichtig dabei: Wer die Datei vorher entschlüsseln konnte, kennt die alten Werte. Nach dem Entzug änderst Du deshalb auch die Passwörter selbst.
Häufige Fehlermeldungen
no matching creation rules found. Der Dateiname passt nicht zu path_regex in der .sops.yaml, oder sops findet die Datei nicht, weil Du in einem anderen Ordner bist. sops sucht .sops.yaml im aktuellen Ordner und in den Ordnern darüber.
failed to get the data key required to decrypt the SOPS file. sops findet keinen passenden privaten Schlüssel. Auf dem Mac liegt die Datei meist noch unter ~/.config/sops/age/, wo sops sie nicht sucht. Verschiebe sie nach ~/Library/Application Support/sops/age/ oder setze die Umgebungsvariable SOPS_AGE_KEY_FILE auf den Pfad.
Die Klartext-.env ist schon einmal in Git gelandet. Dann liegt sie in der Historie, auch wenn Du sie später löschst. Betrachte alle darin enthaltenen Passwörter als bekannt, ändere sie und verschlüssle erst danach neu.
Unterm Strich
sops und age kosten Dich eine Viertelstunde Einrichtung und lösen ein Problem, das fast jedes Selfhosting-Projekt hat: Die Zugangsdaten sind versioniert, nachvollziehbar und trotzdem geschützt. Ein neuer Server ist damit in Minuten aufgesetzt, denn Repository klonen, Schlüssel ablegen und sops exec-env reichen. Wenn Du den Server dafür ohnehin neu aufsetzt, hilft Dir die Entscheidungshilfe Proxmox: Docker im LXC-Container oder in einer VM? bei der Grundlage.
Weiterlesen.
-
Apple MacMac für Entwickler einrichten: der Leitfaden für 2026
Neuen Mac fürs Programmieren einrichten: Homebrew, Git, SSH, GitHub, VS Code, Python, Docker, Claude Code und ein Brewfile für den nächsten Mac.
Nathan Base -
Raspberry Pi & LinuxProxmox: Docker im LXC-Container oder in einer VM?
Docker unter Proxmox VE 9.2: wann ein LXC-Container reicht, warum eine VM meist die bessere Wahl ist und wie Du die Docker-VM richtig einrichtest.
Nathan Base -
Web & CodeAuthentik einrichten: ein Login für alle eigenen Dienste
Single Sign-on für Selfhosting: Authentik mit Docker installieren, absichern und Immich per OpenID Connect anbinden, damit ein Konto für alles reicht.
Nathan Base -
Web & CodeForgejo: eigener Git-Server mit Docker statt GitHub
Forgejo mit Docker Compose installieren, per SSH nutzen und Repositories von GitHub übernehmen, für Code und Konfigurationen, die privat bleiben.
Nathan Base -
Web & CodeStirling-PDF selbst hosten: PDF-Werkzeuge ohne Upload
PDFs zusammenführen, verkleinern, schwärzen und per OCR durchsuchbar machen, ohne Upload zu Online-Diensten: Stirling-PDF mit Docker einrichten.
Nathan Base -
Web & CodeDocker-Container aktualisieren: Diun statt Watchtower
Watchtower ist eingestellt. Mit Diun und ntfy erfährst Du von neuen Docker-Images und aktualisierst Container kontrolliert statt blind.
Nathan Base