// palaver · doku · mehrere server einrichten

Weitere Palaver-Server auf einem Host

Für jeden weiteren Server (Beispiel: guild):

Bestandteil Wert
Name guild
Domain guild.palaver.social
Systembenutzer, Datenbankrolle palaver-guild
Datenbank palaver_guild
Konfiguration /opt/palaver/instances/guild/palaverd.env
systemd-Unit palaverd@guild
Port 8091 (Schritt 3)
LiveKit-Raumpräfix guild- (PALAVER_LIVEKIT_ROOM_PREFIX)
LiveKit-Schlüssel eigener Schlüssel in /opt/palaver/etc/livekit.yaml
nginx Site palaver-guild, Snippet palaver-web-guild.conf
nginx-Logs /var/log/nginx/palaver/guild.access.log, guild.error.log

Platzhalter in den Befehlen:

Platzhalter Ersetzen durch
guild den Namen des Servers (Schritt 2), in jedem Namen der Tabelle oben; ein - im Namen wird im Datenbanknamen zu _ (z. B. palaver_my_guild)
guild.palaver.social die Domain des Servers
8091 den Port des Servers (Schritt 3)
palaver.social in /etc/letsencrypt/live/palaver.social/: den Namen des Zertifikats (eigenes Zertifikat: die Domain des Servers); sonst: die Domain des Hauptservers
you@example.org E-Mail-Adresse für Let's Encrypt; nur eigenes Zertifikat
<livekit-key>, <livekit-secret> Ausgabe aus Schritt 7, „Erzeuge … für die LiveKit-API“
'Guild', 'Guild subtitle' Name und Untertitel des Servers in der App (Schritt 12); kein Untertitel: ''
2026-10-10 das heutige Datum (Namen der Sicherungsdateien)
vi Editor deiner Wahl

1. Voraussetzungen

  • Hauptserver nach Palavers INSTALL.md (palaverd.service, Benutzer und Datenbank palaver, /opt/palaver/etc, LiveKit, Snippet palaver-web.conf), Port 8090
  • Shell-Zugang mit sudo
  • keine weiteren Ports: 443 und die LiveKit-Ports des Hauptservers

Prüfe, ob der Hauptserver da ist.

sudo ls /opt/palaver/etc/palaverd.env /opt/palaver/etc/livekit.yaml

2. Name und Domain

  • Name: a-z, 0-9, - (nicht als erstes oder letztes Zeichen), höchstens 24 Zeichen
  • reserviert: palaver, www, account, konto, support, api, admin, dogan, livekit, mail, status
  • Domain: Hostname, kleingeschrieben
  • Zertifikat: das Wildcard-Zertifikat palaver.social oder ein eigenes (Schritt 9)
  • Name und Untertitel in der App: je bis zu 64 Zeichen
  • eine Bestellung über account.palaver.social zeigt diese Werte zum Kopieren

Prüfe, ob der Name frei ist; erwartet: beide Befehle schlagen fehl.

ls -d /opt/palaver/instances/guild
id palaver-guild

3. Port

  • ein localhost-Port je Server; Hauptserver: 8090
  • neuer Server: höchster belegter Port + 1

Zeige die belegten Ports; nimm den höchsten + 1.

sudo sh -c 'grep -H "^PALAVER_LISTEN=" /opt/palaver/etc/palaverd.env /opt/palaver/instances/*/palaverd.env 2>/dev/null'

4. Unit-Vorlage

Einmal je Host; überspringen, wenn /etc/systemd/system/palaverd@.service existiert.

ls /etc/systemd/system/palaverd@.service

Öffne /etc/systemd/system/palaverd@.service in einem Editor.

sudo vi /etc/systemd/system/palaverd@.service

Inhalt, unverändert:

[Unit]
Description=Palaver app server %i
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
User=palaver-%i
Group=palaver-%i
EnvironmentFile=/opt/palaver/instances/%i/palaverd.env
ExecStart=/opt/palaver/bin/palaverd serve
Restart=on-failure
RestartSec=2
LogNamespace=palaver

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
LockPersonality=yes
MemoryDenyWriteExecute=yes

[Install]
WantedBy=multi-user.target

Lade sie.

sudo systemctl daemon-reload

5. Benutzer und Verzeichnis

Lege das Verzeichnis für alle weiteren Server an.

sudo install -d -o root -g root -m 755 /opt/palaver/instances

Lege den Systembenutzer an.

sudo useradd --system --home-dir /opt/palaver/instances/guild --no-create-home --shell /usr/sbin/nologin palaver-guild

Lege das Konfigurationsverzeichnis an.

sudo install -d -o palaver-guild -g palaver-guild -m 700 /opt/palaver/instances/guild

6. PostgreSQL

Lege die Datenbankrolle und die Datenbank an.

sudo -u postgres createuser palaver-guild
sudo -u postgres createdb -O palaver-guild palaver_guild

7. LiveKit-Schlüssel

Erzeuge den Schlüssel für die LiveKit-API (<livekit-key>).

openssl rand -hex 8 | sed 's/^/API/'

Erzeuge das Geheimnis für die LiveKit-API (<livekit-secret>).

openssl rand -base64 36 | tr -d '/+='

Öffne /opt/palaver/etc/livekit.yaml in einem Editor.

sudo vi /opt/palaver/etc/livekit.yaml

Füge unter keys: eine Zeile hinzu, um zwei Leerzeichen eingerückt:

  <livekit-key>: <livekit-secret>

Prüfe die Schlüssel; erwartet: eine Zeile je Server.

sudo sed -n '/^keys:/,/^[a-z]/p' /opt/palaver/etc/livekit.yaml

Starte LiveKit neu; Gespräche auf allen Servern brechen ab.

sudo systemctl restart livekit
  • LiveKit-Schlüssel sind nicht auf Räume begrenzt: die Konfiguration eines Servers gibt Zugriff auf die Gespräche aller Server

8. palaverd

Öffne /opt/palaver/instances/guild/palaverd.env in einem Editor.

sudo vi /opt/palaver/instances/guild/palaverd.env

Inhalt:

PALAVER_DATABASE_URL=postgres:///palaver_guild?host=/var/run/postgresql
PALAVER_LISTEN=127.0.0.1:8091
PALAVER_PUBLIC_URL=https://guild.palaver.social
PALAVER_LIVEKIT_URL=wss://guild.palaver.social/livekit
PALAVER_LIVEKIT_API_URL=http://127.0.0.1:7880
PALAVER_LIVEKIT_API_KEY=<livekit-key>
PALAVER_LIVEKIT_API_SECRET=<livekit-secret>
PALAVER_LIVEKIT_ROOM_PREFIX=guild-
PALAVER_OPEN_REGISTRATION=false
PALAVER_CTL="sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env"
PALAVER_UNIT=palaverd@guild
PALAVER_ENV_FILE=/opt/palaver/instances/guild/palaverd.env

Mach sie nur für palaver-guild lesbar.

sudo chown palaver-guild:palaver-guild /opt/palaver/instances/guild/palaverd.env
sudo chmod 600 /opt/palaver/instances/guild/palaverd.env

9. Zertifikat

Unter einem Wildcard-Zertifikat

Prüfe, ob es die Domain abdeckt; erwartet: DNS:*.palaver.social.

sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/palaver.social/fullchain.pem

Weiter: Schritt 10.

Eigenes Zertifikat

Öffne /etc/nginx/sites-available/palaver-guild in einem Editor.

sudo vi /etc/nginx/sites-available/palaver-guild

Inhalt:

server {
    listen 80;
    listen [::]:80;
    server_name guild.palaver.social;
    location /.well-known/acme-challenge/ { root /var/www/html; }
    location / { return 404; }
}

