ein Template, viele Stages, null manuelle Kopien
Ein Config Generator ersetzt handgepflegte Kopien von .env-Dateien und Server-Konfigurationen durch ein einziges Template pro Format, kombiniert mit klar getrennten Variablen je Umgebung. Dieser Artikel zeigt, wie ein solcher Config Generator mit envsubst, strikter Secrets-Trennung und einer Validierungsstufe in Bash entsteht, sodass Entwicklung, Staging und Produktion nie wieder aus dem Ruder laufen.
Inhaltsverzeichnis
- 1. Warum ein Config Generator für mehrere Umgebungen
- 2. Grundprinzip: Template, Variablen, Overrides
- 3. Template-Syntax mit envsubst
- 4. Variablen-Dateien pro Umgebung strukturieren
- 5. Secrets getrennt von normalen Variablen behandeln
- 6. Generierte Konfiguration validieren
- 7. Mehrere Zieldateien aus einer Quelle generieren
- 8. Kommandozeilen-Interface des Config Generators
- 9. Config Generator im Vergleich zu Alternativen
- 10. Zusammenfassung
- 11. FAQ
1. Warum ein Config Generator für mehrere Umgebungen
Sobald ein Projekt mehr als eine Umgebung betreibt, entsteht fast automatisch eine Sammlung fast identischer Konfigurationsdateien: .env.dev, .env.staging, .env.production, jeweils von Hand gepflegt und bei jeder Änderung an drei Stellen gleichzeitig zu aktualisieren. Ein Config Generator löst dieses Problem, indem er genau ein Template pro Dateiformat pflegt und die tatsächlichen Werte pro Umgebung aus separaten Variablen-Dateien einspeist.
Der Nutzen eines Config Generators zeigt sich besonders, wenn eine neue Konfigurationsoption hinzukommt: Statt drei Dateien parallel zu pflegen, wird die Variable einmal im Template ergänzt, und der Config Generator übernimmt automatisch die Verteilung auf alle Umgebungen. Fehler durch vergessene Kopien, wie eine Produktionsdatei, die eine neue Variable nicht kennt, verschwinden dadurch strukturell.
In diesem Artikel entsteht ein Config Generator auf Basis von envsubst, dem GNU-gettext-Werkzeug zum Ersetzen von Umgebungsvariablen in Textdateien. Es ist in den meisten Linux-Distributionen bereits vorinstalliert und benötigt keine zusätzliche Laufzeitumgebung, was den Config Generator auch in minimalen Container-Images sofort einsatzbereit macht.
2. Grundprinzip: Template, Variablen, Overrides
Das Grundprinzip jedes Config Generators besteht aus drei Schichten: Ein Template mit Platzhaltern in der Form ${VARIABLE_NAME}, eine Basisdatei mit gemeinsamen Werten für alle Umgebungen, und eine umgebungsspezifische Override-Datei, die nur die abweichenden Werte enthält. Diese Schichtung reduziert Redundanz auf ein Minimum, weil gemeinsame Werte wie der Anwendungsname nur einmal gepflegt werden müssen.
Ein Config Generator lädt beim Aufruf zuerst die Basiswerte, danach die umgebungsspezifischen Overrides, sodass spätere Werte frühere überschreiben. Diese Reihenfolge ist entscheidend: Würde man sie vertauschen, könnten produktionsspezifische Werte versehentlich von generischen Standardwerten überschrieben werden, was in der Praxis zu schwer nachvollziehbaren Fehlkonfigurationen führt.
3. Template-Syntax mit envsubst
envsubst ersetzt in einer Eingabedatei jede ${VARIABLE} oder $VARIABLE Referenz durch den aktuellen Wert der gleichnamigen Umgebungsvariable. Ein Config Generator nutzt dieses Werkzeug, weil es deutlich robuster ist als eigene sed-Ersetzungen und automatisch mit Sonderzeichen in Werten wie Slashes oder Ampersands umgeht, die bei sed sonst escaped werden müssten.
Wichtig ist die Einschränkung von envsubst auf genau die benötigten Variablen mit dem Argument envsubst '$DB_HOST,$DB_PORT,$APP_ENV'. Ohne diese Einschränkung würde der Config Generator versehentlich jede vorhandene Shell-Variable im Prozess ersetzen, einschließlich technischer Variablen wie $PATH, falls das Template zufällig ein $PATH-ähnliches Muster enthält.
#!/usr/bin/env bash
# app.env.template — placeholder file processed by envsubst
APP_ENV=${APP_ENV}
APP_DEBUG=${APP_DEBUG}
DB_HOST=${DB_HOST}
DB_PORT=${DB_PORT}
DB_NAME=${DB_NAME}
CACHE_DRIVER=${CACHE_DRIVER}
API_BASE_URL=${API_BASE_URL}
4. Variablen-Dateien pro Umgebung strukturieren
Ein bewährtes Verzeichnislayout für einen Config Generator trennt Basiswerte und umgebungsspezifische Overrides klar voneinander: config/base.env für gemeinsame Werte, config/dev.env, config/staging.env und config/production.env für die jeweiligen Abweichungen. Jede dieser Dateien enthält nur klassische KEY=VALUE Zeilen, die sich mit set -a; source datei.env; set +a sicher in Umgebungsvariablen laden lassen.
Der Config Generator sollte beim Laden jeder Variablen-Datei defensiv vorgehen: Kommentarzeilen mit # überspringen, leere Zeilen ignorieren und bei fehlenden Pflichtvariablen nach dem Laden aller Schichten sofort abbrechen, statt mit einer leeren Variable im generierten Ergebnis fortzufahren.
#!/usr/bin/env bash
# load-env-layers.sh — load base config, then environment-specific overrides
set -euo pipefail
load_layer() {
local file="$1"
if [[ -f "$file" ]]; then
echo "[LOAD] $file" >&2
set -a
# shellcheck disable=SC1090
source "$file"
set +a
else
echo "[SKIP] $file not found" >&2
fi
}
target_env="${1:?Usage: load-env-layers.sh <dev|staging|production>}"
load_layer "config/base.env"
load_layer "config/${target_env}.env"
: "${DB_HOST:?DB_HOST missing after loading all layers}"
: "${DB_NAME:?DB_NAME missing after loading all layers}"
5. Secrets getrennt von normalen Variablen behandeln
Datenbank-Passwörter, API-Keys und andere Geheimnisse gehören nicht in dieselben Variablen-Dateien wie normale Konfigurationswerte, selbst wenn beide am Ende im selben generierten Ergebnis landen. Ein sauberer Config Generator lädt Secrets aus einer separaten, nicht versionierten Datei oder direkt aus einem Secrets-Manager wie Vault oder AWS Secrets Manager, während die restlichen Variablen ganz normal im Git-Repository liegen dürfen.
Diese Trennung erlaubt es, die Template-Dateien und die nicht sensiblen Variablen-Dateien öffentlich im Repository zu versionieren, während der Config Generator die Secrets erst zur Laufzeit aus einer geschützten Quelle nachlädt. So bleibt die volle Historie der Konfigurationsänderungen nachvollziehbar, ohne dass jemals ein Passwort im Commit-Log landet.
#!/usr/bin/env bash
# load-secrets.sh — pull secrets separately, never commit them
set -euo pipefail
target_env="${1:?Usage: load-secrets.sh <dev|staging|production>}"
readonly SECRETS_FILE="/etc/mironsoft/secrets/${target_env}.env"
if [[ ! -f "$SECRETS_FILE" ]]; then
echo "[ERROR] Secrets file not found: ${SECRETS_FILE}" >&2
echo " This file must be provisioned outside of git." >&2
exit 1
fi
if [[ "$(stat -c '%a' "$SECRETS_FILE")" != "600" ]]; then
echo "[ERROR] Secrets file has unsafe permissions, expected 600" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source "$SECRETS_FILE"
set +a
6. Generierte Konfiguration validieren
Ein Config Generator, der eine syntaktisch fehlerhafte Konfiguration erzeugt, ist schlimmer als kein Generator, weil der Fehler oft erst beim Start der Anwendung sichtbar wird. Deshalb sollte jede generierte Datei direkt nach der Erzeugung validiert werden: Für .env-Dateien reicht ein einfacher Regex-Check auf gültige KEY=VALUE Zeilen, für docker-compose.yml lohnt sich ein Aufruf von docker compose config, das die Datei parst, ohne etwas zu starten.
Ein zusätzlicher, oft übersehener Validierungsschritt im Config Generator: die Prüfung, ob nach der Ersetzung noch unaufgelöste Platzhalter wie ${UNDEFINED_VAR} im Ergebnis übrig geblieben sind. Das deutet fast immer auf eine fehlende Variable in einer der Schichten hin und sollte den gesamten Generierungslauf mit einem klaren Fehler abbrechen.
#!/usr/bin/env bash
# validate-generated-config.sh — catch broken output before deployment
set -euo pipefail
file="$1"
# Detect leftover, unresolved placeholders
if grep -qE '\$\{[A-Z_]+\}' "$file"; then
echo "[ERROR] Unresolved placeholders found in ${file}:" >&2
grep -oE '\$\{[A-Z_]+\}' "$file" | sort -u >&2
exit 1
fi
# Basic KEY=VALUE sanity check for .env files
if [[ "$file" == *.env ]]; then
while IFS= read -r line; do
[[ -z "$line" || "$line" == \#* ]] && continue
if [[ ! "$line" =~ ^[A-Z_][A-Z0-9_]*=.*$ ]]; then
echo "[ERROR] Invalid line in ${file}: ${line}" >&2
exit 1
fi
done < "$file"
fi
echo "[OK] ${file} passed validation"
7. Mehrere Zieldateien aus einer Quelle generieren
In realen Projekten reicht selten eine einzige Konfigurationsdatei. Ein vollständiger Config Generator erzeugt aus derselben Variablenbasis mehrere Ausgabeformate: eine .env-Datei für die Anwendung, eine docker-compose.override.yml für lokale Container-Anpassungen und eine nginx.conf für den Webserver. Der entscheidende Vorteil: Alle drei Dateien stammen aus derselben Wahrheit, sodass Datenbank-Host oder API-Basis-URL nie zwischen den Formaten auseinanderlaufen.
Der Config Generator iteriert dafür über eine Liste von Template-Zieldatei-Paaren und ruft für jedes Paar envsubst mit denselben geladenen Umgebungsvariablen auf. Das hält die Logik zentral und macht das Hinzufügen eines weiteren Formats, etwa eine supervisord.conf, zu einer einzeiligen Ergänzung der Paar-Liste.
#!/usr/bin/env bash
# generate-all.sh — render every target format from the same variable set
set -euo pipefail
declare -A targets=(
[templates/app.env.template]="dist/app.env"
[templates/docker-compose.override.yml.template]="dist/docker-compose.override.yml"
[templates/nginx.conf.template]="dist/nginx.conf"
)
readonly VARS='$APP_ENV,$APP_DEBUG,$DB_HOST,$DB_PORT,$DB_NAME,$CACHE_DRIVER,$API_BASE_URL'
for template in "${!targets[@]}"; do
output="${targets[$template]}"
mkdir -p "$(dirname "$output")"
envsubst "$VARS" < "$template" > "$output"
bash validate-generated-config.sh "$output"
echo "[OK] Generated ${output} from ${template}"
done
8. Kommandozeilen-Interface des Config Generators
Ein durchdachter Config Generator braucht ein einfaches CLI, das die Zielumgebung als Argument entgegennimmt und den gesamten Ablauf orchestriert: Basiswerte laden, Overrides laden, Secrets laden, alle Zieldateien generieren und validieren. Ein einziger Aufruf wie ./generate-config.sh production sollte genügen, um den vollständigen Satz an Konfigurationsdateien für eine Umgebung zu erzeugen.
Für den lokalen Entwicklungsalltag lohnt sich zusätzlich ein --dry-run Flag im Config Generator, das die generierten Dateien nur anzeigt statt sie zu schreiben. So können Entwickler vor dem eigentlichen Deployment prüfen, ob die richtigen Werte für die gewählte Umgebung tatsächlich einfließen.
9. Config Generator im Vergleich zu Alternativen
Es gibt spezialisierte Konfigurationswerkzeuge wie Consul-Template oder Ansible-Templating, die ähnliche Probleme lösen, aber zusätzliche Infrastruktur oder Agenten voraussetzen. Ein Config Generator auf Basis von envsubst benötigt dagegen nur ein einziges, praktisch überall vorinstalliertes Kommandozeilenwerkzeug.
| Ansatz | Zusätzliche Infrastruktur | Secrets-Trennung | Geeignet für |
|---|---|---|---|
| Manuelle .env-Kopien | Keine | Häufig vermischt | Sehr kleine Projekte |
| Bash Config Generator (envsubst) | Keine, nur gettext-base | Explizit getrennt | Container, Deployment-Skripte |
| Consul-Template | Consul-Cluster nötig | Über Consul KV | Dynamische Service-Discovery-Setups |
| Ansible-Templating | Ansible-Control-Node | Über Vault-Integration | Vorhandene Ansible-Infrastruktur |
Für Teams, die keine zusätzliche Infrastruktur betreiben wollen, bleibt ein einfacher Config Generator auf Bash-Basis die pragmatischste Lösung, gerade weil er in jedem Container-Build-Schritt ohne weitere Vorbereitung läuft.
Mironsoft
Shell-Automatisierung, Konfigurationsmanagement und Deployment-Infrastruktur
Ein Config Generator für all eure Umgebungen?
Wir bauen einen Config Generator, der Templates, Umgebungsvariablen und Secrets sauber trennt und für Dev, Staging und Produktion konsistente Konfigurationsdateien erzeugt.
Skript-Entwicklung
Individueller Config Generator passend zu eurem Stack
Secrets-Management
Saubere Trennung von Konfiguration und sensiblen Werten
CI-Integration
Automatische Generierung und Validierung in der Deployment-Pipeline
10. Zusammenfassung
Ein Config Generator auf Basis von envsubst reduziert die Pflege mehrerer Umgebungen auf ein einziges Template pro Format und klar geschichtete Variablen-Dateien. Basiswerte, umgebungsspezifische Overrides und getrennt behandelte Secrets sorgen dafür, dass jede Umgebung konsistent bleibt, ohne dass jemand mehrere Kopien synchron halten muss.
Validierung direkt nach der Generierung fängt fehlerhafte Ausgaben ab, bevor sie in einer Anwendung zu kryptischen Startfehlern führen. Wer den Config Generator zusätzlich so aufbaut, dass er mehrere Zielformate aus derselben Datenquelle erzeugt, eliminiert eine ganze Klasse von Inkonsistenzen zwischen Anwendungs-, Container- und Webserver-Konfiguration.
Config Generator mit Bash Templates — Das Wichtigste auf einen Blick
Schichtung
Basiswerte, umgebungsspezifische Overrides und Secrets werden strikt getrennt geladen.
Template-Engine
envsubst ersetzt Platzhalter robust, ohne eigene sed-Escaping-Logik zu benötigen.
Validierung
Unaufgelöste Platzhalter und fehlerhafte KEY=VALUE Zeilen werden vor dem Deployment erkannt.
Mehrere Formate
Eine Datenquelle speist .env, docker-compose und Server-Konfiguration gleichzeitig, ohne Drift.