n8n selbst hosten: Docker-Setup, Produktions-Config und Backups

March 22, 2026 · 10 min read · n8n, self-hosted, automation, docker, dach
n8n selbst hosten: Docker-Setup, Produktions-Config und Backups

n8n selbst hosten klingt nach viel Arbeit, ist es aber nicht. Mit Docker Compose, einem 4 GB VPS und einer Domain bist du in unter 40 Minuten produktiv. Der Rest dieses Artikels behandelt das, was danach kommt: Produktions-Config, Backups, Sicherheit und Skalierung.

Ich betreibe mehrere n8n-Instanzen auf Debian-VPS, mit Postgres als Backend und Caddy als Reverse Proxy. Das Setup läuft ohne manuelle Eingriffe, abgesehen von Upgrades und gelegentlichen Log-Kontrollen. Diese Anleitung ist genau das, was ich auf einem neuen Server einrichten würde, wenn ich heute bei Null anfange.

Wenn du dich erst noch entscheidest, ob n8n oder Make besser zu deinem Use Case passt, schau in den Make vs n8n Vergleich. Wenn du von Zapier migrierst, ist der Zapier-zu-n8n Migrations-Guide die bessere Startseite.

Warum n8n selbst hosten?

Vier Gründe, die in DACH-Projekten immer wieder auftauchen:

Datenhoheit. DSGVO ist kein Nice-to-have, sondern regulatorisch bindend. Self-hosted n8n auf einem EU-Server bedeutet, dass Kundendaten, API-Keys und Webhook-Payloads deinen Server nicht verlassen. Kein Auftragsverarbeitungsvertrag mit einem US-SaaS-Anbieter, keine Third-Country-Transfer-Prüfung, keine Schrems-II-Diskussion mit dem Datenschutzbeauftragten.

Kostenkontrolle. n8n Cloud wird ab einem gewissen Execution-Volumen teuer. Self-hosted zahlst du Server-Miete plus Strom, der Rest ist fix. Auf einem Hetzner CX22 laufen problemlos 50.000 Executions im Monat, die Rechnung bleibt bei unter 10 Euro. Für die gleiche Last zahlst du bei Cloud-Anbietern schnell einen dreistelligen Betrag. Der Break-Even liegt grob bei 5.000 bis 10.000 Executions pro Monat, abhängig von Workflow-Komplexität.

Kein Vendor-Lock-In. Deine Workflows sind JSON-Dateien auf deiner Platte. Deine Credentials liegen verschlüsselt in deiner Postgres-Datenbank. Wenn du morgen umziehst, packst du das Verzeichnis ein und startest den Container woanders neu. Cloud-Anbieter können Preise erhöhen, Features deprecaten oder Konten sperren. Auf deinem eigenen Server entscheidest du über Lifecycle und Roadmap.

Custom Nodes und interne Netzwerke. Self-hosted n8n kann beliebige Community-Nodes laden, interne APIs erreichen, auf On-Premise-Datenbanken im VPN zugreifen. Cloud-n8n kann das nicht. Gerade in DACH-Projekten sitzen geschäftskritische Systeme (Warenwirtschaft, CRM, ERP) oft hinter VPN-only-Endpoints, die von keinem SaaS-Anbieter erreicht werden dürfen.

Wann Self-Hosting NICHT die richtige Wahl ist: Ganz am Ende dieses Artikels findest du eine Liste von Situationen, in denen Cloud der kleinere Aufwand ist. Self-Hosting ist kein Selbstzweck.

Was du brauchst

  • Linux VPS mit 4 GB RAM. Hetzner CX22 für den Einstieg, CX32 wenn du schon weisst, dass es volumenreich wird.
  • Domain mit DNS-Kontrolle. Subdomain reicht, zum Beispiel n8n.deinefirma.de.
  • Docker und Docker Compose auf dem Server. Wenn der VPS leer ist, schau in den Linux-VPS-Setup-Guide für eine komplette Grundinstallation.
  • SSH-Grundkenntnisse. Du musst auf dem Server arbeiten können, ohne ein Tutorial zu googeln.
  • 20 bis 40 Minuten für die erste Installation. Danach sind Updates eine Frage von zwei Befehlen.

