← Blog

Chatwoot mit Postgres, Redis und Sidekiq selbst hosten

Railyard

11. April 2026 · 8 Min. Lesezeit

Folge dem auf deinem eigenen Server. Kostenlos starten, keine Karte nötig

Chatwoot gibt dir einen geteilten Posteingang und ein Live-Chat-Widget ohne SaaS-Rechnung pro Agent, aber das Projekt selbst besteht aus vier bewegten Teilen: einem Rails-Webprozess, einem Sidekiq-Worker für Background-Jobs, Postgres und Redis. Machst du bei einem davon etwas falsch (eine veraltete FRONTEND_URL, ein Worker, der Redis nicht erreicht, ein Zertifikat, das nie ausgestellt wird), bekommst du eine Login-Seite, die endlos lädt, statt einen Support-Posteingang. Am Ende dieser Anleitung laufen alle vier Dienste auf deinem eigenen Server, hinter HTTPS, mit Backups und einem Upgrade-Pfad, den du tatsächlich wiederholen kannst.

Was du brauchst

  • Einen Server mit mindestens 4 vCPUs und 8 GB RAM. Chatwoots eigene Self-Hosting-Doku nennt 2 Kerne / 4 GB als absolutes Minimum, aber das gilt nur für den Webprozess allein; sobald der Sidekiq-Worker, Postgres und Redis auf derselben Box dazukommen, verschiebt ihre Doku die Empfehlung auf 4+ Kerne und 8 GB+. Ein Hetzner CX33 (4 vCPU, 8 GB RAM, 80 GB NVMe) erfüllt diese Empfehlung und kostet derzeit 8,99 €/Monat exkl. MwSt.
  • Ubuntu 22.04 oder neuer, mit Root- oder Sudo-Zugriff.
  • Einen Domainnamen, auf den du einen A-Record zeigen lassen kannst (diese Anleitung nutzt chat.example.com).
  • Optional, aber empfohlen: SMTP-Zugangsdaten von einem transaktionalen E-Mail-Anbieter, damit Chatwoot Passwort-Resets und Agent-Benachrichtigungen senden kann. Chatwoot wird standardmäßig ohne konfiguriertes SMTP ausgeliefert.

Schritt 1: DNS auf den Server zeigen lassen und die Firewall öffnen

Lege einen A-Record für deine Subdomain an, der auf die öffentliche IPv4-Adresse des Servers zeigt. DNS-Propagierung ist der Grund, warum der spätere HTTPS-Schritt scheitern kann, wenn du ihn überstürzt, mach das also zuerst und lass es sich einpendeln, während du den Rest des Setups durchgehst.

Öffne auf dem Server selbst nur die Ports, die du brauchst:

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Du solltest Status: active mit diesen drei aufgelisteten Regeln sehen, wenn du ufw status ausführst.

Schritt 2: Docker installieren

Chatwoots Docker-Deployment braucht Docker Engine und das Compose-Plugin. Das offizielle Installationsskript richtet beides ein:

curl -fsSL https://get.docker.com | sh
usermod -aG docker $(whoami)

Melde dich ab und wieder an, damit die Gruppenänderung wirksam wird, und prüfe, dass beide Teile vorhanden sind:

docker --version
docker compose version

Du solltest eine Docker-Version und eine Compose-Version ausgegeben sehen, keinen “command not found”-Fehler.

Schritt 3: Die benötigten Secrets generieren

Drei Werte müssen existieren, bevor du die Compose-Datei schreibst: ein Rails-Secret-Key, ein Postgres-Passwort und ein Redis-Passwort. Generiere jetzt alle drei, damit du nicht in Versuchung kommst, einen Default stehen zu lassen:

openssl rand -hex 64   # SECRET_KEY_BASE
openssl rand -hex 24   # POSTGRES_PASSWORD
openssl rand -hex 24   # REDIS_PASSWORD

Speichere die drei Ausgaben irgendwo, von wo du sie einfügen kannst. Lege sie in eine .env-Datei neben der Compose-Datei, statt sie hart zu codieren, damit die Compose-Datei selbst sicher in der Versionskontrolle bleiben kann:

mkdir -p /opt/chatwoot && cd /opt/chatwoot
cat > .env <<'EOF'
SECRET_KEY_BASE=paste-the-first-value-here
POSTGRES_PASSWORD=paste-the-second-value-here
REDIS_PASSWORD=paste-the-third-value-here
SMTP_ADDRESS=smtp.your-provider.com
SMTP_USERNAME=your-smtp-username
SMTP_PASSWORD=your-smtp-password
EOF
chmod 600 .env

Schritt 4: Die Compose-Datei schreiben

