Sicherung und Rückweg
Datenbank, Schlüssel, Abläufe. Fehlt eines davon, ist die Sicherung im Ernstfall wertlos — und das merkt man genau dann.
Der Befehl
sudo bash automatisierung/n8n-sichern.sh
# zusätzlich verschlüsselt:
sudo bash automatisierung/n8n-sichern.sh --passwortDas Skript legt Umgebung (mit Schlüssel), Datenbankabzug, alle Abläufe als JSON und eine Wiederherstellungsanleitung in eine Datei. Was es nicht sichert, sind die Daten der Fremdsysteme — Postfach, Anwendungsdatenbank, Dateiablagen. Die haben ihre eigene Sicherung.
Das Sicherungsskript zum Mitlesen n8n-sichern.sh 154 Zeilen — Kopierknopf oben rechts
#!/usr/bin/env bash
#
# n8n-sichern.sh — Datenbank, Schlüssel und Abläufe in eine Datei.
#
# n8n verschlüsselt alle hinterlegten Zugänge mit einem einzigen Schlüssel:
# N8N_ENCRYPTION_KEY. Wer die Datenbank sichert, diesen Schlüssel aber nicht,
# hat eine Sicherung, die sich nicht mehr öffnen lässt — die Daten sind
# vorhanden und unlesbar. Deshalb legt dieses Skript beides zusammen.
#
# sudo bash n8n-sichern.sh
# sudo bash n8n-sichern.sh --passwort (zusätzlich verschlüsselt)
# sudo bash n8n-sichern.sh --ziel /root/umschlag.tar.gz --leise
#
# Es ändert nichts an der laufenden Installation und liest nur.
# Der Umschlag gehört VOM SERVER HERUNTER — sonst ist er im Ernstfall
# genau dort, wo er nicht mehr erreichbar ist.
set -Eeuo pipefail
VERZEICHNIS="/opt/n8n"
ZIEL=""
VERSCHLUESSELN=0
LEISE=0
BEHALTEN=14 # so viele Sicherungen bleiben im Verzeichnis liegen
if [[ -t 1 ]]; then
F_OK=$'\e[32m'; F_WARN=$'\e[33m'; F_LEISE=$'\e[2m'; F_AUS=$'\e[0m'
else
F_OK=""; F_WARN=""; F_LEISE=""; F_AUS=""
fi
ok() { (( LEISE )) || printf ' %s✓%s %s\n' "$F_OK" "$F_AUS" "$1"; }
info() { (( LEISE )) || printf ' %s·%s %s\n' "$F_LEISE" "$F_AUS" "$1"; }
warnung() { printf ' %s!%s %s\n' "$F_WARN" "$F_AUS" "$1" >&2; }
abbruch() { printf '\n✗ %s\n Nächster Schritt: %s\n\n' "$1" "$2" >&2; exit 1; }
while [[ $# -gt 0 ]]; do
case "$1" in
--verzeichnis) VERZEICHNIS="${2:?}"; shift 2 ;;
--ziel) ZIEL="${2:?}"; shift 2 ;;
--passwort) VERSCHLUESSELN=1; shift ;;
--leise) LEISE=1; shift ;;
-h|--hilfe|--help) sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) abbruch "Unbekannte Angabe: $1" "bash $0 --hilfe" ;;
esac
done
COMPOSE="${VERZEICHNIS}/docker-compose.yml"
UMGEBUNG="${VERZEICHNIS}/.env"
[[ -f "$COMPOSE" ]] || abbruch "${COMPOSE} fehlt." "Verzeichnis angeben: --verzeichnis /pfad/zu/n8n"
[[ -r "$UMGEBUNG" ]] || abbruch "${UMGEBUNG} nicht lesbar." "Mit sudo ausführen."
STAND="$(date '+%Y-%m-%d_%H%M')"
ORDNER="${VERZEICHNIS}/sicherung"
[[ -n "$ZIEL" ]] || ZIEL="${ORDNER}/n8n-${STAND}.tar.gz"
mkdir -p "$ORDNER"
ARBEIT="$(mktemp -d)"
trap 'rm -rf "$ARBEIT"' EXIT
PAKET="${ARBEIT}/n8n-${STAND}"
mkdir -p "$PAKET"
dc() { docker compose -f "$COMPOSE" "$@"; }
umg() { grep -E "^${1}=" "$UMGEBUNG" | head -1 | cut -d= -f2-; }
# 1 · Der Schlüssel und die Umgebung
cp "$UMGEBUNG" "${PAKET}/umgebung.env"
chmod 600 "${PAKET}/umgebung.env"
ok "Umgebung samt Verschlüsselungsschlüssel übernommen"
# 2 · Die Datenbank
DBID="$(dc ps -q db 2>/dev/null | head -1)"
if [[ -n "$DBID" ]]; then
if docker exec "$DBID" pg_dump -U "$(umg POSTGRES_USER)" -d "$(umg POSTGRES_DB)" \
> "${PAKET}/datenbank.sql" 2>/dev/null; then
ok "Datenbank gesichert ($(du -h "${PAKET}/datenbank.sql" | cut -f1))"
else
warnung "pg_dump fehlgeschlagen — Sicherung ist unvollständig"
fi
else
warnung "Datenbank-Container läuft nicht — keine Datenbanksicherung"
fi
# 3 · Die Abläufe als lesbare Dateien (ohne Zugangsdaten)
N8NID="$(dc ps -q n8n 2>/dev/null | head -1)"
if [[ -n "$N8NID" ]]; then
mkdir -p "${PAKET}/ablaeufe"
if docker exec "$N8NID" n8n export:workflow --all --pretty --separate --output=/ablaeufe/ >/dev/null 2>&1; then
cp -r "${VERZEICHNIS}/ablaeufe/." "${PAKET}/ablaeufe/" 2>/dev/null || true
ok "Abläufe als JSON gesichert ($(find "${PAKET}/ablaeufe" -name '*.json' | wc -l | tr -d ' ') Dateien)"
info "Zugangsdaten sind darin NICHT enthalten — sie stehen verschlüsselt in der Datenbank."
else
warnung "Export der Abläufe fehlgeschlagen — die Datenbanksicherung enthält sie trotzdem"
fi
fi
# 4 · Anleitung zum Zurückspielen
cat > "${PAKET}/ANLEITUNG.txt" <<TXT
Wiederherstellung — n8n, Stand ${STAND}
Voraussetzung: ein Server mit Docker, wie in Kurs 1 aufgesetzt.
1. Verzeichnis anlegen und diese Dateien hineinlegen:
mkdir -p /opt/n8n && cd /opt/n8n
2. Umgebung zurückspielen — MIT dem alten Verschlüsselungsschlüssel:
cp umgebung.env /opt/n8n/.env && chmod 600 /opt/n8n/.env
Ein anderer Schlüssel macht alle hinterlegten Zugänge unlesbar.
Das ist der häufigste Fehler beim Zurückspielen.
3. docker-compose.yml aus dem Repository holen (oder n8n-aufsetzen.sh laufen
lassen — es lässt eine bestehende .env unangetastet).
4. Nur die Datenbank starten und den Abzug einspielen:
docker compose up -d db
docker compose exec -T db psql -U n8n -d n8n < datenbank.sql
5. n8n starten und anmelden:
docker compose up -d
6. Die Probe: einen Ablauf von Hand auslösen und im Protokoll nachsehen,
ob er durchläuft. Erst dann war die Sicherung eine.
Was NICHT in diesem Paket ist: die Daten der Fremdsysteme (Postfach,
Datenbank der Anwendung, Dateiablagen). Die haben ihre eigene Sicherung.
TXT
# 5 · Einpacken
tar -czf "${ARBEIT}/paket.tar.gz" -C "$ARBEIT" "n8n-${STAND}"
if (( VERSCHLUESSELN )); then
command -v openssl >/dev/null || abbruch "openssl fehlt." "Ohne --passwort erneut ausführen."
openssl enc -aes-256-cbc -pbkdf2 -salt -in "${ARBEIT}/paket.tar.gz" -out "${ZIEL}.enc"
ZIEL="${ZIEL}.enc"
else
mv "${ARBEIT}/paket.tar.gz" "$ZIEL"
fi
chmod 600 "$ZIEL"
ok "Sicherung liegt hier: ${ZIEL} ($(du -h "$ZIEL" | cut -f1))"
# 6 · Alte Sicherungen aufräumen
ANZAHL="$(ls -1t "${ORDNER}"/n8n-*.tar.gz* 2>/dev/null | wc -l | tr -d ' ')"
if (( ANZAHL > BEHALTEN )); then
ls -1t "${ORDNER}"/n8n-*.tar.gz* | tail -n +$((BEHALTEN+1)) | xargs -r rm -f
info "Alte Sicherungen entfernt — die letzten ${BEHALTEN} bleiben liegen"
fi
(( LEISE )) || cat <<TXT
Der wichtigste Schritt fehlt noch: die Datei vom Server holen.
scp betreiber@$(umg DOMAIN 2>/dev/null || hostname):${ZIEL} ~/Desktop/
Und einmal im Quartal: auf einen leeren Container zurückspielen.
Eine Sicherung, die nicht zurückgespielt wurde, ist keine.
TXT
Die Probe — einmal im Quartal
Einen leeren Container nehmen
Ein zweiter Server, eine zweite Maschine, ein leeres Verzeichnis. Nicht der laufende — es geht darum, ob die Sicherung allein trägt.
Umgebung zurückspielen — mit dem alten Schlüssel
Das ist die Stelle, an der es schiefgeht. Ein anderer Verschlüsselungsschlüssel macht jeden hinterlegten Zugang unlesbar; n8n meldet dann bei jedem Ablauf „credentials could not be decrypted".
Datenbank einspielen und starten
docker compose up -d db docker compose exec -T db psql -U n8n -d n8n < datenbank.sql docker compose up -d
Einen Ablauf von Hand auslösen
Läuft er durch, war die Sicherung eine. Läuft er nicht, haben Sie es an einem Dienstagvormittag gemerkt und nicht im Ernstfall.
- Prüfliste Sicherung