7. Pepsi betreiben

In diesem Kapitel geht es um die Wochen nach der Installation: wo die Logs liegen, was zu beobachten ist und was zu sichern ist, damit eine verlorene Platte Sie einen Nachmittag kostet und nicht die Schlüssel aller Benutzer. Das Upgrade ist in Upgrade beschrieben, und was zu tun ist, wenn etwas schiefgeht, in Fehlersuche.

7.1. Logs

Jedes Pepsi-Programm schreibt seine Logs auf die Standardfehlerausgabe. Unter den mitgelieferten systemd-Units ist das das Journal, eine Unit je langlebigem Programm:

journalctl -u pepsi-ingress -u pepsi-dispatch -u pepsi-httpd --since today
journalctl -u pepsi-dispatch -f          # follow the pipeline live
journalctl -u 'pepsi-*' -p warning       # warnings and errors, every unit

Die Stage-Programme haben keine eigenen Units: Der Dispatcher startet sie als Worker, und sie erben seine Standardfehlerausgabe, daher erscheint alles, was eine Stage protokolliert, unter pepsi-dispatch. Jeder Eintrag nennt das Programm, das ihn geschrieben hat. Die Timer (Schlüsselauffrischung, Quota-Abgleich, Log-Bereinigung, …) protokollieren unter ihren eigenen .service-Namen; systemctl list-timers 'pepsi-*' listet sie mit ihrem letzten und nächsten Lauf auf.

7.1.1. Log-Level

Die Level sind error, warn, info (der Standard), debug und trace.

  • [pepsi] LOG setzt das Level für jedes Programm auf einmal, einschließlich der Stage-Worker, die vom Dispatcher keine Kommandozeilen-Flags erhalten. Starten Sie die Dienste nach einer Änderung neu (der Dispatcher startet seine Worker selbst neu).

  • -L LEVEL (vor dem Unterbefehl, z. B. pepsi-queue -L debug list) überschreibt es für einen Prozess; so sieht man üblicherweise bei einem von Hand gestarteten Werkzeug genauer hin.

  • -v lässt zusätzlich die eigenen Einträge der Datenbank-, HTTP- und TLS-Bibliotheken durch, die sonst ausgeblendet sind. Nützlich bei einem Verbindungsproblem; sehr gesprächig.

  • [pepsi] LOG_JSON = yes schreibt ein JSON-Objekt je Eintrag, für einen Log-Aggregator.

info schreibt eine oder wenige Zeilen je Nachricht und Stage. warn behält, was Aufmerksamkeit braucht, und passt zu einem ausgelasteten Host. debug und trace können Umschlagadressen und Protokolldetails enthalten; lassen Sie sie nicht eingeschaltet.

7.1.2. Was aufbewahrt wird, und wie lange

Die Aufbewahrung des Journals ist Sache von systemd (journald.conf). In der Datenbank hält Pepsi:

  • das Prüfprotokoll (pepsi.event_log): wer was geändert hat, über die Kommandozeilenwerkzeuge, die API und die Konsole, täglich durch pepsi-log-prune.timer bereinigt;

  • kein Zustellprotokoll je Nachricht, es sei denn, Sie schalten [pepsi] MAIL_LOG ein (siehe pepsi.conf(5) und die dortige Datenschutzwarnung). Eine zugestellte Nachricht wird aus der Warteschlange gelöscht, ohne es ist das Journal also der einzige Nachweis, dass eine Nachricht durchgelaufen ist;

  • TLS-Reporting-Zähler (pepsi.tls_session), bereinigt durch pepsi-tlsrpt-prune.timer.

7.2. Überwachung

Zwei Dinge sagen Ihnen, ob Mail fließt: die Warteschlange und die Nachrichten, die die Pipeline aufgegeben hat.

7.2.1. pepsi-status

