Multi Environment Config Generator mit Bash Templates
AI generated
$_
#!/
Bash · Konfiguration · DevOps · Real World
Multi Environment Config Generator
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.

18 Min. Lesezeit envsubst · Templates · .env · docker-compose Bash 4.x · 5.x · gettext-base

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.

11. FAQ: Multi Environment Config Generator mit Bash

1Was ist ein Config Generator?
Ein Skript, das aus Template und Variablen automatisch Konfigurationsdateien je Umgebung erzeugt, statt Kopien manuell zu pflegen.
2Warum envsubst statt sed?
Es geht robust mit Sonderzeichen um und ist auf fast jedem Linux-System bereits vorinstalliert.
3Wie werden ungewollte Ersetzungen verhindert?
Über eine explizite Variablenliste als Argument für envsubst, statt alle Umgebungsvariablen zuzulassen.
4Wie werden Secrets behandelt?
Getrennt von normalen Werten, aus einer nicht versionierten Datei oder einem Secrets-Manager geladen.
5Wie wird die Ausgabe validiert?
Prüfung auf unaufgelöste Platzhalter, danach formatspezifische Checks wie docker compose config.
6Mehrere Formate aus einer Quelle?
Ja, über Template-Zieldatei-Paare, die alle aus derselben Variablenbasis gerendert werden.
7Fehlende Pflichtvariable?
Explizite Prüfung mit : "${VAR:?Meldung}" bricht sofort ab, statt eine leere Variable zu übernehmen.
8Reihenfolge von Basis und Overrides?
Erst Basiswerte, dann umgebungsspezifische Overrides, damit spätere Werte gezielt frühere überschreiben.
9Zusätzliche Laufzeitumgebung nötig?
Nein, envsubst gehört zu gettext-base und ist meist schon vorhanden.
10Lohnt sich --dry-run?
Ja, damit sich Werte vor dem Deployment prüfen lassen, ohne Dateien zu überschreiben.