Deployment: Docker Compose ist der Sweet Spot

Es gibt drei ernsthafte Wege, n8n selbst zu hosten:

Docker Compose. Alles in einer docker-compose.yml, Reverse Proxy inklusive, Datenbank als Container daneben. Ein docker compose up -d und es läuft. Für 99% der Self-Hoster die richtige Wahl.

Systemd plus Node direkt. Du installierst Node.js auf dem Host, klonst das n8n-Repo, baust es und startest es als systemd-Service. Mehr Kontrolle, mehr manuelle Arbeit, mehr Upgrade-Reibung. Sinnvoll nur, wenn du aus Firmenrichtlinien keine Container fahren darfst.

Kubernetes. Overkill für alles unter 10 produktiven Workflows. Wenn du den Cluster bereits hast und n8n nur eine weitere App ist, spricht nichts dagegen. Sonst lass es.

Der Rest dieser Anleitung nutzt Docker Compose mit Postgres und Caddy.

Schritt für Schritt: erste Installation

Verzeichnisstruktur. Auf dem Server legst du an:

sudo mkdir -p /opt/n8n/{data,postgres,caddy}
cd /opt/n8n

In /opt/n8n/ kommen drei Dateien: docker-compose.yml, .env und Caddyfile.

Die .env Datei. Hier landen alle Variablen. Kein einziger Wert gehört in docker-compose.yml hardcoded.

# Domain
N8N_HOST=n8n.deinefirma.de
WEBHOOK_URL=https://n8n.deinefirma.de/

# Datenbank
POSTGRES_USER=n8n
POSTGRES_PASSWORD=ersetze-mit-echtem-passwort
POSTGRES_DB=n8n

# Verschluesselung
N8N_ENCRYPTION_KEY=generiere-mit-openssl-rand-base64-32

# Basic Auth für die Web-UI
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=ersetze-mit-echtem-passwort

# Zeitzone
GENERIC_TIMEZONE=Europe/Berlin
TZ=Europe/Berlin

Den Encryption Key erzeugst du mit openssl rand -base64 32. Der verschlüsselt alle Credentials in deiner n8n-Datenbank. Wenn du ihn verlierst, sind deine gespeicherten API-Keys und OAuth-Tokens nicht mehr entschlüsselbar. Backup separat sichern, nicht im gleichen Restic-Repo wie die Postgres-Dumps.

Die docker-compose.yml.

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: n8nio/n8n:latest
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
      DB_POSTGRESDB_USER: ${POSTGRES_USER}
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_HOST: ${N8N_HOST}
      N8N_PROTOCOL: https
      N8N_PORT: 5678
      WEBHOOK_URL: ${WEBHOOK_URL}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE}
      N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
      N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
      GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
      TZ: ${TZ}
      EXECUTIONS_DATA_SAVE_ON_SUCCESS: none
      EXECUTIONS_DATA_SAVE_ON_ERROR: all
      N8N_LOG_LEVEL: info
      N8N_DEFAULT_BINARY_DATA_MODE: filesystem
    volumes:
      - ./data:/home/node/.n8n

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/data:/data
      - ./caddy/config:/config
    depends_on:
      - n8n

Das Caddyfile. Caddy holt sich Let’s Encrypt Zertifikate automatisch, solange der DNS-Record stimmt.

n8n.deinefirma.de {
    reverse_proxy n8n:5678
    encode gzip
}

DNS setzen. A-Record für n8n.deinefirma.de auf die IP deines Servers. Warte bis die DNS-Propagation durch ist, bevor du startest. Ohne aufgelösten Namen kriegt Caddy kein Zertifikat.

