Rocket.Chat für Team-Chat selbst hosten
Folge dem auf deinem eigenen Server. Kostenlos starten, keine Karte nötig
Slacks Preis pro Sitzplatz wächst mit der Mitarbeiterzahl, nicht damit, wie viel dein Team tatsächlich chattet, und in dem Moment, in dem jede Nachricht auf Infrastruktur bleiben muss, die du kontrollierst, ist ein gehosteter SaaS-Posteingang überhaupt keine Option mehr. Rocket.Chat ist eine Open-Source-Team-Chat-Plattform, die denselben Job erledigt, Kanäle, Direktnachrichten, Dateiaustausch, Suche, und du betreibst sie selbst. Die eine Sache, die das Selbst-Hosten hier schwerer macht als bei den meisten Chat-Tools, ist, dass MongoDB als Replica-Set konfiguriert werden muss, nicht als einfache Standalone-Datenbank, weil Rocket.Chat auf MongoDBs Change-Streams angewiesen ist, um Nachrichten in Echtzeit an verbundene Clients zu übertragen. Am Ende dieser Anleitung läuft eine funktionierende Rocket.Chat-Instanz auf deinem eigenen Hetzner-Server, hinter HTTPS, mit korrekt initialisiertem Replica-Set, Backups und einem sicheren Weg für ein späteres Upgrade.
Was du brauchst
- Ein Hetzner-Cloud-Konto und einen Server. Rocket.Chat plus ein MongoDB-Replica-Set braucht mehr Spielraum als die kleinsten Instanzen, deshalb nutzt diese Anleitung den CX33 (4 vCPU, 8 GB Arbeitsspeicher, 80 GB NVMe), gelistet für 8,99 €/Monat exkl. MwSt. Prüfe die aktuellen Preise in der Hetzner Cloud Console, bevor du bestellst, da sich Angebot und Preise ändern können.
- Einen Domainnamen (oder eine Subdomain einer Domain, die du bereits besitzt), mit Zugriff auf die DNS-Einstellungen.
- Ein SSH-Schlüsselpaar auf deiner Maschine (
ssh-keygen -t ed25519, falls du noch keines hast). - Docker und das Docker-Compose-Plugin auf dem Server installiert (Schritt 2).
- Ein Terminal und etwa zwanzig Minuten, hauptsächlich beim Warten auf DNS und die initiale Synchronisation.
Schritt 1: Server erstellen und eine Domain darauf zeigen lassen
Öffne in der Hetzner Cloud Console ein Projekt oder erstelle eines, dann wähle Add Server. Wähle einen Standort: Nürnberg, Falkenstein oder Helsinki halten deine Daten in der EU, während Ashburn, Hillsboro oder Singapur auch verfügbar sind, falls dein Team anderswo sitzt. Wähle Ubuntu 24.04 als Image, dann unter Servertyp den Shared-vCPU-CX33 (4 vCPU, 8 GB Arbeitsspeicher, 80 GB NVMe). Füge unter SSH-Keys den öffentlichen Schlüssel deiner Maschine hinzu, statt auf ein generiertes Passwort zu vertrauen, und erstelle dann den Server.
Sobald er startet, notiere die öffentliche IPv4-Adresse und verbinde dich, um den Zugriff zu bestätigen:
ssh root@DEINE_SERVER_IP
Du solltest an einer Root-Shell mit dem Ubuntu-Willkommensbanner landen, ohne Passwortaufforderung.
Füge in den DNS-Einstellungen deiner Domain einen A-Eintrag hinzu, der eine Subdomain, zum Beispiel chat.deinedomain.com, auf diese IP zeigt:
Type: A
Name: chat
Value: <IP deines Servers>
TTL: 300
Die DNS-Propagierung kann wenige Minuten bis einige Stunden dauern. Bestätige das von deiner eigenen Maschine aus, bevor du weitermachst, da Caddys Zertifikatsanfrage in Schritt 5 davon abhängt:
dig chat.deinedomain.com +short
Du solltest die IP deines Servers zurückbekommen.
Schritt 2: Docker installieren
Verbinde dich per SSH mit dem Server und installiere Docker Engine samt Compose-Plugin:
apt-get update
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
systemctl enable --now docker
Hetzner stellt standardmäßig keine Firewall vor einen neuen Server, also sichere den Host mit ufw:
ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable
Prüfe die Installation:
docker --version
docker compose version
Du solltest eine Docker- und eine Compose-Version angezeigt bekommen, nicht einen “command not found”-Fehler.
Schritt 3: Projekt und seine Geheimnisse anlegen
Erstelle ein Verzeichnis für den Stack und eine .env-Datei für die Domain und alles andere, das nicht in der Compose-Datei selbst stehen sollte:
mkdir -p /opt/rocketchat && cd /opt/rocketchat
cat > .env <<'EOF'
DOMAIN=chat.deinedomain.com
EOF
chmod 600 .env
Rocket.Chat nimmt keinen Admin-Benutzernamen oder Passwort als Container-Konfiguration entgegen, wie es manche Apps tun. Das erste Admin-Konto wird später über den eigenen Einrichtungsassistenten der App erstellt, es gibt also an dieser Stelle über die Domain selbst hinaus kein Credential zu generieren oder zu speichern.
Schritt 4: Docker-Compose-Datei schreiben
Hier zeigt sich die Replica-Set-Anforderung tatsächlich. Der mongo-Dienst muss mongod mit --replSet rs0 starten, dann prüfen, ob schon ein Replica-Set initialisiert ist, und falls nicht, eines mit sich selbst als einzigem Mitglied initiieren. Das ist ein zusammengesetzter Shell-Befehl, kein einzelner Binäraufruf, er muss also über eine Shell laufen statt als direkte Argumente von mongod.
Erstelle docker-compose.yml:
services:
rocket-chat:
image: rocketchat/rocket.chat:8.5.4
restart: unless-stopped
environment:
- PORT=3000
- ROOT_URL=https://${DOMAIN}
- DEPLOY_METHOD=docker
- MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0
- MONGO_OPLOG_URL=mongodb://mongo:27017/local?replicaSet=rs0
volumes:
- rocketchat_uploads:/app/uploads
depends_on:
- mongo
networks:
- rocketchat-net
mongo:
image: mongodb/mongodb-community-server:8.0-ubi8
restart: unless-stopped
command: ["bash", "-c", "mongod --replSet rs0 --bind_ip_all --fork --logpath /data/db/mongod.log && mongosh --quiet --eval \"try { rs.status().ok } catch (e) { rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongo:27017'}]}) }\" && tail -f /data/db/mongod.log"]
volumes:
- mongo_data:/data/db
networks:
- rocketchat-net
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN=${DOMAIN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- rocket-chat
networks:
- rocketchat-net
networks:
rocketchat-net:
volumes:
rocketchat_uploads:
mongo_data:
caddy_data:
caddy_config:
Ein paar Dinge sind eine Erklärung wert:
mongoscommandtut drei Dinge in Folge:mongodals Hintergrundprozess (--fork) starten, der an alle Schnittstellen gebunden ist mit aktivierter Replikation, dannmongoshausführen, umrs.status().okzu prüfen, den Fehler abfangen, den dieser Aufruf auf einer frischen Datenbank wirft, undrs.initiate()aufrufen, mit diesem Container als einzigem Mitglied vonrs0, falls das passiert, und schließlich das Logfile mittail -fverfolgen, damit der Container einen Vordergrundprozess hat, an dem er am Leben bleibt. Bei jedem Neustart nach dem ersten gelingtrs.status().okbereits, also wirdrs.initiate()übersprungen und der Container startet einfach in den Dienstbetrieb.- Weder
rocket-chatnochmongobekommen einensecurity_opt: ["no-new-privileges:true"]-Eintrag, und das ist bewusst so, kein Versehen. MongoDBs eigener Entrypoint legt Root-Rechte ab, indem er sich selbst als Subprozess neu ausführt, und dieses Re-Exec braucht die Fähigkeit, neue Rechte zu erlangen. Fügt man hier das übliche Hardening-Flag hinzu, brichtmongod --forkmit einem generischen “child process failed, exited with 1” ohne klarere Fehlermeldung im Log. - Beide Images sind auf exakte Tags festgepinnt,
rocket.chat:8.5.4undmongodb-community-server:8.0-ubi8, statt auflatest. Ein Chat-Server, der bei einem Neustart stillschweigend sein Datenbankverhalten ändert, ist nichts, das man während eines Arbeitstags entdecken möchte. - Nur Caddy veröffentlicht Ports zum Host. Rocket.Chat und MongoDB sind nur im internen Docker-Netzwerk erreichbar.
Prüfe, dass die Datei sich korrekt parsen lässt:
docker compose config
Du solltest die vollständig aufgelöste Konfiguration ohne Fehlerzeilen sehen.
Schritt 5: Caddyfile schreiben und alles starten
Erstelle Caddyfile im selben Verzeichnis:
{$DOMAIN} {
reverse_proxy rocket-chat:3000
}
Caddy fordert automatisch ein kostenloses TLS-Zertifikat von Let’s Encrypt für deine Domain an, solange DNS bereits auf diesen Server aufgelöst wird und die Ports 80 und 443 aus dem Internet erreichbar sind, was Schritt 1 und Schritt 2 schon eingerichtet haben.
Starte den Stack:
docker compose up -d
Beobachte die Logs, während Mongo hochkommt:
docker compose logs -f mongo
Der erste Start dauert länger als spätere Neustarts, da das Replica-Set initialisiert werden muss, bevor Rocket.Chat sich verbinden kann. Sobald das abgeschlossen ist, prüfe, ob Caddy sein Zertifikat bekommen hat:
docker compose logs -f caddy
Du solltest eine Zeile sehen, die ein für deine Domain erhaltenes Zertifikat erwähnt. Bestätige, dass alles läuft:
docker compose ps
Alle drei Dienste sollten als Up angezeigt werden.
Schritt 6: Erstes Admin-Konto anlegen
Besuche https://chat.deinedomain.com in einem Browser. Rocket.Chats eigener Einrichtungsassistent lädt und fragt nach einem Organisationsnamen, dann nach einem Admin-Konto, E-Mail und Passwort. Das ist Rocket.Chats eigener Ersteinrichtungs-Ablauf, nichts, was die Compose-Datei für dich konfiguriert, es gibt hier also nichts zu skripten außer einmal durchzugehen. Danach bietet der Assistent ein paar optionale Schritte an, Registrierungseinstellungen, eine Federation-Abfrage, die du überspringen oder nach Belieben konfigurieren kannst; nichts davon blockiert, dass die Instanz funktioniert.
Schritt 7: Datenbank und Uploads-Volume sichern
Zwei Dinge müssen gesichert werden: die MongoDB-Daten (Kanäle, Nachrichten, Benutzer, Einstellungen) und das rocketchat_uploads-Volume (im Chat geteilte Dateien). Ein mongodump gegen den laufenden Container, direkt in ein komprimiertes Archiv geleitet:
docker compose exec -T mongo mongodump --archive --gzip > rocketchat-backup-$(date +%F).gz
Lege das in einen täglichen Cronjob und kopiere das Ergebnis irgendwohin, das nicht dieser Server ist, einen Object-Storage-Bucket, eine andere Maschine, überallhin, nur nicht auf diese Box. Ein Backup, das nur auf dem Server liegt, den es schützen soll, überlebt genau den Ausfall nicht, der einen Server tatsächlich lahmlegt.
Für das Uploads-Volume:
docker run --rm -v rocketchat_rocketchat_uploads:/data -v $(pwd):/backup alpine \
tar czf /backup/rocketchat-uploads-backup.tar.gz -C /data .
Bewahre beide Dateien irgendwo zugriffsbeschränkt auf; ein Backup der Nachrichtenhistorie ist immer noch echte Benutzerdaten.
Schritt 8: Bewusst upgraden
Beide Images sind festgepinnt, ein Upgrade ist also eine bewusste Entscheidung, nicht etwas, das bei einem routinemäßigen Neustart passiert. Lies vor dem Hochsetzen eines der Tags Rocket.Chats eigene Release-Notes für jede Version zwischen der, die du betreibst, und der, zu der du wechselst. Rocket.Chat kann, wie viele Apps mit langer Historie, verlangen, dass man der Reihe nach durch Zwischenversionen von Hauptversionen geht, statt direkt von einem alten Release zum neuesten zu springen, also prüfe das, bevor du annimmst, dass ein einzelner Sprung sicher ist.
Hebe den Tag in docker-compose.yml an:
image: rocketchat/rocket.chat:8.6.0
Dann:
docker compose pull
docker compose up -d
Prüfe die Logs auf einen sauberen Start ohne Migrationsfehler und bestätige, dass deine Kanäle und Nachrichtenhistorie nach dem erneuten Login erhalten sind. Falls eine Migration tatsächlich schiefgeht, ist dein Backup aus Schritt 7 der Weg zurück zur vorherigen Version; stelle es gegen das ältere Image-Tag wieder her, bevor du entscheidest, wie weiter.
Die Abkürzung: mit Railyard
Alles oben Beschriebene, ein dediziertes, korrekt initialisiertes MongoDB-Replica-Set, das Uploads-Volume, bereits fertig eingerichtetes HTTPS, ist auch als Ein-Klick-Deploy verfügbar, aber weil es ein Multi-Service-Stack ist statt ein einzelner Container, deployt der Link nicht direkt so, wie es ein Ein-Container-App-Template tun würde:
https://app.railyard.run/deploy?stack=rocket-chat
Dieser Link öffnet stattdessen den Template-Katalog, statt sofort zu deployen, da ein Stack erst die Bestätigung seiner Dienste braucht, bevor er etwas bereitstellt. Wähle von dort Rocket.Chat im Template-Katalog. Railyard verdrahtet dieselbe Replica-Set-Anforderung, die diese Anleitung von Hand durchgeht, dasselbe persistente Uploads-Volume und HTTPS auf deiner Domain, ohne dass du die Compose-Datei oder den Shell-Befehl im Mongo-Container selbst schreiben musst.
Deploy-Button erklärt, wie der ?stack=-Link und der Template-Katalog-Ablauf funktionieren, falls du einen anderswo verstehen oder einbinden willst. Datenbanken und Backups deckt ab, wie Backups über Templates allgemein funktionieren, nicht nur bei diesem. Wenn du erst mit Railyard anfängst, bevor du dich konkret für Rocket.Chat entscheidest, deckt der Quickstart die Grundlagen ab. Railyards Free-Plan deckt einen Server plus einen Datenbankserver und drei Apps ab, ohne Kreditkarte, Pro kostet 19 $/Monat pro Team, und Team kostet 49 $/Monat pro Team.
Troubleshooting
Das Replica-Set initialisiert sich nie, oder rs.status().ok schlägt immer wieder fehl. Prüfe docker compose logs mongo auf den tatsächlichen Fehler aus dem rs.initiate()-Aufruf. Die häufigste Ursache ist, dass der Hostname in members nicht mit dem übereinstimmt, was der rocket-chat-Dienst tatsächlich auflöst; in dieser Compose-Datei ist dieser Host der Dienstname mongo, falls du den Dienst umbenannt hast, muss der host:-Wert im Befehl exakt dazu passen.
Mongo scheitert beim Start mit mongod --fork: "child process failed, exited with 1". Das liegt fast immer an security_opt: ["no-new-privileges:true"], das dem mongo-Dienst hinzugefügt wurde, meist durch einen allgemeinen Container-Hardening-Durchgang über jeden Dienst einer Compose-Datei, ohne Ausnahme für diesen. MongoDBs Entrypoint führt sich selbst als Subprozess neu aus, um Root-Rechte abzulegen, und dieses Re-Exec braucht die Fähigkeit, neue Rechte zu erlangen; das Hardening-Flag blockiert genau das. Entferne es speziell beim mongo-Dienst.
Rocket.Chat lädt, aber jeder Link, Avatar oder Redirect zeigt auf die falsche Adresse. Das ist eine ROOT_URL-Fehlkonfiguration. Sie muss die exakte HTTPS-URL sein, unter der du die Instanz tatsächlich besuchst, inklusive Schema; ist sie auf http:// statt https:// gesetzt, oder auf einen anderen Hostnamen als den, den Caddy bedient, siehst du Redirect-Schleifen im Browser oder kaputte Asset-URLs im Seitenquelltext.
Caddy bekommt nie ein Zertifikat, oder der Browser zeigt eine Zertifikatswarnung. Das liegt fast immer an DNS, nicht an Caddy. Führe dig chat.deinedomain.com +short aus und prüfe, ob die IP dieses Servers zurückkommt, nicht eine alte oder gar nichts. Let’s Encrypt braucht für den anfänglichen Validierungsschritt auch Port 80 erreichbar aus dem Internet, also prüfe, ob deine Firewall eingehenden Verkehr auf 80 erlaubt, nicht nur 443.
Mongo wird beendet, oder der Server reagiert unter normaler Nutzung nicht mehr. Ein Replica-Set, auch ein Ein-Knoten-Set, hält mehr im Speicher als eine einfache Standalone-Mongo-Instanz, und Rocket.Chat selbst ist kein leichter Prozess. Wenn du auf einem kleineren Server als dem hier verwendeten CX33 bist, ist das mit docker stats das Erste, was du prüfst, bevor du annimmst, dass etwas anderes nicht stimmt; eine Stufe größer zu gehen ist meist der tatsächliche Fix, kein Tuning-Flag.
FAQ
Kann ich das Replica-Set überspringen und Mongo als einfache Standalone-Instanz betreiben? Nein. Rocket.Chat nutzt MongoDBs Change-Streams, die nur auf einem Replica-Set existieren, um neue Nachrichten und Anwesenheitsaktualisierungen ohne Polling an verbundene Clients zu übertragen. Eine Standalone-Mongo lässt Rocket.Chat zwar starten, aber Echtzeit-Updates funktionieren dann nicht korrekt, das ist also kein optionaler Hardening-Schritt, den du für einen kleineren Fußabdruck überspringen kannst.
Ist das der klassische Rocket.Chat oder die Microservices-Version? Das ist der klassische Monolith, ein rocket-chat-Container, der mit einer Datenbank spricht, was für die meisten Teamgrößen passt und das Einfachste ist, das man selbst betreiben kann. Rocket.Chat veröffentlicht auch eine Microservices-Aufteilung für sehr große Deployments; die eigene Dokumentation deckt diesen Weg ab, falls du jemals über das hinaus skalieren musst, was ein einzelner Container leistet.
Was kostet das im Betrieb? Der Server kostet 8,99 €/Monat exkl. MwSt. für den hier verwendeten CX33. Rocket.Chat selbst ist kostenlose, Open-Source-Software ohne Gebühr pro Sitzplatz; die Kosten sind die Box, auf der es läuft, keine Lizenz.
Was passiert, wenn eine Migration während eines Upgrades fehlschlägt? Rocket.Chats Start führt beim Booten seine eigenen internen Migrationen gegen die Datenbank aus. Falls eine fehlschlägt, zeigen die Logs des Containers das klar an, statt dass die App stillschweigend halb migriert hochkommt. Deine Optionen sind dann, das Backup aus Schritt 7 gegen das vorherige Image-Tag wiederherzustellen, oder den konkreten Migrationsfehler und die zugehörigen Release-Notes zu lesen, bevor du es erneut versuchst; versuche es nicht blind gegen denselben Datenbankzustand erneut.
Brauche ich einen separaten E-Mail-Dienst, um Benutzer einzuladen? Nicht, um die Instanz zum Laufen zu bringen; das Admin-Konto aus Schritt 6 kann Benutzer direkt aus dem Admin-Panel erstellen und einladen, ohne konfiguriertes ausgehendes E-Mail. Du willst irgendwann SMTP konfigurieren für Passwort-Resets und Benachrichtigungs-E-Mails, aber es blockiert nicht ein erstes funktionierendes Deployment.
Eine selbst gehostete Rocket.Chat-Instanz ist ein bescheidener Aufwand für etwas, das du wahrscheinlich jahrelang laufen lässt: Die Nachrichtenhistorie deines Teams bleibt auf einem Server, den du kontrollierst, mit einer Backup-Routine und einem Upgrade-Pfad, die von niemand anderem Release-Zeitplan abhängen.
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.