pepsi-status ist eine rein lesende Zusammenfassung der Warteschlange, der hängenden Nachrichten mit dem jeweiligen Grund, der kumulierten Zähler je Stage, der ausgehenden TLS-Ergebnisse, der Postfach-Quotas und der jüngsten Schlüsseländerungen. Es kann jederzeit gefahrlos ausgeführt werden, und --json liefert denselben Bericht an ein Skript:

pepsi-status
pepsi-status --json | jq .problem_total

problem_total ist die Anzahl der Nachrichten in failed oder timeout. Der Dispatcher versucht sie nie erneut; pepsi-failure-bouncer.timer bounct sie an ihre Absender, sobald sie seit MIN_AGE gescheitert sind (eine Stunde; siehe Nachrichten in failed oder timeout), sodass der Zähler normalerweise von selbst wieder auf null fällt. Steigt er weiter, scheitert dieselbe Stage an einer Nachricht nach der anderen, und die Ursache muss behoben werden. Ein Fehler des Hosts zeigt sich hier zunächst nicht: Seine Nachrichten warten paused an der scheiternden Stage (pepsi_pause_backlog), mit dem Fehler in state.last_error, und werden bis zum MAX_LIFETIME der Stage erneut versucht.

7.2.2. /metrics

pepsi-httpd liefert Prometheus-Metriken unter /metrics auf einem Listener mit ADMIN = yes (und nirgends sonst; es verlangt keine Zugangsdaten, markieren Sie also einen Listener, den nur Ihr Überwachungssystem erreichen kann). Die Reihen:

Reihe

Typ

Bedeutung

pepsi_stage_active_messages{stage}

gauge

Nachrichten, die gerade verarbeitet werden.

pepsi_pause_backlog{stage}

gauge

Nachrichten, die auf einen erneuten Versuch warten: ein entfernter Server, der zurückgestellt hat, ein Schlüssel, der gesucht wird, eine erwartete Zahlung.

pepsi_stage_messages_total{stage}

counter

Nachrichten, die eine Stage verarbeitet hat.

pepsi_stage_duration_seconds_total{stage}

counter

In der Stage verbrachte Zeit; geteilt durch die vorige Reihe ergibt sich der Durchschnitt.

pepsi_stage_timeouts_total{stage}

counter

Worker, die wegen Überschreitung des MAX_RUNTIME der Stage beendet wurden.

pepsi_stage_crashes_total{stage}

counter

Worker, die bei der Bearbeitung einer Nachricht abgestürzt sind.

pepsi_messages_processed_total

counter

Nachrichten, die die Pipeline verlassen haben.

pepsi_stages_executed_total

counter

Stage-Durchläufe, alle Stages zusammen.

Die Zähler schreibt der Dispatcher alle STATS_INTERVAL sowie wenn er untätig wird, sie hinken also bis zu diesem Intervall hinterher. Es sind Summen seit dem Anlegen des Schemas, nicht je Abfrage; verwenden Sie rate().

7.2.3. Worauf alarmiert werden sollte

Bedingung

Warum

pepsi-status --json meldet länger als MIN_AGE plus zehn Minuten problem_total über null

Eine Nachricht ist gescheitert und wurde nicht gebounct: pepsi-failure-bouncer.timer läuft nicht (systemctl list-timers), oder der Bounce selbst ist gescheitert. Nachrichten, die an der Bounce-Stage scheitern, werden nicht erneut gebounct und warten auf Sie.

pepsi_pause_backlog einer Stage, die keine Zustell-Stage ist, wächst, oder das Journal wiederholt „failed at stage … retrying“

Ein Fehler des Hosts an dieser Stage (ein unlesbarer Schlüssel oder eine unlesbare Vorlage, ein Helper, der nicht starten kann, eine abgelehnte Anweisung). Ihre Nachrichten werden bis MAX_LIFETIME zurückgehalten, nicht gebounct; state.last_error sagt, warum.

Der Dispatcher protokolliert für eine Stage „the worker refused to start“

Die Konfiguration der Stage oder ein Geheimnis, das sie liest, lässt sich nicht mehr parsen; ihre Nachrichten werden zurückgehalten, bis es wieder geht.

