Rallly mit Docker Compose selbst hosten: Terminabstimmung ohne Doodle
Rallly ist ein quelloffenes Werkzeug zur Gruppenterminabstimmung und damit eine Alternative zu Doodle, die vollständig im eigenen Haus läuft. Diese Anleitung zeigt den offiziellen Docker-Compose-Stack, erklärt jede Pflichtvariable, die SMTP-Anbindung und die Basis-URL, dokumentiert Backup und Wiederherstellung der PostgreSQL-Datenbank und benennt die Lizenzgrenzen für den Mehrbenutzerbetrieb offen.

Wer in einem Team einen Termin finden muss, greift oft zu Doodle. Die Teilnehmer tragen ihre Verfügbarkeiten ein, der Organisator liest das beste Zeitfenster ab. Bequem ist das, nur liegen dabei Namen, E-Mail-Adressen und Abwesenheiten auf einem fremden Server. Für Unternehmen, die Termine mit Kunden, Betriebsräten oder externen Dienstleistern abstimmen, ist das eine Verarbeitung personenbezogener Daten durch einen Auftragsverarbeiter, die dokumentiert und vertraglich abgesichert sein will. Rallly löst genau dieses Problem, indem es dieselbe Funktion auf einem Server im eigenen Verantwortungsbereich bereitstellt.
Rallly ist eine Webanwendung auf Basis von Next.js mit PostgreSQL als Datenbank. Der Organisator legt eine Umfrage mit mehreren Terminvorschlägen an, verschickt einen Einladungslink, und die Teilnehmer markieren pro Vorschlag, ob sie können, nicht können oder notfalls können. Anschließend bestätigt der Organisator einen Termin. Diese Anleitung installiert Rallly mit dem offiziellen Docker-Compose-Stack, erklärt jede Pflichtvariable, richtet einen vorgelagerten Reverse Proxy ein und zeigt Backup und Wiederherstellung der Datenbank. Alle Befehle wurden auf einem Linux-Host mit Docker 29.1.3 und Docker Compose 2.40.3 ausgeführt, die beobachteten Ausgaben stehen im Text.
Was Rallly leistet und wofür es nicht taugt
Rallly deckt einen eng umrissenen Anwendungsfall ab: die Abstimmung eines Termins innerhalb einer Gruppe über mehrere zur Wahl gestellte Zeitfenster. Das ist bewusst schmal gehalten, und genau darin liegt die Stärke. Die Oberfläche ist in wenigen Minuten verstanden, Teilnehmer brauchen kein Konto und stimmen über einen Link ab. Nur der Organisator meldet sich an.
- Gruppenabstimmung über mehrere Vorschläge: Der Organisator stellt beispielsweise sechs mögliche Termine zur Wahl, acht Teilnehmer antworten, das Ergebnis ist eine Matrix mit Zustimmung, Ablehnung und dem Zwischenwert "wenn es sein muss".
- Teilnahme ohne Registrierung: Wer nur abstimmt, legt kein Konto an. Das senkt die Hürde für externe Teilnehmer erheblich und reduziert zugleich die Menge dauerhaft gespeicherter Konten.
- Mehrsprachigkeit: Die Oberfläche liegt in zahlreichen Sprachen einschließlich Deutsch vor, die Sprache der Anmelde-Mails folgt seit Version 4.15.1 der Kontoeinstellung des Empfängers.
- Anbindung an eigene Anmeldung: Neben dem Anmeldecode per E-Mail unterstützt Rallly Single Sign-on über OpenID Connect, Google und Microsoft, laut offizieller Dokumentation. Diese Anbindung wurde hier nicht selbst getestet.
Ebenso wichtig ist, wofür Rallly nicht gedacht ist. Es ist kein Buchungssystem, bei dem Kunden selbständig einen freien Slot im Kalender einer bestimmten Person reservieren. Dafür braucht es ein Werkzeug wie Cal.com, das Kalenderverfügbarkeiten liest und Slots freigibt. Rallly ist auch kein Gruppenkalender, kein Raumbuchungssystem und keine Ressourcenplanung. Es ersetzt keinen Exchange- oder Nextcloud-Kalender, sondern ergänzt ihn um den Schritt davor: die Einigung auf einen Termin.
Eine weitere Grenze betrifft den Mailversand. Ohne funktionierenden SMTP-Zugang ist eine Rallly-Instanz praktisch unbrauchbar, weil die Anmeldung des Organisators über einen per E-Mail zugestellten Code läuft und Benachrichtigungen über neue Antworten ebenfalls per Mail gehen. Ein Mailserver oder ein Transaktionsmail-Dienst ist damit keine Kür, sondern Voraussetzung.
Lizenzlage und kostenpflichtige Funktionen
Rallly steht unter der GNU Affero General Public License v3.0, das bestätigt die GitHub-API für das Repository lukevella/rallly beim Abruf am 16.09.2026. Der Quellcode ist damit offen, und wer die Software modifiziert und über ein Netzwerk anbietet, muss den geänderten Quellcode zugänglich machen. Das ist beim internen Betrieb einer unveränderten Instanz unproblematisch, sollte bei eigenen Anpassungen aber bekannt sein.
Davon getrennt zu betrachten ist das Geschäftsmodell des Projekts, und hier ist Klartext angebracht. Die offizielle Dokumentation beschreibt auf der Seite "Licensing" ein Stufenmodell für selbst gehostete Instanzen: Der Betrieb ist für den persönlichen Gebrauch mit einem einzigen Benutzerkonto kostenfrei, für Mehrbenutzerbetrieb erwartet das Projekt den Kauf eines Lizenzschlüssels. Genannt werden die Stufen Plus für bis zu fünf Benutzer, Organization für bis zu 50 Benutzer und Enterprise darüber hinaus, mit einer jeweils einmaligen Gebühr für die gesamte Serie 4.x. Als Benutzer zählen laut FAQ ausschließlich registrierte Konten, Teilnehmer, die ohne Konto abstimmen, zählen nicht mit.
Das Projekt beschreibt die Durchsetzung dieser Grenze selbst als Vertrauenssystem: Bei Überschreitung laufe die Instanz unverändert weiter und zeige lediglich einen Hinweis. Eine technische Sperre wird in der Dokumentation nicht beschrieben, und im hier durchgeführten Test wurde keine geprüft. Unabhängig davon ist die Erwartung des Projekts eindeutig formuliert, und Unternehmen, die Rallly produktiv mit mehreren Konten einsetzen, sollten die Lizenzfrage mit der Geschäftsführung klären statt sie zu ignorieren.
Zusätzlich sind einzelne Funktionen ausdrücklich lizenzgebunden. Die Dokumentation vermerkt bei den Branding-Variablen, dass White Labeling, also eigenes Logo, eigene Primärfarbe und das Ausblenden des Hinweises "Powered by Rallly", eine Enterprise-Lizenz mit entsprechendem Zusatz voraussetzt. Die Aktivierung eines Lizenzschlüssels erfordert außerdem, dass der Server den Dienst licensing.rallly.co über HTTPS erreichen kann, was in abgeschotteten Netzen eine Ausnahme in der Firewall bedeutet.
Voraussetzungen und Ressourcenbedarf
Die offizielle Installationsanleitung nennt als Mindestausstattung einen Server mit 2 GB RAM, Docker ab Version 19.03 mit Compose v2, eine Domain mit passendem A-Record sowie einen SMTP-Zugang. Für den gebündelten Reverse Proxy müssen die Ports 80 und 443 frei sein.
Im Test auf einem Linux-Host mit acht CPU-Kernen und 8 GB RAM zeigte docker stats im Leerlauf kurz nach dem Start folgende Werte: der Anwendungscontainer 342 MiB, PostgreSQL 79 MiB, der Objektspeicher Garage 15 MiB. Für einen Produktivbetrieb mit wenigen gleichzeitigen Umfragen sind 2 GB RAM damit knapp, aber realistisch; 4 GB geben Luft für Updates, bei denen kurzzeitig alte und neue Container nebeneinander bestehen. Die heruntergeladenen Images belegen zusammen rund 1 GB Plattenplatz, die Datenbank wächst mit der Zahl der Umfragen und bleibt bei üblicher Nutzung klein.
Der Aufwand lässt sich nüchtern einordnen. Die reine Installation dauert bei vorhandenem Docker und vorbereiteter DNS-Konfiguration etwa 20 bis 30 Minuten. Der weitaus größere Teil ist die Anbindung an den Mailversand und die Klärung, welcher Absender verwendet wird und ob SPF und DKIM für diese Domain sauber gesetzt sind. Laufend fallen Updates der Images, Kontrolle der Backups und gelegentliche Schemaänderungen an. Als Daumenwert sind das ein bis zwei Stunden im Quartal, sofern nichts Ungewöhnliches passiert.
Wichtiger Hinweis zur Architektur des Stacks
Seit Version 4.10 liefert das Projekt den selbst gehosteten Betrieb nicht mehr als handgepflegte Compose-Datei aus, sondern als verwalteten Stack im separaten Repository lukevella/rallly-selfhosted mit einem Installationsskript und einem Verwaltungsskript namens rallly.sh. Die Dokumentation weist ausdrücklich darauf hin, dass die Datei docker-compose.yml nicht bearbeitet werden soll, weil ein Update lokale Änderungen überschreibt. Konfiguriert wird ausschließlich über die Datei .env. Wer noch eine ältere Installation mit eigener Compose-Datei betreibt, findet in der offiziellen Dokumentation eine eigene Migrationsanleitung, die einen Datenbank-Export voraussetzt.
Projekt- und Versionsstand mit Beleg
Alle Angaben in dieser Tabelle stammen aus einem direkten Abruf der GitHub-API am 16.09.2026.
| Merkmal | Wert am 16.09.2026 |
|---|---|
| Repository | lukevella/rallly |
| Sterne auf GitHub | 5255 |
| Forks | 556 |
| Letzter Push | 16.09.2026 |
| Archiviert | nein |
| Lizenz | AGPL-3.0 |
| Aktuelles Release | v4.15.1, veröffentlicht am 13.09.2026 |
| Offene Issues | 5 |
| Stack-Repository | lukevella/rallly-selfhosted |
Das Release v4.15.1 behebt laut den Release-Notes einen Fehler, bei dem Anmelde-, Registrierungs- und Passwort-Zurücksetzen-Mails immer auf Englisch verschickt wurden, weil die Sprachermittlung für die API-Routen nicht griff. Die Notes vermerken ausdrücklich, dass dieses Release weder Konfigurations- noch Datenbankänderungen mitbringt. Das im Test verwendete Image lukevella/rallly:4 trug das Erstellungsdatum 13.09.2026 und passt damit zu diesem Release.
Stack, Dateien und Konfiguration
Der offizielle Weg ist das Installationsskript, das den Stack nach /opt/rallly legt, die Konfiguration interaktiv abfragt und die Geheimnisse selbst erzeugt:
# Offizieller Installer, legt den Stack unter /opt/rallly an
curl -fsSL https://get.rallly.co | bash
Wer lieber sieht, was passiert, bevor ein Skript aus dem Netz ausgeführt wird, lädt die Dateien einzeln. Genau so wurde es im Test gemacht:
# Verzeichnis anlegen und in das Verzeichnis wechseln
mkdir -p /opt/rallly
cd /opt/rallly
# Compose-Datei, Override fuer externen Proxy und Vorlage der Konfiguration holen
curl -sO https://raw.githubusercontent.com/lukevella/rallly-selfhosted/main/docker-compose.yml
curl -sO https://raw.githubusercontent.com/lukevella/rallly-selfhosted/main/docker-compose.external-proxy.yml
curl -sO https://raw.githubusercontent.com/lukevella/rallly-selfhosted/main/.env.example
# Konfigurationsdatei fuer den Objektspeicher Garage
mkdir -p config
curl -s -o config/garage.toml https://raw.githubusercontent.com/lukevella/rallly-selfhosted/main/config/garage.toml
# Verwaltungsskript holen und ausfuehrbar machen
curl -sO https://raw.githubusercontent.com/lukevella/rallly-selfhosted/main/rallly.sh
chmod +x rallly.sh
Der Stack besteht aus vier Diensten. Traefik terminiert TLS und holt Zertifikate von Let's Encrypt, der Dienst web ist die Anwendung selbst, db ist PostgreSQL, und garage ist ein S3-kompatibler Objektspeicher für hochgeladene Dateien wie Profilbilder. Die drei gebündelten Dienste Traefik, PostgreSQL und Garage sind über Compose-Profile geschaltet und werden automatisch übersprungen, sobald in der Konfiguration ein externer Endpunkt eingetragen ist. Nur Traefik belegt Ports auf dem Host, alles andere bleibt im internen Docker-Netz.
Die Datei .env
Die folgende Vorlage enthält ausschließlich Platzhalter. Alle Werte in spitzen Klammern müssen ersetzt werden, echte Geheimnisse gehören niemals in eine Dokumentation oder in eine Versionsverwaltung.
# /opt/rallly/.env
# Domain, unter der die Instanz erreichbar ist, ohne Schema und ohne Schraegstrich
DOMAIN=rallly.example.com
# Adresse fuer Ablaufwarnungen von Let's Encrypt
ACME_EMAIL=it@example.com
# Sitzungsschluessel, mindestens 32 Zeichen, PLATZHALTER
SECRET_PASSWORD=
# Kontaktadresse, die Nutzern angezeigt wird
SUPPORT_EMAIL=it@example.com
# Absenderadresse fuer Systemmails, faellt sonst auf SUPPORT_EMAIL zurueck
NOREPLY_EMAIL=no-reply@example.com
# Erster Benutzer mit dieser Adresse darf sich unter /control-panel Adminrechte holen
INITIAL_ADMIN_EMAIL=it@example.com
# SMTP, ohne funktionierenden Mailversand ist die Anmeldung nicht moeglich
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=
SMTP_PWD=
# Registrierung auf die eigene Domain begrenzen
ALLOWED_EMAILS=*@example.com
# Automatisch erzeugte Geheimnisse, PLATZHALTER
POSTGRES_PASSWORD=
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
GARAGE_RPC_SECRET=
# PostgreSQL-Hauptversion, bei Neuinstallation 18
POSTGRES_VERSION=18
POSTGRES_DATA_MOUNT=/var/lib/postgresql
POSTGRES_VOLUME=db-data
Die Geheimnisse erzeugt man mit OpenSSL und fügt sie dann in die Datei ein. Die Dokumentation nennt für den Sitzungsschlüssel ausdrücklich eine Mindestlänge von 32 Zeichen:
# Sitzungsschluessel, mindestens 32 Zeichen
openssl rand -base64 32
# Datenbankpasswort
openssl rand -hex 16
# S3-Zugangsschluessel und S3-Geheimnis
openssl rand -hex 16
openssl rand -hex 32
# RPC-Geheimnis fuer den Objektspeicher
openssl rand -hex 32
# Anschliessend die Rechte der Konfigurationsdatei einschraenken
chmod 600 /opt/rallly/.env
Die wichtigsten Parameter im Überblick
| Variable | Pflicht | Bedeutung |
|---|---|---|
| DOMAIN | ja | Domain ohne Schema, steuert Zertifikatsanforderung und Routing im gebündelten Traefik. |
| ACME_EMAIL | ja | Adresse für Let's Encrypt, nur beim gebündelten Proxy relevant. |
| SECRET_PASSWORD | ja | Schlüssel zur Verschlüsselung der Sitzungen, mindestens 32 Zeichen. Eine Änderung macht alle Sitzungen ungültig. |
| SUPPORT_EMAIL | ja | Kontaktadresse, die Nutzern in der Oberfläche angezeigt wird. |
| SMTP_HOST | ja | Mailserver für Anmeldecodes und Benachrichtigungen. |
| SMTP_SECURE | nein | false für STARTTLS auf Port 587, true für implizites TLS auf Port 465. |
| NEXT_PUBLIC_BASE_URL | bedingt | Vollständige öffentliche URL inklusive Schema. Wird aus DOMAIN abgeleitet, muss beim Betrieb des Images ohne das Verwaltungsskript selbst gesetzt werden. |
| PROXY_MODE | nein | external schaltet den gebündelten Traefik ab und veröffentlicht die Anwendung auf einem Host-Port. |
| WEB_PORT | nein | Bindung des Host-Ports bei externem Proxy, Vorgabe 127.0.0.1:3000. |
| ALLOWED_EMAILS | nein | Begrenzt Registrierung und Anmeldung auf bestimmte Adressen, Platzhalter werden unterstützt. |
| RALLLY_IMAGE | nein | Legt das verwendete Image fest, Vorgabe lukevella/rallly:4. |
Ein Detail verdient besondere Aufmerksamkeit: Die Variable SECRET_PASSWORD ist der Anker für alle Sitzungen. Wird sie bei einer Migration neu erzeugt statt übernommen, sind sämtliche angemeldeten Nutzer ausgeloggt. Die offizielle Migrationsanleitung markiert diesen Punkt entsprechend als kritisch.
Installation und erster Start
Ist die Konfiguration fertig, startet der Stack über das Verwaltungsskript, das die passenden Compose-Profile selbst setzt:
# In das Stack-Verzeichnis wechseln
cd /opt/rallly
# Alle Dienste starten
./rallly.sh start
# Status der Dienste anzeigen
./rallly.sh status
# Log der Anwendung verfolgen
./rallly.sh logs web
Beim ersten Start passiert einiges gleichzeitig. Docker lädt die Images, PostgreSQL initialisiert seinen Datenbereich, und die Anwendung führt die Datenbankmigrationen aus, bevor der Webserver antwortet. Im Test dauerte der Vorgang vom Absetzen des Startbefehls bis zur ersten erfolgreichen Antwort etwa 50 Sekunden, wobei der größte Anteil auf den Download der Images entfiel.
Der Log des Anwendungscontainers zeigte dabei die Liste der angewandten Migrationen und abschließend diese Zeilen:
All migrations have been successfully applied.
▲ Next.js 16.3.3
- Local: http://localhost:3000
- Network: http://0.0.0.0:3000
✓ Ready in 0ms
Die Migrationen sind kein Nebenschauplatz. Die Anwendung bringt ihr Datenbankschema bei jedem Start selbst auf Stand, im Test waren nach dem Erststart 152 abgeschlossene Migrationen in der Tabelle _prisma_migrations verzeichnet. Das bedeutet auch, dass ein Update mit Schemaänderungen die Datenbank verändert, sobald der neue Container startet. Genau daraus ergeben sich die weiter unten beschriebenen Grenzen beim Zurückrollen.
Erstkonfiguration und Adminkonto
Rallly kennt keinen vorkonfigurierten Administrator mit Standardpasswort, was der Sicherheit zugutekommt. Stattdessen legt man die Adresse des künftigen Administrators vorab über INITIAL_ADMIN_EMAIL fest. Der Ablauf nach dem ersten Start sieht laut offizieller Dokumentation so aus:
- Die Instanz im Browser aufrufen und die Anmeldung mit genau der Adresse starten, die in
INITIAL_ADMIN_EMAILeingetragen ist. - Den per E-Mail zugestellten Anmeldecode eingeben und das Konto anlegen.
- Den Pfad
/control-panelaufrufen und dort die Administratorrolle beanspruchen. - Unter den Instanzeinstellungen entscheiden, ob neue Registrierungen weiterhin erlaubt sein sollen.
Diese vier Schritte konnten im Testaufbau nicht durchlaufen werden, weil dafür ein echter Postfachzugriff auf den zugestellten Code nötig gewesen wäre. Sie sind daher ausschließlich nach offizieller Dokumentation beschrieben und hier nicht selbst getestet. Der technische Unterbau dafür war jedoch nachweislich vorhanden: In der ausgelieferten Oberfläche war das Merkmal emailLogin aktiv, und die Anwendung enthielt den passenden Hinweistext für den Fall fehlender Adminrechte, der ausdrücklich auf die Variable INITIAL_ADMIN_EMAIL verweist.
Zwei Einstellungen sollten direkt nach der Übernahme des Adminkontos geprüft werden. Erstens die Registrierung: Wer die Instanz nur intern nutzt, begrenzt sie über ALLOWED_EMAILS auf die eigene Domain oder schaltet sie über REGISTRATION_ENABLED=false ganz ab, sobald alle benötigten Konten bestehen. Zweitens der Absender: NOREPLY_EMAIL sollte auf eine Adresse zeigen, für die SPF und DKIM korrekt gesetzt sind, sonst landen die Anmeldecodes im Spamordner und die Anwendung wirkt kaputt, obwohl sie es nicht ist.
Funktions- und Healthcheck
Nach dem Start sollten drei Dinge geprüft werden: laufen alle Container, antwortet die Anwendung mit HTTP 200, und ist der Mailserver aus dem Anwendungscontainer heraus erreichbar.
# Zustand aller Dienste
docker compose ps
# Antwort der Startseite, erwartet wird 200
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:3000/
# Zahl der abgeschlossenen Migrationen in der Datenbank
docker compose exec -T db psql -U postgres -d rallly -tAc \
"select count(*) from _prisma_migrations where finished_at is not null;"
Im Test lieferte docker compose ps diese Übersicht:
SERVICE STATE STATUS
db running Up 2 minutes (healthy)
garage running Up 2 minutes (healthy)
web running Up 25 seconds (health: starting)
Der Aufruf der Startseite antwortete mit HTTP 200, die Anmeldeseite lieferte eine vollständige Seite von rund 88 Kilobyte mit dem Titel "Login". Die Abfrage der Migrationstabelle ergab den Wert 152. Zusätzlich existiert unter dem Pfad /healthz eine Route, die im Test ebenfalls mit HTTP 200 antwortete; sie eignet sich als Ziel für eine externe Überwachung, liefert allerdings eine HTML-Seite und keinen maschinenlesbaren Statuswert.
Die Erreichbarkeit des Mailservers lässt sich direkt aus dem Anwendungscontainer heraus prüfen, ohne eine Mail zu verschicken. Im Testaufbau lief dafür ein Mailpit-Container als Empfänger, der Verbindungstest gegen dessen Port 1025 lieferte die Begrüßungszeile des Servers:
SMTP banner: 220 7fda5b61c973 Mailpit ESMTP Service ready
Damit ist belegt, dass der Anwendungscontainer den konfigurierten Mailserver über das Docker-Netz erreicht. Der vollständige Versand eines Anmeldecodes einschließlich Zustellung wurde dagegen nicht getestet, weil dafür eine Anmeldung über die Browseroberfläche nötig gewesen wäre. Dieser Schritt ist hier nur dokumentiert. Für eine produktive Instanz gilt: Die Anmeldung mit einer echten Adresse ist der einzige belastbare Beweis, dass die Mailstrecke funktioniert, und dieser Test gehört vor die Freigabe für die Nutzer.
Persistente Daten, Volumes und Rechte
Der Stack legt seine Daten in benannten Docker-Volumes ab, nicht in Verzeichnissen des Hosts. Nach dem Start waren im Test folgende Volumes vorhanden, jeweils mit dem Projektnamen als Präfix:
- db-data: der Datenbereich von PostgreSQL. Hier liegen alle Umfragen, Teilnehmer, Stimmen und Konten. Dieses Volume ist der eigentliche Wert der Installation.
- garage-data und garage-meta: Nutzdaten und Metadaten des Objektspeichers, hier landen hochgeladene Dateien wie Profil- oder Logobilder.
- acme-data: die von Let's Encrypt ausgestellten Zertifikate, nur beim gebündelten Traefik in Gebrauch.
Weil benannte Volumes verwendet werden, entfällt die bei Bind-Mounts übliche Fummelei mit Benutzer- und Gruppen-IDs. Die Container legen Eigentümer und Rechte im Volume selbst korrekt an. Eine Ausnahme ist das PostgreSQL-Volume beim Versionswechsel: Ab Version 18 erwarten die offiziellen Postgres-Images das Volume an einem anderen Pfad, weshalb der Stack die Variablen POSTGRES_VERSION, POSTGRES_DATA_MOUNT und POSTGRES_VOLUME kennt. Diese Werte sollten nur angefasst werden, wenn sie zur tatsächlichen Hauptversion des vorhandenen Datenbereichs passen, sonst verweigert PostgreSQL den Start.
Auf Hostseite gehört die Datei .env geschützt, denn sie enthält das Datenbankpasswort, das SMTP-Passwort und den Sitzungsschlüssel im Klartext. Das Installationsskript setzt selbst chmod 600, bei manueller Anlage muss das nachgeholt werden. In eine Versionsverwaltung gehört diese Datei nicht.
Sichere Netzwerkfreigabe, Reverse Proxy und TLS
Für eine Neuinstallation auf einem dedizierten Server ist der gebündelte Traefik der einfachste Weg: Er belegt die Ports 80 und 443, leitet HTTP auf HTTPS um und fordert das Zertifikat selbständig an. Es genügt, DOMAIN und ACME_EMAIL zu setzen und dafür zu sorgen, dass der A-Record der Domain auf den Server zeigt.
Auf einem Server, auf dem bereits ein Reverse Proxy läuft, ist das der falsche Weg, weil zwei Dienste nicht gleichzeitig auf Port 443 hören können. Dann wird der gebündelte Proxy abgeschaltet und die Anwendung auf einem lokalen Port veröffentlicht:
# Ergaenzung in /opt/rallly/.env
PROXY_MODE=external
WEB_PORT=127.0.0.1:3000
Die Bindung an 127.0.0.1 ist hier kein Detail, sondern die Absicherung: Die Anwendung ist dann ausschließlich lokal erreichbar, und nur der vorgelagerte Proxy kommt heran. Genau diese Betriebsart wurde im Test verwendet, mit Port 18300 statt 3000. Eine passende Konfiguration für nginx sieht so aus:
server {
listen 443 ssl http2;
server_name rallly.example.com;
ssl_certificate /etc/letsencrypt/live/rallly.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rallly.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server {
listen 80;
server_name rallly.example.com;
return 301 https://$host$request_uri;
}
Der häufigste Stolperstein beim externen Proxy ist die Basis-URL. Rallly baut die Links in Einladungsmails und die Weiterleitungen nach der Anmeldung aus dieser Adresse. Beim Betrieb über das Verwaltungsskript wird sie aus DOMAIN abgeleitet und ergibt https:// plus Domain. Wer das Image dagegen ohne das Verwaltungsskript betreibt, muss NEXT_PUBLIC_BASE_URL selbst setzen, vollständig mit Schema und ohne abschließenden Schrägstrich. Stimmt der Wert nicht, funktioniert die Oberfläche scheinbar, aber die Einladungslinks zeigen auf die falsche Adresse. Im Testaufbau ohne TLS wurde dafür NEXT_PUBLIC_BASE_URL=http://localhost:18300 gesetzt, und die von der Anwendung ausgelieferten Logo-Pfade enthielten anschließend genau diese Basis-URL, was die Wirkung der Variable belegt.
Backup und Wiederherstellung
Das Verwaltungsskript bringt einen eigenen Sicherungsbefehl mit, der die Datenbank als komprimierte Datei mit Zeitstempel unter ./backups/ ablegt:
# Sicherung ueber das Verwaltungsskript
cd /opt/rallly
./rallly.sh backup
Wer die Sicherung in eine bestehende Backup-Kette einbauen will, ruft pg_dump direkt im Datenbankcontainer auf. Genau so wurde im Test gesichert:
# Datenbank sichern, komprimiert und mit Zeitstempel im Dateinamen
cd /opt/rallly
mkdir -p backups
docker compose exec -T db pg_dump -U postgres -Fp rallly \
| gzip > backups/rallly_$(date +%Y%m%d_%H%M%S).sql.gz
# Ergebnis pruefen
ls -lh backups/
Im Test ergab das eine Datei von 23 Kilobyte für eine frisch migrierte, noch leere Instanz. Wichtig ist der Hinweis der offiziellen Dokumentation, dass diese Sicherung ausschließlich die Datenbank umfasst. Hochgeladene Dateien liegen im Volume des Objektspeichers und müssen separat gesichert werden, etwa über docker run mit eingehängtem Volume und tar. Ebenso gehört die Datei .env in die Sicherung, denn ohne den ursprünglichen Sitzungsschlüssel ist eine Wiederherstellung zwar möglich, meldet aber alle Nutzer ab.
Wiederherstellung, im Test durchgespielt
Ein Backup, das nie zurückgespielt wurde, ist eine Vermutung und kein Backup. Der folgende Ablauf wurde im Test vollständig ausgeführt, inklusive absichtlicher Zerstörung von Daten und anschließender Kontrolle:
# Anwendung stoppen, damit sie die Verbindungen zur Datenbank freigibt
docker compose stop web
# Datenbank verwerfen und leer neu anlegen, dafuer mit der Datenbank postgres verbinden
docker compose exec -T db psql -U postgres -d postgres -c "DROP DATABASE IF EXISTS rallly;"
docker compose exec -T db psql -U postgres -d postgres -c "CREATE DATABASE rallly;"
# Sicherung einspielen, ON_ERROR_STOP bricht beim ersten Fehler ab
gunzip < backups/rallly_20260916_100401.sql.gz \
| docker compose exec -T db psql -v ON_ERROR_STOP=1 -U postgres rallly
# Anwendung wieder starten
docker compose start web
Zur Kontrolle wurde vor der Sicherung eine Markierungstabelle in der Datenbank angelegt, diese nach der Sicherung gelöscht und nach der Wiederherstellung wieder abgefragt. Der zurückgelieferte Wert entsprach dem ursprünglich eingetragenen, und die Zahl der Einträge in der Migrationstabelle lag unverändert bei 152. Der anschließende Aufruf der Startseite antwortete erneut mit HTTP 200. Die Wiederherstellung funktioniert also, und zwar genau in dieser Reihenfolge: erst die Anwendung stoppen, dann die Datenbank leeren, dann einspielen, dann starten. Wer den Stopp der Anwendung überspringt, bekommt eine Fehlermeldung, weil offene Verbindungen das Verwerfen der Datenbank blockieren.
Der Parameter ON_ERROR_STOP=1 verdient Beachtung. Ohne ihn arbeitet psql ein fehlerhaftes Skript bis zum Ende ab und meldet am Schluss Erfolg, obwohl Tabellen fehlen. Mit ihm bricht der Vorgang beim ersten Fehler ab, was genau das gewünschte Verhalten ist.
Updates und Grenzen beim Zurückrollen
Der Stack folgt standardmäßig dem Tag lukevella/rallly:4 und bleibt damit innerhalb der Hauptversion 4. Ein Update besteht aus dem Abholen der neuen Images und dem Neuerstellen der Container:
# Vor jedem Update: Sicherung erstellen und wegkopieren
cd /opt/rallly
./rallly.sh backup
# Update durchfuehren
./rallly.sh update
# Danach Zustand und Log kontrollieren
./rallly.sh status
./rallly.sh logs web
Die Reihenfolge ist nicht verhandelbar. Beim Start der neuen Version wendet die Anwendung offene Datenbankmigrationen an, und diese Migrationen sind in aller Regel nicht rückwärts ausführbar. Wird eine Spalte umbenannt oder entfernt, kann die vorherige Version mit dem veränderten Schema nichts mehr anfangen. Das Zurücksetzen auf ein älteres Image allein reicht deshalb nicht aus.
Ein Rollback bedeutet in der Praxis: das ältere Image über RALLLY_IMAGE festlegen und die vor dem Update erstellte Datenbanksicherung zurückspielen. Alle Daten, die zwischen Sicherung und Rollback entstanden sind, gehen dabei verloren. Deswegen ist das Backup unmittelbar vor dem Update kein Ritual, sondern die einzige vorhandene Rückfahrkarte. Bei einem Wechsel auf eine neue Hauptversion kommt hinzu, dass ein Sprung über mehrere Hauptversionen hinweg nicht vorgesehen ist; hier gehört die Migrationsanleitung des Projekts gelesen, bevor irgendetwas angefasst wird.
Ein zweites Detail betrifft die Stack-Dateien selbst. Die Dokumentation weist ausdrücklich darauf hin, dass das Update die Dateien des Stacks erwartet, wie sie ausgeliefert wurden, und lokale Änderungen daran überschreibt. Anpassungen gehören ausschließlich in die Datei .env. Wer eigene Dienste ergänzen möchte, arbeitet mit einer zusätzlichen Override-Datei statt mit Änderungen an der Originaldatei.
Typische Fehler mit Diagnose und Lösung
Die folgenden Fehlerbilder stammen teils aus dem eigenen Test, teils aus der offiziellen Dokumentation. Die Herkunft ist jeweils angegeben.
| Symptom | Ursache | Abhilfe |
|---|---|---|
| Warnung "The ACME_EMAIL variable is not set" bei jedem Compose-Befehl | Im Test aufgetreten: Bei PROXY_MODE=external wird Traefik nicht gestartet, die Variable bleibt leer, Compose wertet sie trotzdem aus. | Unkritisch. Wer die Meldung loswerden will, trägt ACME_EMAIL= mit leerem Wert in die Datei .env ein. |
Container web steht dauerhaft auf "health: starting" | Im Test beobachtet: Der Start dauert nach dem Neustart rund 20 bis 30 Sekunden, bis die Anwendung antwortet. | Abwarten und danach docker compose ps erneut aufrufen. Antwortet die Startseite mit HTTP 200, läuft der Dienst. |
| Wiederherstellung schlägt fehl, Datenbank lässt sich nicht verwerfen | Im Test reproduziert: Die Anwendung hält offene Verbindungen zur Datenbank. | Vor dem Verwerfen docker compose stop web ausführen, danach neu starten. |
| Kein Zertifikat wird ausgestellt | Laut Dokumentation: DNS zeigt nicht auf den Server oder Port 80 und 443 sind blockiert. | A-Record prüfen, Firewall öffnen, ./rallly.sh logs traefik auf ACME-Fehler durchsehen. |
| Port 80 oder 443 bereits belegt | Laut Dokumentation: Ein anderer Dienst hört bereits auf diesen Ports. | Entweder den anderen Dienst abschalten oder PROXY_MODE=external setzen und den vorhandenen Proxy verwenden. |
| Container starten immer wieder neu | Laut Dokumentation: Fehlende oder ungültige Werte in der Datei .env, etwa ein leerer Sitzungsschlüssel. | Werte prüfen, insbesondere Mindestlänge von 32 Zeichen bei SECRET_PASSWORD, danach ./rallly.sh restart. |
| Anmeldecodes kommen nicht an | Laut Dokumentation: Falsche Kombination aus Port und TLS-Einstellung oder schlechte Zustellbarkeit des Absenders. | Für Port 587 SMTP_SECURE=false, für Port 465 true setzen. Verbindung mit openssl s_client -starttls smtp -connect host:587 prüfen, SPF und DKIM der Absenderdomain kontrollieren. |
| Einladungslinks zeigen auf die falsche Adresse | Laut Dokumentation: Basis-URL falsch oder mit Schrägstrich am Ende gesetzt. | DOMAIN ohne Schema und ohne Schrägstrich eintragen, beim Betrieb ohne Verwaltungsskript NEXT_PUBLIC_BASE_URL vollständig setzen. |
Für die Diagnose des Mailversands gibt die Dokumentation einen konkreten Hinweis: Die früher übliche Variable SMTP_DEBUG wird seit Version 4.16.0 nicht mehr unterstützt, stattdessen protokolliert die Anwendung fehlgeschlagene Sendeversuche mitsamt der Antwort des Mailservers. Für die Prüfung auf Verbindungsebene bleibt der direkte Test mit OpenSSL das Mittel der Wahl.
Saubere Deinstallation
Beim Entfernen des Stacks ist zwischen zwei Fällen sauber zu unterscheiden, und die Unterscheidung entscheidet über den Erhalt aller Daten.
Das Abschalten ohne Datenverlust entfernt lediglich Container und Netzwerk, die Volumes bleiben bestehen:
# Nur stoppen, Daten bleiben erhalten
cd /opt/rallly
docker compose down
Die vollständige Entfernung löscht dagegen auch die Volumes. Warnung: Der folgende Befehl löscht die PostgreSQL-Datenbank mit allen Umfragen, Stimmen und Konten sowie alle hochgeladenen Dateien unwiderruflich. Es gibt danach keinen Weg zurück außer einer zuvor erstellten und an anderer Stelle abgelegten Sicherung. Vor dem Ausführen ist zu prüfen, dass eine solche Sicherung existiert und dass sie testweise zurückgespielt wurde.
# Letzte Sicherung erstellen und an einen anderen Ort kopieren
cd /opt/rallly
./rallly.sh backup
# Erst danach: Stack samt Volumes entfernen, Daten sind dann weg
docker compose down -v
# Verbliebene Volumes kontrollieren
docker volume ls
# Stack-Verzeichnis entfernen, enthaelt auch die Datei .env mit den Geheimnissen
rm -rf /opt/rallly
Genau dieser Ablauf wurde im Test zum Aufräumen verwendet. Docker meldete dabei das Entfernen der Container, des Netzwerks und der drei Volumes für Datenbank, Objektspeicher-Nutzdaten und Objektspeicher-Metadaten. Ein anschließender Blick auf die Volume-Liste bestätigte, dass keine Reste des Projekts zurückblieben. Wer Domain und Reverse-Proxy-Konfiguration nicht mehr braucht, entfernt zusätzlich den Server-Block im Proxy und den DNS-Eintrag.
Einordnung für den Betrieb im Unternehmen
Rallly ist ein kleines Werkzeug mit klarem Nutzen und überschaubarem Betriebsaufwand. Der Stack läuft mit vier Containern, die Datenbank ist PostgreSQL, Backup und Wiederherstellung folgen den gewohnten Mustern mit pg_dump und psql. Wer bereits Docker-Dienste betreibt, integriert Rallly ohne neue Konzepte.
Aus Datenschutzsicht ist der Gewinn konkret: Teilnehmerdaten und Terminwünsche verlassen die eigene Infrastruktur nicht, die Aufbewahrungsdauer bestimmt das Unternehmen selbst, und es entfällt die Notwendigkeit, einen weiteren externen Auftragsverarbeiter in das Verzeichnis der Verarbeitungstätigkeiten aufzunehmen. Das ersetzt keine datenschutzrechtliche Prüfung, verschiebt aber die Verantwortung dorthin, wo sie steuerbar ist.
Drei Punkte sollten vor einer Produktivfreigabe verbindlich geklärt sein. Erstens der Mailversand, denn ohne zuverlässige Zustellung der Anmeldecodes ist die Instanz für die Nutzer wertlos. Zweitens das Backup samt geprüfter Wiederherstellung, weil Updates das Datenbankschema verändern und ein Rollback ohne Sicherung nicht existiert. Drittens die Lizenzfrage, sobald mehr als ein Konto angelegt wird.
Passende Anleitungen auf S-EDV
- Cal.com als Calendly-Alternative auf der Synology: der andere Anwendungsfall, bei dem Kunden selbständig einen freien Slot im Kalender einer Person buchen, statt in der Gruppe über mehrere Vorschläge abzustimmen.
- PostgreSQL-Backups mit pg_dump und pg_restore: vertieft die Sicherungs- und Wiederherstellungsverfahren, die hinter dem Backup-Befehl von Rallly stehen.
- Mailpit als SMTP-Testserver mit Docker Compose: nützlich, um die SMTP-Anbindung einer neuen Instanz zu prüfen, ohne echte Mails zu verschicken.
Quellen
- Rallly Dokumentation: Self-Hosting Einführung
- Rallly Dokumentation: Installation
- Rallly Dokumentation: Konfigurationsvariablen
- Rallly Dokumentation: Verwaltung, Backup und Wiederherstellung
- Rallly Dokumentation: Lizenzierung und Preisstufen
- GitHub: lukevella/rallly, Repository und Lizenz
- GitHub: Release-Notes zu v4.15.1
- GitHub: lukevella/rallly-selfhosted, Compose-Stack und Vorlage der Konfiguration