From 87cd0053528e7966ed7e87234c76a0373f71fe0d Mon Sep 17 00:00:00 2001 From: dschlueter Date: Tue, 5 May 2026 17:31:59 +0200 Subject: [PATCH] feat: complete n8n stack setup Docker Compose stack (n8n, PostgreSQL 16, Redis 7, 1 worker) with: - nginx local proxy on port 8088, YunoHost TLS termination config - helper scripts: backup/restore/import/export/update - .env.example, README with architecture, ops commands, to-do list Co-Authored-By: Claude Sonnet 4.6 --- .env.example | 35 +++++++++ README.md | 142 +++++++++++++++++++++++++++++++++- compose/docker-compose.yml | 118 ++++++++++++++++++++++++++++ docs/nginx-local-n8n.conf | 28 +++++++ docs/yunohost-nginx-n8n.conf | 49 ++++++++++++ scripts/backup-n8n.sh | 48 ++++++++++++ scripts/export-credentials.sh | 19 +++++ scripts/export-workflows.sh | 17 ++++ scripts/import-credentials.sh | 24 ++++++ scripts/import-workflow.sh | 35 +++++++++ scripts/restore-n8n.sh | 50 ++++++++++++ scripts/update-n8n.sh | 23 ++++++ 12 files changed, 586 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 compose/docker-compose.yml create mode 100644 docs/nginx-local-n8n.conf create mode 100644 docs/yunohost-nginx-n8n.conf create mode 100755 scripts/backup-n8n.sh create mode 100755 scripts/export-credentials.sh create mode 100755 scripts/export-workflows.sh create mode 100755 scripts/import-credentials.sh create mode 100755 scripts/import-workflow.sh create mode 100755 scripts/restore-n8n.sh create mode 100755 scripts/update-n8n.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1faebfc --- /dev/null +++ b/.env.example @@ -0,0 +1,35 @@ +# ============================================================================= +# n8n Stack — Konfigurationsvorlage +# Kopieren nach .env und alle Werte ausfüllen. +# ============================================================================= + +# --- Domain & URL --- +N8N_HOST=n8n.linix.de +N8N_PROTOCOL=https +WEBHOOK_URL=https://n8n.linix.de/ + +# --- Datenbank --- +POSTGRES_DB=n8n +POSTGRES_USER=n8n +POSTGRES_PASSWORD=HIER_STARKES_PASSWORT_EINTRAGEN + +# --- Redis --- +REDIS_PASSWORD=HIER_STARKES_PASSWORT_EINTRAGEN + +# --- n8n Encryption Key --- +# KRITISCH: Einmalig generieren (z.B. openssl rand -base64 32) und nie mehr ändern! +# Änderung macht alle gespeicherten Credentials unbrauchbar. +N8N_ENCRYPTION_KEY=HIER_ZUFALLSKEY_EINTRAGEN + +# --- SMTP --- +SMTP_HOST=linix.de +SMTP_PORT=587 +SMTP_USER=nutzer@linix.de +SMTP_PASSWORD=SMTP_PASSWORT +SMTP_SENDER=n8n@linix.de + +# --- Worker --- +N8N_WORKER_REPLICAS=1 + +# --- Zeitzone --- +TZ=Europe/Berlin diff --git a/README.md b/README.md index edeeb01..827d5a6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,141 @@ -# n8n_stack +# n8n Stack -Das Projekt installiert und konfiguriert einen vollständigen, produktionsnahen n8n-Stack auf einem Ubuntu-Linux-System. \ No newline at end of file +Produktionsnaher n8n-Stack auf Docker Compose mit PostgreSQL, Redis und Queue-Mode. + +## Architektur + +``` +Internet → n8n.linix.de:443 + → YunoHost (192.168.179.10) — TLS-Terminierung (Let's Encrypt) + → nginx lokal :8088 (192.168.179.124) + → n8n Docker :5678 + ↔ PostgreSQL (n8n-stack-postgres-1) + ↔ Redis (n8n-stack-redis-1) + ← n8n-worker (n8n-stack-n8n-worker-1) +``` + +## Verzeichnisstruktur + +``` +n8n_stack/ +├── compose/docker-compose.yml # Stack-Definition +├── .env # Secrets (600, nie committen!) +├── .env.example # Vorlage +├── scripts/ # Hilfsskripte (700) +├── data/ # Docker-Volumes (gitignoriert) +│ ├── n8n/ +│ ├── postgres/ +│ └── redis/ +├── backups/ # Backups, max. 2 (gitignoriert) +├── imports/workflows/ # Workflow-JSONs zum Importieren +├── imports/credentials/ # Credentials-Exporte (unverschlüsselt!) +├── local-files/ # Dateien für n8n-Container (/files) +└── docs/ # nginx-Configs, Runbooks +``` + +## Start / Stop / Status + +```bash +# Start +docker compose -f compose/docker-compose.yml up -d + +# Stop +docker compose -f compose/docker-compose.yml down + +# Status +docker compose -f compose/docker-compose.yml ps + +# Logs +docker compose -f compose/docker-compose.yml logs -f n8n + +# Worker skalieren (z.B. auf 2) +docker compose -f compose/docker-compose.yml up -d --scale n8n-worker=2 +``` + +## Update + +```bash +bash scripts/update-n8n.sh +``` + +Führt automatisch ein Backup aus, zieht neue Images und startet den Stack neu. + +## Backup / Restore + +```bash +# Backup erstellen (behält immer die letzten 2) +bash scripts/backup-n8n.sh + +# Restore aus Backup-Verzeichnis +bash scripts/restore-n8n.sh backups/backup_20240101_120000 +``` + +## Import / Export von Workflows + +```bash +# Einzelne Workflow-JSON importieren +bash scripts/import-workflow.sh imports/workflows/mein-workflow.json + +# Ganzes Verzeichnis importieren +bash scripts/import-workflow.sh imports/workflows/ + +# Alle Workflows exportieren +bash scripts/export-workflows.sh +``` + +## Import / Export von Credentials + +```bash +# Credentials importieren +bash scripts/import-credentials.sh imports/credentials/creds.json + +# Credentials exportieren (UNVERSCHLÜSSELT — sicher aufbewahren!) +bash scripts/export-credentials.sh +``` + +**Wichtig:** Credential-Exporte enthalten alle Secrets im Klartext. Export-Dateien nach Gebrauch löschen. + +## Übernahme fremder n8n-Projekte + +1. Workflow-JSON-Dateien nach `imports/workflows/` kopieren +2. `bash scripts/import-workflow.sh imports/workflows/.json` +3. Falls Credentials mitgeliefert: `bash scripts/import-credentials.sh ` +4. In der n8n-UI: Credentials der importierten Workflows prüfen und neu verknüpfen + +Bei Voll-Exporten (`.zip` mit mehreren JSONs): entpacken, dann Verzeichnis übergeben. + +## Deployment auf neuem Host + +1. Repo klonen +2. `.env.example` → `.env` kopieren, alle Werte ausfüllen +3. Neuen `N8N_ENCRYPTION_KEY` generieren: `openssl rand -base64 32` +4. `N8N_HOST` und `WEBHOOK_URL` auf die neue Domain anpassen +5. nginx-Config aus `docs/nginx-local-n8n.conf` anpassen und aktivieren +6. YunoHost-Config aus `docs/yunohost-nginx-n8n.conf` anpassen und installieren +7. `docker compose -f compose/docker-compose.yml up -d` + +## Kritische Hinweise + +- **`N8N_ENCRYPTION_KEY`** darf nach dem ersten Start **nie geändert** werden — eine Änderung macht alle gespeicherten Credentials unbrauchbar. Key steht in `.env`. +- **`.env`** nie committen (steht in `.gitignore`). +- Backup vor jedem Update läuft automatisch via `update-n8n.sh`. + +## URLs und Zugangspfade + +| Was | Wert | +|---|---| +| n8n Web-UI | https://n8n.linix.de | +| Erster Login | Beim ersten Aufruf Owner-Account anlegen | +| Secrets-Datei | `./.env` (Rechte 600) | +| Backup-Verzeichnis | `./backups/` | +| nginx lokal | `/etc/nginx/sites-available/n8n` (Port 8088) | +| YunoHost nginx | `/etc/nginx/conf.d/n8n.linix.de.conf` | + +## To-do (manuell auf YunoHost) + +- [ ] DNS: `n8n.linix.de` → `92.50.108.218` (A-Record beim DNS-Provider) +- [ ] YunoHost: Domain hinzufügen: `sudo yunohost domain add n8n.linix.de` +- [ ] YunoHost: Let's Encrypt: `sudo yunohost domain cert install n8n.linix.de` +- [ ] YunoHost: `docs/yunohost-nginx-n8n.conf` → `/etc/nginx/conf.d/n8n.linix.de.conf` kopieren, dann `sudo nginx -t && sudo systemctl reload nginx` +- [ ] SMTP-Credentials in `.env` eintragen (`SMTP_USER`, `SMTP_PASSWORD`) +- [ ] Ersten Owner-Account in n8n anlegen (https://n8n.linix.de beim ersten Aufruf) diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml new file mode 100644 index 0000000..0e36e4c --- /dev/null +++ b/compose/docker-compose.yml @@ -0,0 +1,118 @@ +name: n8n-stack + +services: + + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - ../data/postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - n8n-internal + + redis: + image: redis:7-alpine + restart: unless-stopped + command: redis-server --requirepass ${REDIS_PASSWORD} + volumes: + - ../data/redis:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - n8n-internal + + n8n: + image: n8nio/n8n:latest + restart: unless-stopped + ports: + - "127.0.0.1:5678:5678" + environment: + # URL & Protokoll + N8N_HOST: ${N8N_HOST} + N8N_PORT: 5678 + N8N_PROTOCOL: ${N8N_PROTOCOL} + WEBHOOK_URL: ${WEBHOOK_URL} + # Datenbank + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: postgres + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} + DB_POSTGRESDB_USER: ${POSTGRES_USER} + DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} + # Encryption + N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} + # Queue / Redis + EXECUTIONS_MODE: queue + QUEUE_BULL_REDIS_HOST: redis + QUEUE_BULL_REDIS_PORT: 6379 + QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD} + # SMTP + N8N_EMAIL_MODE: smtp + N8N_SMTP_HOST: ${SMTP_HOST} + N8N_SMTP_PORT: ${SMTP_PORT} + N8N_SMTP_USER: ${SMTP_USER} + N8N_SMTP_PASS: ${SMTP_PASSWORD} + N8N_SMTP_SENDER: ${SMTP_SENDER} + N8N_SMTP_SSL: "false" + # User Management + N8N_USER_MANAGEMENT_DISABLED: "false" + # Zeitzone + GENERIC_TIMEZONE: ${TZ} + TZ: ${TZ} + volumes: + - ../data/n8n:/home/node/.n8n + - ../local-files:/files + - ../imports:/imports + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - n8n-internal + + n8n-worker: + image: n8nio/n8n:latest + restart: unless-stopped + command: worker + environment: + # Datenbank + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: postgres + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} + DB_POSTGRESDB_USER: ${POSTGRES_USER} + DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} + # Encryption + N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} + # Queue / Redis + EXECUTIONS_MODE: queue + QUEUE_BULL_REDIS_HOST: redis + QUEUE_BULL_REDIS_PORT: 6379 + QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD} + # Zeitzone + GENERIC_TIMEZONE: ${TZ} + TZ: ${TZ} + volumes: + - ../data/n8n:/home/node/.n8n + - ../local-files:/files + depends_on: + - n8n + networks: + - n8n-internal + +networks: + n8n-internal: + driver: bridge diff --git a/docs/nginx-local-n8n.conf b/docs/nginx-local-n8n.conf new file mode 100644 index 0000000..2171cc3 --- /dev/null +++ b/docs/nginx-local-n8n.conf @@ -0,0 +1,28 @@ +# Lokale nginx-Konfiguration auf 192.168.179.124 +# Ablegen als: /etc/nginx/sites-available/n8n +# Aktivieren mit: sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/n8n +# Danach: sudo nginx -t && sudo systemctl reload nginx + +server { + listen 8088; + server_name n8n.linix.de; + + # n8n-Proxy + location / { + proxy_pass http://127.0.0.1:5678; + proxy_http_version 1.1; + + # WebSocket-Support (n8n Editor + Push-Benachrichtigungen) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + client_max_body_size 50m; + } +} diff --git a/docs/yunohost-nginx-n8n.conf b/docs/yunohost-nginx-n8n.conf new file mode 100644 index 0000000..025f8be --- /dev/null +++ b/docs/yunohost-nginx-n8n.conf @@ -0,0 +1,49 @@ +# YunoHost nginx-Konfiguration (auf dem YunoHost-Server 192.168.179.10) +# Ablegen als: /etc/nginx/conf.d/n8n.linix.de.conf +# Danach: sudo nginx -t && sudo systemctl reload nginx +# +# Voraussetzungen auf YunoHost: +# 1. Domain n8n.linix.de in YunoHost hinzufügen: +# sudo yunohost domain add n8n.linix.de +# 2. Let's Encrypt Zertifikat ausstellen: +# sudo yunohost domain cert install n8n.linix.de +# 3. Diese Datei ablegen und nginx neu laden. +# +# Cert-Pfad prüfen mit: ls /etc/yunohost/certs/ + +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name n8n.linix.de; + + ssl_certificate /etc/yunohost/certs/n8n.linix.de/crt.pem; + ssl_certificate_key /etc/yunohost/certs/n8n.linix.de/key.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + location / { + proxy_pass http://192.168.179.124:8088; + proxy_http_version 1.1; + + # WebSocket-Support + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_read_timeout 300s; + proxy_send_timeout 300s; + client_max_body_size 50m; + } +} + +server { + listen 80; + listen [::]:80; + server_name n8n.linix.de; + return 301 https://$host$request_uri; +} diff --git a/scripts/backup-n8n.sh b/scripts/backup-n8n.sh new file mode 100755 index 0000000..432fb94 --- /dev/null +++ b/scripts/backup-n8n.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" +ENV="$ROOT/.env" +BACKUP_DIR="$ROOT/backups" +MAX_BACKUPS=2 + +# shellcheck source=/dev/null +source "$ENV" + +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +TARGET="$BACKUP_DIR/backup_$TIMESTAMP" +mkdir -p "$TARGET" + +echo "[backup] Starte Backup → $TARGET" + +# PostgreSQL dump +echo "[backup] Datenbank-Dump..." +docker compose -f "$COMPOSE" exec -T postgres \ + pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \ + > "$TARGET/postgres.sql" + +# n8n data +echo "[backup] n8n Datendirectory..." +tar -czf "$TARGET/n8n-data.tar.gz" -C "$ROOT/data" n8n + +# Compose-Konfiguration +echo "[backup] Compose + .env..." +cp "$COMPOSE" "$TARGET/docker-compose.yml" +cp "$ENV" "$TARGET/.env" + +echo "[backup] Backup abgeschlossen: $TARGET" + +# Retention: nur die letzten MAX_BACKUPS behalten +EXISTING=$(ls -dt "$BACKUP_DIR"/backup_* 2>/dev/null) +COUNT=$(echo "$EXISTING" | wc -l) +if [ "$COUNT" -gt "$MAX_BACKUPS" ]; then + TO_DELETE=$(echo "$EXISTING" | tail -n +"$((MAX_BACKUPS + 1))") + echo "[backup] Lösche alte Backups:" + echo "$TO_DELETE" + echo "$TO_DELETE" | xargs rm -rf +fi + +echo "[backup] Fertig. Aktuelle Backups:" +ls -lht "$BACKUP_DIR" diff --git a/scripts/export-credentials.sh b/scripts/export-credentials.sh new file mode 100755 index 0000000..9d3b5eb --- /dev/null +++ b/scripts/export-credentials.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" + +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +OUTPUT_DIR="$ROOT/imports/credentials/export_$TIMESTAMP" +mkdir -p "$OUTPUT_DIR" + +echo "[export-credentials] WARNUNG: Export enthält unverschlüsselte Credentials!" +echo "[export-credentials] Exportiere → $OUTPUT_DIR" +docker compose -f "$COMPOSE" exec n8n \ + n8n export:credentials --all --output="/imports/credentials/export_$TIMESTAMP/" + +echo "[export-credentials] Fertig." +echo "[export-credentials] WICHTIG: Export-Verzeichnis sichern und nach Gebrauch löschen!" +ls -lh "$OUTPUT_DIR" diff --git a/scripts/export-workflows.sh b/scripts/export-workflows.sh new file mode 100755 index 0000000..4169ee7 --- /dev/null +++ b/scripts/export-workflows.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" + +TIMESTAMP=$(date +"%Y%m%d_%H%M%S") +OUTPUT_DIR="$ROOT/imports/workflows/export_$TIMESTAMP" +mkdir -p "$OUTPUT_DIR" + +echo "[export-workflows] Exportiere alle Workflows → $OUTPUT_DIR" +docker compose -f "$COMPOSE" exec n8n \ + n8n export:workflow --all --output="/imports/workflows/export_$TIMESTAMP/" + +echo "[export-workflows] Fertig." +ls -lh "$OUTPUT_DIR" diff --git a/scripts/import-credentials.sh b/scripts/import-credentials.sh new file mode 100755 index 0000000..dd8a354 --- /dev/null +++ b/scripts/import-credentials.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" + +if [ -z "${1:-}" ]; then + echo "Verwendung: $0 " + echo "WARNUNG: Credentials-Exporte sind unverschlüsselt. Datei nach Import löschen!" + exit 1 +fi + +SOURCE="$1" +DEST="$ROOT/imports/credentials/$(basename "$SOURCE")" +cp "$SOURCE" "$DEST" + +echo "[import-credentials] Importiere: $(basename "$SOURCE")" +echo "[import-credentials] WARNUNG: Diese Datei enthält unverschlüsselte Credentials!" +docker compose -f "$COMPOSE" exec n8n \ + n8n import:credentials --input="/imports/credentials/$(basename "$SOURCE")" + +echo "[import-credentials] Fertig." +echo "[import-credentials] Empfehlung: Quelldatei jetzt löschen: rm '$SOURCE'" diff --git a/scripts/import-workflow.sh b/scripts/import-workflow.sh new file mode 100755 index 0000000..20f6c0d --- /dev/null +++ b/scripts/import-workflow.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" + +if [ -z "${1:-}" ]; then + echo "Verwendung: $0 " + exit 1 +fi + +SOURCE="$1" + +if [ -d "$SOURCE" ]; then + # Verzeichnis: alle JSON-Dateien importieren + echo "[import-workflow] Importiere alle Workflows aus: $SOURCE" + for f in "$SOURCE"/*.json; do + [ -f "$f" ] || continue + DEST="$ROOT/imports/workflows/$(basename "$f")" + cp "$f" "$DEST" + echo "[import-workflow] → $(basename "$f")" + docker compose -f "$COMPOSE" exec n8n \ + n8n import:workflow --input="/imports/workflows/$(basename "$f")" + done +else + # Einzelne Datei + DEST="$ROOT/imports/workflows/$(basename "$SOURCE")" + cp "$SOURCE" "$DEST" + echo "[import-workflow] Importiere: $(basename "$SOURCE")" + docker compose -f "$COMPOSE" exec n8n \ + n8n import:workflow --input="/imports/workflows/$(basename "$SOURCE")" +fi + +echo "[import-workflow] Fertig." diff --git a/scripts/restore-n8n.sh b/scripts/restore-n8n.sh new file mode 100755 index 0000000..89443ec --- /dev/null +++ b/scripts/restore-n8n.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" +ENV="$ROOT/.env" + +if [ -z "${1:-}" ]; then + echo "Verwendung: $0 " + echo "Verfügbare Backups:" + ls -lht "$ROOT/backups/" + exit 1 +fi + +BACKUP="$1" +if [ ! -d "$BACKUP" ]; then + echo "Fehler: Verzeichnis '$BACKUP' nicht gefunden." + exit 1 +fi + +# shellcheck source=/dev/null +source "$ENV" + +echo "[restore] Stelle wieder her aus: $BACKUP" +echo "[restore] Stack wird gestoppt..." +docker compose -f "$COMPOSE" down + +# n8n data +echo "[restore] n8n Daten..." +rm -rf "$ROOT/data/n8n" +mkdir -p "$ROOT/data/n8n" +tar -xzf "$BACKUP/n8n-data.tar.gz" -C "$ROOT/data" + +# Postgres +echo "[restore] Datenbank wird gestartet..." +docker compose -f "$COMPOSE" up -d postgres +echo "[restore] Warte auf PostgreSQL..." +sleep 10 + +echo "[restore] Datenbank-Restore..." +docker compose -f "$COMPOSE" exec -T postgres \ + psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" \ + < "$BACKUP/postgres.sql" + +echo "[restore] Stack wird neu gestartet..." +docker compose -f "$COMPOSE" up -d + +echo "[restore] Fertig. Stack-Status:" +docker compose -f "$COMPOSE" ps diff --git a/scripts/update-n8n.sh b/scripts/update-n8n.sh new file mode 100755 index 0000000..919360d --- /dev/null +++ b/scripts/update-n8n.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(dirname "$SCRIPT_DIR")" +COMPOSE="$ROOT/compose/docker-compose.yml" + +echo "[update] Starte Pre-Update-Backup..." +bash "$SCRIPT_DIR/backup-n8n.sh" + +echo "[update] Neue Images ziehen..." +docker compose -f "$COMPOSE" pull + +echo "[update] Stack neu starten..." +docker compose -f "$COMPOSE" up -d + +echo "[update] Warte 15s auf Start..." +sleep 15 + +echo "[update] Stack-Status nach Update:" +docker compose -f "$COMPOSE" ps + +echo "[update] Update abgeschlossen."