pepsi_pause_backlog einer Relay-Stage wächst über Stunden

Der nächste Hop, DNS oder der ausgehende Port 25 ist nicht erreichbar. Die Wiederholungen laufen bis MAX_LIFETIME, danach erhalten die Absender Bounces.

rate(pepsi_stage_crashes_total[15m]) oder rate(pepsi_stage_timeouts_total[15m]) über null

Ein Stage-Programm schlägt fehl; seine Nachrichten enden als failed oder timeout.

rate(pepsi_messages_processed_total[1h]) bei null, obwohl Mail erwartet wird

Nichts verlässt die Pipeline: Der Dispatcher ist ausgefallen oder eine Stage hängt.

Eine pepsi-*-Unit im Zustand failed (systemctl --failed)

Die langlebigen Units versuchen es endlos erneut, eine gescheiterte ist daher meist ein Timer oder ein einmaliger Job.

Journal-Einträge auf error

Alles, was als Fehler protokolliert wird, verdient einen Blick.

Der Ablauf der TLS-Zertifikate

certbot erneuert sie; eine fehlgeschlagene Erneuerung protokolliert certbot, nicht Pepsi.

Freier Platz dort, wo PostgreSQL seine Daten ablegt

Unterhalb von [pepsi] MIN_FREE_SPACE (Standard 1 GiB) weist pepsi-ingress neue Mail mit einem temporären Fehler ab.

7.3. Sicherung und Wiederherstellung

Warnung

Ein Datenbank-Dump allein ist keine Sicherung von Pepsi. Die privaten Schlüssel in der Datenbank sind unter einem Schlüsselverschlüsselungsschlüssel verschlüsselt, der absichtlich außerhalb der Datenbank liegt, in /etc/pepsi/secrets.d/pepsi-crypto.secret. Geht diese Datei verloren, gehen die gespeicherten privaten Schlüssel aller Benutzer mit ihr verloren: Nichts kann sie wieder öffnen, und pepsi-setup erzeugt keinen Ersatz, solange verschlüsselte Schlüssel existieren.

7.3.1. Was zu sichern ist

Was

Was ohne es verloren geht

/etc/pepsi/secrets.d/, vor allem pepsi-crypto.secret

pepsi-crypto.secret: jeder gespeicherte private Schlüssel (siehe oben). Die anderen Fragmente: der SRS-Schlüssel (Bounces zu Mail, die vor dem Verlust weitergeleitet wurde, werden abgewiesen), der Secure-Link-Pepper (noch nicht abgeholte sichere Nachrichten lassen sich nicht mehr öffnen), der Herkunftsnachweis-Schlüssel (Zahlungsaufforderungen zu Mail, die vor dem Verlust gesendet wurde, werden nicht mehr als unsere erkannt) sowie die Passwörter und Tokens, die Sie eingegeben haben.

Die PostgreSQL-Datenbank (standardmäßig pepsi)

Die Warteschlange, benutzerbezogene Einstellungen, Whitelists, Mailinglisten und ihr Archiv, der Schlüsselspeicher, Quotas, Administratorkonten und API-Tokens, das Prüfprotokoll.

/etc/pepsi/ (der Rest)

Die Konfiguration. Sie lässt sich mit dem Assistenten neu schreiben, aber nicht schnell.

/var/pepsi/keys/

Die DKIM- und ARC-Signierschlüssel. Neue lassen sich erzeugen, aber jeder muss erneut im DNS veröffentlicht werden, und davor signierte Mail besteht DKIM nicht.

/var/lib/pepsi-wallets/

Mit pepsi-stage-auto-pay im gemeinsamen Wallet-Modus: die Wallets und das Geld darin. Im Modus local-user liegen die Wallets in den Home-Verzeichnissen der Benutzer.

/etc/letsencrypt/

Nichts Dauerhaftes; certbot beschafft neue Zertifikate. Behalten Sie es trotzdem, wenn Sie DANE-(TLSA-)Einträge für den aktuellen Schlüssel veröffentlichen.