Starten.

cd /opt/n8n
docker compose up -d
docker compose logs -f n8n

Beim ersten Start legt n8n die Datenbank-Schemas an, Caddy holt das SSL-Zertifikat. Nach etwa einer Minute ist https://n8n.deinefirma.de erreichbar. Basic-Auth-Prompt, dann der n8n-Owner-Setup-Screen. Admin-User anlegen, fertig.

Häufige Fehler beim ersten Start. Wenn Caddy kein Zertifikat bekommt, liegt es meistens an zwei Dingen: DNS zeigt nicht auf den Server, oder Port 80 ist durch einen anderen Prozess belegt (nginx, apache). sudo ss -tlnp | grep :80 zeigt dir, was dort lauscht. Wenn n8n beim Start sofort abstürzt, prüfe den Encryption Key, oft wird er mit Shell-Sonderzeichen generiert, die bash im .env nicht korrekt liest. Keys ohne $, ` oder \ nutzen oder in einfachen Anführungszeichen in der .env hinterlegen.

Produktions-Konfiguration

Die Default-Konfiguration von n8n ist für Entwicklungs-Setups gedacht. Ein paar Variablen sollten in Produktion anders stehen:

Execution Data sparsam speichern. EXECUTIONS_DATA_SAVE_ON_SUCCESS=none und EXECUTIONS_DATA_SAVE_ON_ERROR=all ist der beste Kompromiss. Bei erfolgreichen Durchläufen wird nichts persistiert, Fehler landen vollständig in der DB. Das hält die Postgres-Tabelle execution_entity klein und macht Debugging trotzdem möglich.

Log-Level auf info. debug produziert auf Dauer Gigabytes an Logs, warn verschluckt zu viel. info ist der Standard, den du willst.

Binary Data ins Filesystem. N8N_DEFAULT_BINARY_DATA_MODE=filesystem verhindert, dass grosse Dateien in der Datenbank landen und die DB aufblähen. Vor allem bei PDF-Verarbeitung oder Bild-Workflows wichtig.

Queue Mode, wenn die Last steigt. Bei hunderten parallelen Executions wird der Default-Modus (alle Runs im Haupt-Prozess) zum Bottleneck. Dann rüstest du Redis und Worker-Container nach, n8n verteilt die Arbeit auf die Worker. Details stehen in der n8n-Doku unter “Scaling n8n”. Für die meisten Self-Hoster ist das nicht nötig.

Rate Limits auf Webhooks. Wenn deine Webhook-URL öffentlich erreichbar ist, setzt du besser ein Rate Limit auf Caddy-Ebene, sonst kann jeder deine Workflows triggern, bis der Server glüht.

Execution Timeouts setzen. Ein einzelner hängender Workflow kann deinen Server minutenlang blockieren, wenn kein Timeout gesetzt ist. EXECUTIONS_TIMEOUT=3600 (Sekunden) ist ein sinnvoller Default für die meisten Produktions-Workflows. Für langlaufende KI-Inferenz oder Batch-Jobs darfst du das höher setzen, aber immer bewusst.

Separate Datenbank-User für n8n. In der .env nutzt n8n im Beispiel den Postgres-Superuser. Sauberer ist ein eigener User mit nur den Rechten auf seine Datenbank. Postgres macht das über CREATE ROLE n8n LOGIN PASSWORD '...' NOSUPERUSER NOCREATEDB NOCREATEROLE; und GRANT ALL PRIVILEGES ON DATABASE n8n TO n8n;. Nicht zwingend für einen Single-Tenant-Server, aber Best Practice sobald mehrere Services die gleiche Postgres teilen.

Backups einrichten

Ohne Backup kein Self-Hosting. Drei Dinge müssen gesichert werden:

Postgres-Dump täglich. Ein einfacher systemd-Timer reicht. Wie du systemd-Timer für Cron-artige Jobs einrichtest, steht im systemd-Services-Guide.

#!/bin/bash
set -euo pipefail
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
docker compose -f /opt/n8n/docker-compose.yml exec -T postgres \
  pg_dump -U n8n n8n | gzip > /opt/backups/n8n-db-${TIMESTAMP}.sql.gz
find /opt/backups -name "n8n-db-*.sql.gz" -mtime +30 -delete

n8n-Datenverzeichnis mit restic. Das /opt/n8n/data Verzeichnis enthält Workflow-Snapshots und die SQLite-Datei für nicht-DB-persistierte Daten. Restic nach S3 oder Backblaze B2 mit Retention-Policy.

restic -r b2:mein-bucket:n8n backup /opt/n8n/data /opt/backups
restic -r b2:mein-bucket:n8n forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

Encryption Key separat. Der N8N_ENCRYPTION_KEY aus der .env gehört in einen Passwort-Manager, nicht ins Backup. Wer den Key und den Postgres-Dump hat, kann alle Credentials lesen. Trennung der beiden Artefakte ist Pflicht.

Disaster Recovery testen. Einmal im Jahr richtest du auf einem zweiten VPS ein frisches Setup ein, spielst den Dump ein, kopierst das Datenverzeichnis und prüfst, ob deine Workflows laufen und Credentials entschlüsselt werden. Ungetestete Backups sind keine Backups.

Sicherheit

n8n öffentlich ohne Schutz ist eine schlechte Idee. Jeder der die URL hat, kann Workflows ausführen, wenn die Webhook-Endpoints öffentlich reachable sind.

UFW Firewall. Nur 22 (SSH), 80 und 443 offen. Postgres hört nur auf das Docker-Netzwerk, nicht nach aussen.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Basic Auth aktiv lassen. Auch wenn dein n8n nur von dir genutzt wird. Ist der niedrigste Aufwand für den grössten Schutz vor Scans.

Forward Auth für Teams. Wenn mehrere Leute zugreifen, ersetzt du Basic Auth durch Authelia oder Authentik vor dem Caddy. SSO, 2FA, saubere Session-Verwaltung.

Webhook-URLs mit Secret-Path. n8n-Webhook-URLs enthalten eine UUID, die ist schwer zu raten. Trotzdem rotierst du sie nach Personalwechseln oder wenn Logs kompromittiert wurden.

Dateiberechtigungen. Die .env darf nur root lesen.

sudo chmod 600 /opt/n8n/.env
sudo chown root:root /opt/n8n/.env

Docker Updates regelmässig. Einmal pro Woche:

cd /opt/n8n
docker compose pull
docker compose up -d

Vorher checken, ob das n8n-Changelog Breaking Changes meldet.

Skalierung

Bis 100 gleichzeitige Executions reicht eine Single-Instance. Hetzner CX22 mit 4 GB RAM fährt damit in den meisten Workflow-Profilen sauber.

Vertikale Skalierung zuerst. Von CX22 auf CX32 oder CX42 hochziehen, bis RAM oder CPU limitiert. Einfacher als Queue Mode einzurichten.

Queue Mode wenn vertikal nicht mehr reicht. Redis als Message Broker, ein oder mehrere Worker-Container ziehen Jobs aus der Queue. n8n-Doku hat eine Compose-Datei-Referenz für diesen Modus.

Postgres auf eigenen Server. Bei hohem Schreib-Volumen trennst du die DB auf einen separaten Server. Das Netzwerk zwischen den beiden sollte dann in einem privaten Hetzner-Netz liegen, nicht über das Internet.

Wenn dein Volumen in Richtung Enterprise geht, lohnt sich der Vergleich in Make vs n8n für Production Workloads. Bei sehr hohen Volumen kippt das Kostenbild wieder.

Wartung

Version-Upgrades. Erst auf einer Test-Instance, dann Produktion. n8n macht regelmässig Major-Version-Bumps, Breaking Changes stehen in den Release Notes. Ohne Durchlesen upgraden ist der direkte Weg zu kaputten Workflows.

Logs sichten. Einmal pro Woche reicht. docker compose logs --tail 500 n8n zeigt die letzten Einträge. Fehler aus EXECUTIONS_DATA_SAVE_ON_ERROR=all schaust du dir direkt in der UI unter “Executions” an.

Postgres Vacuum. Postgres räumt meist selbst auf. Wenn die execution_entity Tabelle trotzdem wächst, hilft ein manuelles VACUUM FULL im Wartungsfenster.

Monitoring. Ein simples Uptime-Kuma auf demselben oder einem anderen Server pingt die n8n-URL und schickt dir bei Ausfall eine Telegram-Nachricht. Zusätzlich lohnt sich ein Health-Check-Workflow in n8n selbst, der alle 15 Minuten einen einfachen Ping absetzt. Wenn der ausbleibt, weisst du, dass n8n zwar antwortet, aber intern klemmt.

Disk Space im Auge behalten. Postgres-Daten wachsen, vor allem wenn du EXECUTIONS_DATA_SAVE_ON_SUCCESS doch auf all hast. Ein einfacher Cron-Check df -h / | awk 'NR==2 {print $5}' mit Schwellwert 80% und Telegram-Benachrichtigung erspart dir volle Platten mitten in der Produktion.

Node-Versionen der Community-Nodes. Community-Nodes kommen nicht synchron mit n8n-Releases. Nach einem Major-Upgrade lohnt ein Check, ob alle verwendeten Community-Nodes noch laden. Fehler erscheinen in den Logs beim Start unter Failed to load node.

Wann n8n Cloud besser passt

Self-hosting ist nicht immer die richtige Wahl.

  • Du willst keine DevOps-Arbeit. Wenn Server-Wartung, Log-Kontrolle und Backup-Restore-Tests dich nicht interessieren, zahlst du den Cloud-Aufpreis und bekommst eine Sorge weniger.
  • Volumen unter 5.000 Executions pro Monat. In dem Bereich ist Cloud preislich unauffällig und der Verwaltungs-Aufwand für einen eigenen Server lohnt nicht.
  • Kein DSGVO-Zwang zur EU-Lokalisierung. n8n Cloud hat EU-Hosting, aber du unterschreibst einen AVV mit dem Anbieter. Wenn dein Datenschutzkonzept damit lebt, ist Cloud legitim.
  • Du bist ein Einzelperson-Nutzer ohne Team. Self-hosting zahlt sich aus, sobald mehrere Leute, mehrere Kunden oder kritische Produktions-Workflows darauf laufen.

AI-Integration ohne Extra-Aufwand

n8n hat inzwischen native LangChain-Nodes und einen dedizierten Anthropic-Node. Für die meisten Claude-API-Calls reicht aber der normale HTTP Request Node. Kein SDK notwendig, kein Versions-Mismatch, kein zusätzliches Package-Management im Container.

Wie du die Claude-API von n8n aus ansprichst, steht im Claude-API-Guide auf Deutsch. Der Artikel zeigt Request-Struktur, Streaming und Prompt Caching, alles direkt aus n8n-Nodes heraus nutzbar.

Praktisch läuft das so: Ein HTTP Request Node mit Method POST, URL https://api.anthropic.com/v1/messages, Header x-api-key und anthropic-version: 2023-06-01, dazu ein JSON-Body mit deinem Model, System-Prompt und Messages. Credentials speicherst du im n8n-Credential-Store (verschlüsselt via Encryption Key), die Response kannst du per JSON-Path direkt im nächsten Node weiterverarbeiten. Prompt-Caching aktivierst du via cache_control im System-Block, was gerade bei Workflows mit langen, wiederkehrenden Kontexten den Token-Verbrauch drastisch senkt.

Download the AI Automation Checklist (PDF)