Aktiviere die Site, prüfe und lade sie.

sudo ln -sf /etc/nginx/sites-available/palaver-guild /etc/nginx/sites-enabled/palaver-guild
sudo nginx -t && sudo systemctl reload nginx

Hole das Zertifikat.

sudo certbot certonly --webroot -w /var/www/html -d guild.palaver.social -m you@example.org \
    --agree-tos --no-eff-email --non-interactive \
    --deploy-hook "systemctl reload nginx"
  • Site in Schritt 10: /etc/letsencrypt/live/guild.palaver.social/ statt /etc/letsencrypt/live/palaver.social/

10. nginx

Öffne /etc/nginx/snippets/palaver-web-guild.conf in einem Editor.

sudo vi /etc/nginx/snippets/palaver-web-guild.conf

Inhalt:

root /opt/palaver/web;
index index.html;

location / {
    try_files $uri /index.html;
    add_header Cache-Control "no-cache" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob: mediastream:; font-src 'self'; connect-src 'self' wss://guild.palaver.social; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header Permissions-Policy "camera=(), geolocation=(), payment=()" always;
    add_header Strict-Transport-Security "max-age=31536000" always;
}

location /assets/ {
    try_files $uri =404;
    add_header Cache-Control "public, max-age=31536000, immutable" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob: mediastream:; font-src 'self'; connect-src 'self' wss://guild.palaver.social; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=31536000" always;
}

Öffne /etc/nginx/sites-available/palaver-guild in einem Editor.

sudo vi /etc/nginx/sites-available/palaver-guild

Inhalt:

server {
    listen 80;
    listen [::]:80;
    server_name guild.palaver.social;

    access_log /var/log/nginx/palaver/guild.access.log;
    error_log /var/log/nginx/palaver/guild.error.log;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name guild.palaver.social;

    ssl_certificate /etc/letsencrypt/live/palaver.social/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/palaver.social/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    access_log /var/log/nginx/palaver/guild.access.log;
    error_log /var/log/nginx/palaver/guild.error.log;

    client_max_body_size 64k;

    # server push, one WebSocket per client
    location = /api/events {
        proxy_pass http://127.0.0.1:8091;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
    }

    # profile pictures, server icon: 5 MiB
    location ~ ^/api/(me/avatar|server/icon)$ {
        client_max_body_size 5m;
        proxy_pass http://127.0.0.1:8091;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # attachments: 25 MiB
    location ~ ^/api/channels/[0-9]+/attachments$ {
        client_max_body_size 25m;
        proxy_pass http://127.0.0.1:8091;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:8091;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # LiveKit signalling, shared; the trailing slash strips /livekit
    location /livekit/ {
        proxy_pass http://127.0.0.1:7880/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
    }

    include snippets/palaver-web-guild.conf;
}

Aktiviere, prüfe und lade sie.

sudo ln -sf /etc/nginx/sites-available/palaver-guild /etc/nginx/sites-enabled/palaver-guild
sudo nginx -t && sudo systemctl reload nginx

11. Starten

Starte den Server.

sudo systemctl enable --now palaverd@guild

Prüfe, ob er antwortet; erwartet: JSON.

curl -fsS http://127.0.0.1:8091/api/registration

12. Erster Admin

Setze Name und Untertitel des Servers.

sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env profile -name 'Guild' -subtitle 'Guild subtitle'

Erzeuge einen Einladungscode für den ersten Admin; einmal nutzbar, 7 Tage gültig.

sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env invite -role Admin -valid 168h
  • registrieren: Palaver-App → Server https://guild.palaver.social → Registrieren → Code
  • erster Admin = Eigentümer des Servers
  • Konto ohne Admin registriert (Code ohne -role Admin): Rolle nachträglich vergeben
sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env grant <username> Admin

13. Prüfen

Führe doctor aus; erwartet: ok in jeder Zeile.

sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env doctor

Prüfe, ob der Hauptserver weiter antwortet.

sudo -u palaver /opt/palaver/bin/palaverd -env /opt/palaver/etc/palaverd.env doctor

Server auflisten

Namen der weiteren Server.

ls /opt/palaver/instances/

Domain und Port jedes Servers.

sudo sh -c 'grep -H -E "^PALAVER_(PUBLIC_URL|LISTEN)=" /opt/palaver/etc/palaverd.env /opt/palaver/instances/*/palaverd.env'

Name und Untertitel eines Servers.

sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env profile

Zustand jedes Servers.

systemctl list-units --all 'palaverd*'

Sicherung

  • Datenbank: alle Daten des Servers
  • /opt/palaver/instances/guild: Konfiguration, LiveKit-Schlüssel

Sichere die Datenbank.

sudo -u palaver-guild pg_dump -Fc palaver_guild > palaver-guild-2026-10-10.dump

Sichere die Konfiguration.

sudo tar -czf palaver-guild-etc-2026-10-10.tar.gz -C /opt/palaver/instances guild

Wiederherstellen

Voraussetzung: Server mit demselben Namen und derselben Domain, Schritte 1–12.

Halte ihn an.

sudo systemctl stop palaverd@guild

Destruktiv: Lösche die Datenbank des neuen Servers (palaver_guild, alle seine Konten und Nachrichten).

sudo -u postgres dropdb palaver_guild

Lege sie neu an und lade die Sicherung.

sudo -u postgres createdb -O palaver-guild palaver_guild
sudo -u palaver-guild pg_restore -d palaver_guild palaver-guild-2026-10-10.dump

Starte ihn.

sudo systemctl start palaverd@guild

Server entfernen

Zeige die Domain und den LiveKit-Schlüssel (<livekit-key>).

sudo grep -E '^PALAVER_(PUBLIC_URL|LIVEKIT_API_KEY)=' /opt/palaver/instances/guild/palaverd.env

Zeige das Zertifikat der Site; eigenes Zertifikat: Name guild.palaver.social.

grep ssl_certificate /etc/nginx/sites-available/palaver-guild

Vorher sichern: Sicherung.

Halte den Dienst an und deaktiviere ihn.

sudo systemctl disable --now palaverd@guild

Entferne nginx-Site und -Snippet und lade nginx neu.

sudo rm /etc/nginx/sites-enabled/palaver-guild /etc/nginx/sites-available/palaver-guild /etc/nginx/snippets/palaver-web-guild.conf
sudo nginx -t && sudo systemctl reload nginx

Destruktiv: Lösche die Datenbank palaver_guild (alle Konten, Nachrichten, Anhänge) und ihre Rolle palaver-guild.

sudo -u postgres dropdb palaver_guild
sudo -u postgres dropuser palaver-guild

Destruktiv: Lösche den LiveKit-Schlüssel des Servers; öffne /opt/palaver/etc/livekit.yaml in einem Editor.

sudo vi /opt/palaver/etc/livekit.yaml

Entferne:

  • unter keys: die Zeile <livekit-key>: …

Starte LiveKit neu; Gespräche auf den anderen Servern brechen kurz ab.

sudo systemctl restart livekit

Destruktiv: Lösche die Konfiguration /opt/palaver/instances/guild und den Benutzer palaver-guild.

sudo rm -r /opt/palaver/instances/guild
sudo userdel palaver-guild

Destruktiv: Lösche das Zertifikat; nur ein eigenes (guild.palaver.social), nie das Wildcard-Zertifikat.

sudo certbot delete --cert-name guild.palaver.social

Destruktiv: Lösche die nginx-Logs des Servers (/var/log/nginx/palaver/guild.*).

sudo sh -c 'rm /var/log/nginx/palaver/guild.access.log* /var/log/nginx/palaver/guild.error.log*'

Nach dem letzten weiteren Server

Entferne die Unit-Vorlage.

sudo rm /etc/systemd/system/palaverd@.service
sudo systemctl daemon-reload

Entferne das leere Verzeichnis.

sudo rmdir /opt/palaver/instances

Optional: unbekannte Namen zum Hauptserver

  • für: Wildcard-DNS-Eintrag und -Zertifikat für die Domain des Hauptservers
  • unbekannte Namen (Tippfehler): Umleitung zum Hauptserver statt zu nginx' erster Site

Prüfe, ob das Zertifikat *.palaver.social abdeckt.

sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/palaver.social/fullchain.pem

Prüfe, ob die Platzhalter-Site der Distribution default_server beansprucht.

grep default_server /etc/nginx/sites-enabled/default

Falls ja: deaktiviere sie.

sudo rm /etc/nginx/sites-enabled/default

Öffne /etc/nginx/sites-available/palaver-catch-all in einem Editor.

sudo vi /etc/nginx/sites-available/palaver-catch-all

Inhalt:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    location /.well-known/acme-challenge/ { root /var/www/html; }
    location / { return 301 https://palaver.social/; }
}

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name _;
    ssl_certificate /etc/letsencrypt/live/palaver.social/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/palaver.social/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    return 301 https://palaver.social/;
}

Aktiviere, prüfe und lade sie.

sudo ln -sf /etc/nginx/sites-available/palaver-catch-all /etc/nginx/sites-enabled/palaver-catch-all
sudo nginx -t && sudo systemctl reload nginx

Optional: Wildcard-Zertifikat

  • ein Zertifikat für die Hauptdomain und jeden Namen darunter (palaver.social, *.palaver.social)
  • certbot-DNS-Prüfung
  • palaver.social: certbot-Snap, Plugin certbot-dns-hetzner-cloud, Zugangsdaten /root/.secrets/hetzner.ini, Deploy-Hook systemctl reload nginx
  • nur ein certbot je Host

Liste die Zertifikate und die Namen auf, die sie abdecken.

sudo certbot certificates

Prüfe den Deploy-Hook des Wildcard-Zertifikats.

sudo grep -E 'authenticator|renew_hook|deploy_hook' /etc/letsencrypt/renewal/palaver.social.conf

Dateien

Pfad Inhalt
/etc/systemd/system/palaverd@.service Unit-Vorlage
/opt/palaver/instances/guild/palaverd.env Konfiguration (nur Benutzer palaver-guild)
/opt/palaver/etc/livekit.yaml ein Schlüssel je Server unter keys:
/etc/nginx/sites-available/palaver-guild nginx-Site
/etc/nginx/snippets/palaver-web-guild.conf Web-Client, CSP für guild.palaver.social
/etc/nginx/sites-available/palaver-catch-all unbekannte Namen (optional)
/var/log/nginx/palaver/guild.access.log, guild.error.log nginx-Logs (rotiert von Palavers /etc/logrotate.d/palaver)

Fehlersuche

doctor eines Servers.

sudo -u palaver-guild /opt/palaver/bin/palaverd -env /opt/palaver/instances/guild/palaverd.env doctor

Was palaverd meldet.

sudo journalctl --namespace=palaver -u palaverd@guild -n 100

Was nginx meldet.

sudo tail -n 50 /var/log/nginx/palaver/guild.error.log
Symptom Ursache
useradd: invalid user name Name zu lang (höchstens 24 Zeichen)
Sprache geht nur auf diesem Server nicht Schlüssel fehlt in livekit.yaml oder LiveKit nach Schritt 7 nicht neu gestartet
Sprache geht auf keinem Server livekit.yaml durch eine Änderung kaputt: sudo journalctl --namespace=palaver -u livekit -n 50
die Site eines anderen Servers antwortet server_name oder der Symlink in sites-enabled fehlt
Web-Client im Browser ohne Sprache CSP: wss://guild.palaver.social fehlt in palaver-web-guild.conf