/var/pepsi/tokens, /var/pepsi/token-refresh, /var/pepsi/tls

Nur bei einem Smarthost mit OAuth oder Client-Zertifikat: Das Refresh-Token muss erneut autorisiert werden.

Die Postfächer der Benutzer (Maildir oder der Speicher des MDA hinter LMTP) gehören nicht zu Pepsi und gehören in Ihre gewöhnliche Sicherung der Home-Verzeichnisse.

Bewahren Sie die Dateien und den Datenbank-Dump zusammen auf, und aus demselben Zeitpunkt: Ein Dump, der neuer ist als secrets.d, kann Schlüssel enthalten, die unter einem Schlüsselverschlüsselungsschlüssel verpackt sind, den die Dateisicherung nicht hat. Beide enthalten Geheimnisse; speichern Sie sie verschlüsselt und nur für root lesbar.

7.3.2. Eine Sicherung anlegen

sudo -u pepsi-owner pg_dump --format=custom pepsi > pepsi-$(date +%F).dump
tar -C / -czpf pepsi-files-$(date +%F).tar.gz \
    etc/pepsi var/pepsi var/lib/pepsi-wallets

Führen Sie dies als root aus, damit tar secrets.d lesen kann. pg_dump nimmt einen konsistenten Schnappschuss, während die Dienste laufen; die Dateien ändern sich selten (ein neuer Schlüssel, eine Konfigurationsänderung), daher genügt es, sie kurz vor oder nach dem Dump zu sichern. Datenbankname und Eigentümer sind die aus [pepsi-postgres].

Der Dump, den pepsi-setup schema --backup-dir vor einem Upgrade anlegt (siehe Sicherung vor einem Schema-Upgrade), umfasst nur die Datenbank, und nur das Schema pepsi; er ersetzt dies nicht.

7.3.3. Wiederherstellen

Stellen Sie auf einem Host wieder her, auf dem dasselbe Pepsi-Release läuft, aus dem die Sicherung stammt (führen Sie danach ein Upgrade durch, wenn Sie möchten), in dieser Reihenfolge:

  1. Installieren Sie Pepsi. Das Debian-Paket legt die Dienstkonten und Datenbankrollen an; bei einer Installation aus den Quellen legen Sie sie wie in Installation an.

  2. Halten Sie alles an: systemctl stop pepsi.target.

  3. Stellen Sie die Dateien wieder her, secrets.d zuerst. tar bewahrt Eigentümer nach Namen, daher müssen die Konten existieren, bevor Sie auspacken:

    tar -C / -xzpf pepsi-files-2026-10-01.tar.gz
    
  4. Stellen Sie die Datenbank in eine leere Datenbank wieder her, die dem Schema-Eigentümer gehört:

    sudo -u postgres dropdb --if-exists pepsi
    sudo -u postgres createdb -O pepsi-owner pepsi
    sudo -u pepsi-owner pg_restore -d pepsi pepsi-2026-10-01.dump
    
  5. Führen Sie pepsi-setup -c /etc/pepsi/pepsi.conf run aus. Es wendet die Rollenberechtigungen erneut an, schreibt neu, was außerhalb beider Sicherungen liegt (die systemd-Drop-ins, die den Diensten die TLS-Zertifikate übergeben), und prüft die DNS-Einträge gegen die wiederhergestellten Schlüssel. Einen bereits vorhandenen Schlüssel oder ein Geheimnis ersetzt es nicht.

  6. Starten Sie die Dienste: systemctl start pepsi.target.

Wenn pepsi-setup meldet, es weigere sich, einen Schlüsselverschlüsselungsschlüssel zu erzeugen, weil Identitäten existieren, dann wurde secrets.d/pepsi-crypto.secret nicht wiederhergestellt oder ist nicht die Datei, die zu dieser Datenbank gehört. Umgehen Sie das nicht: Suchen Sie die richtige Datei.