Das ist der Teil, über den die meisten Anleitungen hinweggehen: Chatwoot braucht exakt dieselben Umgebungsvariablen sowohl auf dem Web-Container als auch auf dem Sidekiq-Container, denn Sidekiq führt dieselbe Rails-App aus, nur als anderer Prozess. Verpasst du eine auf dem Worker, schlagen Background-Jobs (Mailer, Webhooks, CSAT-Umfragen) lautlos fehl, obwohl die Web-UI gut aussieht.

services:
  postgres:
    image: pgvector/pgvector:0.8.7-pg16
    restart: always
    environment:
      POSTGRES_USER: chatwoot
      POSTGRES_DB: chatwoot
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data

  redis:
    image: redis:7.4.11-alpine
    restart: always
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
    volumes:
      - redis-data:/data

  rails:
    image: chatwoot/chatwoot:v4.18.0
    restart: always
    depends_on:
      - postgres
      - redis
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      RAILS_ENV: production
      NODE_ENV: production
      INSTALLATION_ENV: docker
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      FRONTEND_URL: https://chat.example.com
      REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
      POSTGRES_HOST: postgres
      POSTGRES_USERNAME: chatwoot
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DATABASE: chatwoot
      SMTP_ADDRESS: ${SMTP_ADDRESS}
      SMTP_PORT: "587"
      SMTP_USERNAME: ${SMTP_USERNAME}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      SMTP_AUTHENTICATION: plain
      SMTP_DOMAIN: example.com
      MAILER_SENDER_EMAIL: support@example.com
    command: >
      bash -c "bundle exec rails db:chatwoot_prepare && bundle exec rails s -p 3000 -b 0.0.0.0"
    volumes:
      - storage-data:/app/storage

  sidekiq:
    image: chatwoot/chatwoot:v4.18.0
    restart: always
    depends_on:
      - postgres
      - redis
    environment:
      RAILS_ENV: production
      NODE_ENV: production
      INSTALLATION_ENV: docker
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      FRONTEND_URL: https://chat.example.com
      REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
      POSTGRES_HOST: postgres
      POSTGRES_USERNAME: chatwoot
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DATABASE: chatwoot
      SMTP_ADDRESS: ${SMTP_ADDRESS}
      SMTP_PORT: "587"
      SMTP_USERNAME: ${SMTP_USERNAME}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
      SMTP_AUTHENTICATION: plain
      SMTP_DOMAIN: example.com
      MAILER_SENDER_EMAIL: support@example.com
    command: bundle exec sidekiq -C config/sidekiq.yml
    volumes:
      - storage-data:/app/storage

volumes:
  postgres-data:
  redis-data:
  storage-data:

Beachte, dass der Rails-Container nur an 127.0.0.1:3000 bindet, nicht an das öffentliche Interface. Caddy, das du als Nächstes einrichtest, ist das, was tatsächlich dem Internet zugewandt ist; der App-Container sollte nie direkt erreichbar sein.

Ersetze chat.example.com und support@example.com durch deine echte Domain, bevor du weitermachst. Prüfe, dass die Datei sauber parst:

docker compose config

Du solltest die vollständig aufgelöste Konfiguration ohne Fehler zurückbekommen, und keine übrig gebliebenen ${...}-Platzhalter in der Ausgabe.

Schritt 5: Den Stack hochfahren

docker compose up -d

Der Befehl des rails-Containers führt db:chatwoot_prepare aus, bevor der Webserver startet, was beim ersten Boot die Datenbank anlegt, das Schema lädt und die Initialdaten seedet. Beobachte es:

docker compose logs -f rails

Du solltest Migrationsausgaben sehen, gefolgt von Listening on http://0.0.0.0:3000. Bestätige dann, dass alle vier Container tatsächlich laufen:

docker compose ps

Du solltest postgres, redis, rails und sidekiq alle in einem running- oder healthy-Zustand sehen, nicht restarting.

Schritt 6: Caddy mit automatischem HTTPS davorsetzen

Installiere Caddy und gib ihm einen einzigen Site-Block. Caddy fordert das Zertifikat beim ersten Start selbst an und erneuert es, solange DNS schon auf den Server zeigt:

apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install -y caddy

Ersetze /etc/caddy/Caddyfile durch:

chat.example.com {
	reverse_proxy localhost:3000
}

Lade es dann neu:

systemctl reload caddy

Öffne https://chat.example.com im Browser. Du solltest ein gültiges Schloss-Symbol und Chatwoots Setup-Bildschirm sehen, keine Zertifikatswarnung.

Schritt 7: Postgres nach Zeitplan sichern

Alles, was zählt (Konversationen, Kontakte, Agent-Accounts), lebt im postgres-Volume. Ein täglicher Dump, den du vom Server wegbewegst, ist das minimal brauchbare Backup:

cat > /opt/chatwoot/backup.sh <<'EOF'
#!/bin/sh
cd /opt/chatwoot
docker compose exec -T postgres pg_dump -U chatwoot chatwoot | gzip > /opt/chatwoot/backups/chatwoot-$(date +%F).sql.gz
find /opt/chatwoot/backups -mtime +14 -delete
EOF
chmod +x /opt/chatwoot/backup.sh
mkdir -p /opt/chatwoot/backups
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/chatwoot/backup.sh") | crontab -

Kopiere mindestens eine dieser .sql.gz-Dateien vom Server weg (zu Object Storage, einer anderen Maschine, irgendwohin, das nicht dieser VPS ist), damit eine verlorene Festplatte nicht auch deine Backups mitnimmt. Führe am nächsten Morgen ls /opt/chatwoot/backups aus; du solltest eine neue datierte Datei sehen.

Schritt 8: Auf eine neue Chatwoot-Version upgraden

Jedes Chatwoot-Upgrade braucht den erneuten Lauf des Migrationsbefehls, genauso wie beim ersten Boot. Ziehe den neuen Tag, führe die Migration einmal aus und starte dann beide Container neu, die das Image nutzen:

docker compose pull rails sidekiq
docker compose run --rm rails bundle exec rails db:chatwoot_prepare
docker compose up -d rails sidekiq

Prüfe wieder docker compose logs -f rails. Du solltest den neuen Versions-String im Boot-Log sehen und keine Migrationsfehler.

Die Abkürzung: mit Railyard

Alles oben (die Postgres-Instanz, die Redis-Instanz mit Passwort, der Webprozess, der Sidekiq-Worker, der exakt dieselbe Umgebung liest, das zwischen ihnen geteilte Storage-Volume, HTTPS auf deiner Domain) ist auch als Vorlage in Railyards eigenem Vorlagenkatalog verfügbar. Wähle dort Chatwoot, und die vier Dienste kommen bereits miteinander verdrahtet hoch: Postgres und Redis sind automatisch sowohl mit dem Webprozess als auch mit dem Sidekiq-Worker verbunden, es gibt also kein Copy-Paste von Umgebungsvariablen zwischen zwei Containern in der Hoffnung, keine vergessen zu haben. Deinen eigenen SMTP-Anbieter fügst du danach selbst hinzu, da Railyard auch den nicht für dich konfiguriert. Wenn du eher von deinem eigenen Fork oder einer modifizierten Version des Stacks starten willst, funktionieren Deploy aus einem Repository und der Deploy-Button beide auf die gleiche Weise. Backups für den Postgres-Dienst sind in Datenbanken und Backups behandelt, und falls das dein erstes Mal auf Railyard ist, deckt der Quickstart das Account-Setup vor allem davon ab. Für ein Gefühl, was eine Box wie diese bei verschiedenen Anbietern kostet, siehe Hosting-Kosten.

Troubleshooting

“Port is already allocated” bei docker compose up. Etwas anderes auf dem Server nutzt bereits Port 3000, oder du hast docker compose up zweimal ausgeführt, ohne den ersten Lauf zu stoppen. Prüfe mit ss -tlnp | grep 3000 und stoppe, was ihn belegt, oder ändere die linke Seite des Port-Mappings des rails-Dienstes.

Die Login-Seite lädt, aber Weiterleitungen oder Links sind falsch. FRONTEND_URL muss die exakte HTTPS-URL sein, die Agenten und Kunden benutzen werden, gesetzt sowohl auf dem rails- als auch dem sidekiq-Container. Eine Abweichung (http statt https, ein abschließender Slash, die falsche Subdomain) zerstört Passwort-Reset-Links, Webhook-Callbacks und die eigenen Asset-URLs des Widgets, obwohl die App selbst gut bootet.

Das Zertifikat wird nie ausgestellt, Caddy-Logs zeigen wiederholt “challenge failed”. Das ist fast immer DNS, nicht Caddy. Der A-Record ist entweder noch nicht propagiert oder zeigt nicht auf die IP dieses Servers. Führe dig chat.example.com +short von deiner eigenen Maschine aus und bestätige, dass es die tatsächliche öffentliche IP des Servers zurückgibt, bevor du Caddy erneut neu lädst.

Datei-Uploads oder Avatare schlagen mit Berechtigungsfehlern fehl. Das chatwoot-storage-Volume muss für den Nutzer, als den der Rails-Prozess im Container läuft, beschreibbar sein. Wenn du das Volume-Mount geändert hast oder von einem Bind-Mount migrierst, prüfe die Eigentümerschaft mit docker compose exec rails ls -la /app/storage, statt zu raten.

Der Sidekiq-Container startet ständig neu oder Jobs werden nie verarbeitet. Prüfe zuerst docker compose logs sidekiq. Ein Worker, der OOM-gekillt wird, zeigt sich als Container-Neustart ohne klaren Fehler im Log, in dem Fall ist die Lösung mehr Speicher für die Box, keine Konfigurationsänderung. Wenn er läuft, aber Jobs einfach nie ausgeführt werden, bestätige, dass REDIS_URL auf dem sidekiq-Dienst exakt dem rails-Dienst entspricht, Zeichen für Zeichen, inklusive Passwort.

Es werden nie E-Mails gesendet. Chatwoot wird ohne konfiguriertes SMTP ausgeliefert. Wenn du die SMTP-Variablen in Schritt 4 übersprungen hast, tun Passwort-Resets, Agent-Einladungen und Konversationsbenachrichtigungen alle lautlos nichts. Füge echte SMTP-Zugangsdaten hinzu und starte die Container rails und sidekiq neu.

FAQ

Kann ich Chatwoot ohne Sidekiq betreiben? Nein. Background-Jobs (ausgehende E-Mail, Webhooks, CSAT, Automatisierungsregeln) hängen davon ab; ohne ihn läuft die App, aber große Teile davon funktionieren lautlos nicht mehr.

Braucht Chatwoot Redis auch für etwas außer Sidekiq? Ja, es wird auch für Caching und ActionCable genutzt (die Echtzeit-Updates im Agent-Dashboard), es ist also auch erforderlich, wenn du dich nicht so siehst, als würdest du direkt Background-Jobs nutzen.

Können Postgres und Redis auf einem separaten, kleineren Server als Web und Worker laufen? Ja, und Chatwoots eigene Doku erwähnt das als Option, wenn du lieber Managed-Database-Dienste statt Container nutzen willst. Für ein einzelnes kleines Team ist es einfacher zu betreiben, alle vier auf einer Box zu halten, und das ist, was diese Anleitung behandelt.

Was ist der Unterschied zwischen der Basis-Spec von 2 Kernen / 4 GB und der mit 4 Kernen / 8 GB? Die kleinere Zahl gilt für den Webprozess allein. Die größere ist Chatwoots eigene Empfehlung, sobald Postgres, Redis und der Sidekiq-Worker alle auf derselben Maschine um CPU und Speicher konkurrieren, was der Normalfall für eine selbst gehostete Installation ist.

Muss ich die exakten Image-Tags aus dieser Anleitung verwenden? Nein, neuere Tags erscheinen regelmäßig. Diese Anleitung pinnt chatwoot/chatwoot:v4.18.0, weil das die Version ist, die tatsächlich Ende-zu-Ende verifiziert wurde; prüfe die Release-Notes des Projekts, bevor du zu einem neueren Tag wechselst, und führe in jedem Fall den Migrationsschritt erneut aus.

Eine funktionierende Chatwoot-Installation besteht aus vier kleinen, gut verstandenen Teilen, die korrekt miteinander sprechen, nicht aus einer komplizierten Sache. Sobald DNS, die Umgebungsvariablen und der Migrationsschritt stimmen, bleibt außer Backups und dem gelegentlichen Versions-Bump kaum etwas zu pflegen.

Warum Leute sich dafür Railyard entscheiden

  • Dein eigenes Cloud-Konto. Deploye auf DigitalOcean, Hetzner, Vultr, AWS, OVHcloud oder jedem Linux-Server per SSH, direkt von deinem Anbieter abgerechnet, ohne Aufschlag.
  • Ein fester Preis pro Team. Free ist für immer kostenlos: ein Server plus ein Datenbankserver, drei Apps, keine Karte. Pro kostet 19 $ im Monat für bis zu fünf Server und unbegrenzte Apps. Team kostet 49 $ im Monat mit unbegrenzten Servern. Keine Kosten pro App, pro Deployment, pro Sitz oder pro Gigabyte.
  • Templates mit einem Klick. Deploye Open-Source-Apps, wie Datenbanken, Analytics und Dev-Tools, mit einem Klick auf deinem eigenen Server.
  • Verwaltete Datenbanken. Postgres, MySQL und Redis mit geplanten, wiederherstellungsgeprüften Backups und Point-in-Time-Recovery.
  • Push-to-Deploy. Deploye von GitHub, GitLab oder der CLI, mit automatischem HTTPS und Releases ohne Ausfallzeit.

Server-Setup überspringen

Wähle Chatwoot im Railyard-Vorlagenkatalog und bekomme dieselben vier Dienste verdrahtet und deployt, ohne ein Terminal zu berühren.

Kostenloser Plan, keine Kreditkarte, und 14 Tage Pro für jedes neue Team. Ein fester Preis pro Team.