Zum Inhalt springen

Betrieb

Eine Installation ist ihre Datenbank und ihre .env. Wer beides sichert, kann sie auf einem anderen Host wieder hinstellen. Dazu: was zu überwachen ist, wie man wieder hineinkommt, wenn niemand mehr Mail empfängt, und was im Alltag schiefgehen kann.

Sichern

Zwei Dinge, jede Nacht: ein Dump der Datenbank und eine Kopie der .env.

#!/bin/sh
# /etc/cron.daily/heinzel-pro-backup
set -eu
dir=/var/backups/heinzel-pro
install -d -m 700 "$dir"
cd /opt/heinzel-pro
docker compose exec -T db pg_dump -U heinzel -Fc heinzel_pro \
  > "$dir/heinzel-pro-$(date +%F).dump"
cp .env "$dir/env-$(date +%F)"
# Danach beides dorthin kopieren, wo der Host nicht ist: ein anderer Host, ein Bucket, ein Band.

Die Datei muss ausführbar sein, und die Zeile #!/bin/sh muss ganz oben stehen, sonst überspringt cron sie mit „Exec format error“. Prüfen Sie sie einmal so, wie cron sie aufruft:

$ sudo chmod 755 /etc/cron.daily/heinzel-pro-backup
$ sudo run-parts --verbose --regex '^heinzel-pro-backup$' /etc/cron.daily
$ ls -l /var/backups/heinzel-pro

Mit Ihrer eigenen PostgreSQL gehört der Dump in deren Sicherung; mit der mitgebrachten ist das Volume pgdata die Datenbank, und der Dump ist der Weg, nicht eine Kopie des Volumes im Lauf.

Wiederherstellen

Auf einem neuen Host nach der Installationsanleitung bis einschließlich der Compose-Datei, dann:

  1. Die gesicherte .env nach /opt/heinzel-pro/.env legen. Mit derselben HEINZEL_VERSION, mit der der Dump gezogen wurde; ein Upgrade kommt danach.

  2. Nur die Datenbank starten und den Dump einspielen.

    $ cd /opt/heinzel-pro
    $ sudo docker compose up -d db
    $ sudo docker compose exec -T db pg_restore -U heinzel -d heinzel_pro --clean --if-exists \
      < /var/backups/heinzel-pro/heinzel-pro-2026-10-24.dump
  3. Die Anwendung starten und prüfen.

    $ sudo docker compose up -d
    $ curl -s http://127.0.0.1:4000/health

    Anmelden, einen Server testen: Meldet sich Heinzel an, sind die Schlüssel lesbar, und die Wiederherstellung ist vollständig.

Den DNS-Namen auf den neuen Host umstellen, den Proxy einrichten, und die VPN-Verbindungen: Nach Weg 1 melden sich die Gegenstellen von selbst wieder, sobald der Name auflöst; nach Weg 2 ruft Heinzel den Router an.

Den Vault-Schlüssel rotieren

  1. Neuen Schlüssel erzeugen: openssl rand -base64 32
  2. In der .env den alten Schlüssel nach HEINZEL_VAULT_KEY_RETIRED verschieben und HEINZEL_VAULT_KEY_RETIRED_ID auf die alte Id setzen (bisher 1); den neuen als HEINZEL_VAULT_KEY mit HEINZEL_VAULT_KEY_ID=2 eintragen.
  3. Neu starten, dann jedes Geheimnis neu verschlüsseln: sudo docker compose exec app bin/rotate-vault-key
  4. Die RETIRED-Zeilen entfernen und noch einmal starten. Die .env neu sichern.

Überwachen

GET /health liefert status, version, rules_version, database und pending_migrations und antwortet nur dann mit 200, wenn die Datenbank erreichbar ist und keine Migration aussteht. Ein Monitor, der alle Minute von außen https://heinzel.example.com/health auf 200 prüft, reicht für die Frage „läuft es“. Anfragen auf localhost bleiben auch mit FORCE_SSL unverschlüsselt, für die Probe vom Host.

In der Anwendung sagt Installation, Protokoll im Kasten „Betrieb“ dasselbe und mehr: Lizenz, Mailversand, Not-Aus und die Hintergrundjobs (wartend, geplant, läuft, wird wiederholt, aufgegeben). Ein wachsender Zähler bei „wird wiederholt“ ist ein Mailserver oder ein Webhook, der nicht annimmt.

Installation, Protokoll: der Kasten Betrieb mit Version, Regelwerk, Lizenz, Mailversand, Datenbank, Not-Aus und den Zählern der Hintergrundjobs, darunter die Audit-Kette der Installation mit Anmeldungen und Konten
Der Blick für den Betrieb, und darunter die Kette der Installation: Anmeldungen, Konten, Mandanten.

Umgekehrt nimmt Heinzel Pro Alarme Ihres Monitorings an und sieht auf dem betroffenen Server nach, siehe Alarme aus dem Monitoring.

Logs

sudo docker compose logs app zeigt das Log der Anwendung; LOG_LEVEL in der .env stellt es von info auf debug oder warning. Was Benutzer tun, steht nicht darin, sondern im Audit-Protokoll in der Datenbank: je Mandant eine Kette, für die Installation eine eigene. Beide lassen sich als CSV exportieren und prüfen:

$ sudo docker compose exec app bin/heinzel_pro eval HeinzelPro.Release.verify_audit

Soll das Protokoll in ein SIEM, gibt ein Administrator der Installation einem Mandanten unter Einstellungen, Anbindungen einen HTTPS-Endpunkt; jedes Ereignis geht dann binnen einer Minute als eine Zeile JSON dorthin, mit Position und Hash in der Kette. Die Aufbewahrung steht je Mandant unter Einstellungen, Allgemein; das Löschen nach Frist ist der einzige Weg, Zeilen zu entfernen, und wird selbst protokolliert.

Ausgesperrt

Kommt keine Mail mehr an, oder kennt niemand mehr das Passwort des einzigen Administrators, erzeugt ein Operator mit Shell-Zugang auf dem Host einen Anmeldelink für jedes Konto:

$ sudo docker compose exec app bin/login-link admin@example.com

Der Link gilt einmal und 15 Minuten. Danach unter Konto Passwort oder zweiten Faktor neu einrichten. Hat jemand sein Gerät mit dem Passkey oder der Authenticator-App verloren, setzt ein Administrator der Installation unter Installation, Konten Passkeys und zweiten Faktor dieses Kontos zurück oder schickt einen Anmeldelink. Dort lässt sich auch eine Person in einen Mandanten setzen, dessen einziger Administrator ausgesperrt ist.

Mail später ändern

Mail ist in der .env eingerichtet, weil sie funktionieren muss, bevor sich jemand anmelden kann. Ein neuer Weg heißt: Zeilen ändern, sudo docker compose up -d, dann unter Installation, Mailversand eine Testmail. Die Seite zeigt, was gerade gilt.

Installation, Mailversand: die aktuelle Einrichtung, ein Feld für eine Testmail, und die vier Wege mit ihren Variablen
Mailversand: aktuelle Einrichtung, Testmail, und welche Variablen welcher Weg braucht.

Der Not-Aus

Die Administratoren eines Mandanten haben ihn unter Einstellungen, Richtlinie, die der Installation unter Installation, Mandanten für alle Mandanten auf einmal. Er stoppt jede Session, nimmt jeden wartenden Befehl zurück, bricht laufende Befehle auf den Hosts ab, und von da an startet nichts, bis er aufgehoben wird, auch nach einem Neustart nicht. Er verlangt einen Grund, steht im Audit-Protokoll, und ein Banner auf jeder Seite zeigt, dass er an ist.

Was die Installation verlässt

Eine Installation öffnet Verbindungen zu diesen Stellen und zu keiner anderen. Es gibt keine Telemetrie, keine Nutzungszahlen und keine Lizenzprüfung nach außen; ein Test in der Testsuite des Produkts schlägt fehl, sobald der Code eine Verbindung öffnet, die hier nicht steht.

  • Modellanbieter: die Adresse jedes Anbieters, den Sie anlegen, voreingestellt api.anthropic.com oder api.openai.com, oder ein Server im eigenen Netz.
  • Ihre Server: SSH zu den Hosts, die Sie anlegen, auf dem Port, den Sie angeben.
  • VPN-Verbindungen: WireGuard zur Gegenstelle aus dem Block, den Sie eingefügt haben; eine Tailscale-Verbindung spricht mit dem Koordinationsserver und den Relays von Tailscale oder mit Ihrem Headscale.
  • Mail: der Mailserver oder Maildienst, den Sie eingerichtet haben.
  • Endpunkte, die Sie eintragen: der Webhook des Teams, der Kollektor für das Audit-Protokoll, das Monitoring, das Markierungen annimmt.
  • Single Sign-on: der OpenID-Connect-Aussteller, den Sie einrichten.
  • Regelwerk: api.github.com und github.com, stündlich für die Prüfung auf ein neueres Regelwerk und beim Aktualisieren, ohne Konto und ohne Angaben über die Installation. Wer das automatische Update ausschaltet und das Archiv hochlädt, öffnet diese Verbindung nie.

Docker selbst holt das Image von ghcr.io, wenn Sie es ziehen.

Heinzel Pro entfernen

Für eine Installation nach der Installationsanleitung, etwa nach der Testphase. Die Daten stecken im Volume pgdata; die betreuten Server behalten Heinzels Zugang, bis Sie ihn dort entfernen.

  1. Eine letzte Sicherung, wenn Sie die Daten behalten wollen. Das Skript aus Sichern einmal von Hand, dann Dump und .env vom Host kopieren. Das Audit-Protokoll ist danach nur noch im Dump.

  2. Container, Datenbank und Images entfernen.

    $ cd /opt/heinzel-pro
    $ sudo docker compose down -v --rmi all
    $ cd / && sudo rm -rf /opt/heinzel-pro /var/backups/heinzel-pro
    $ sudo rm -f /etc/cron.daily/heinzel-pro-backup

    -v löscht das Volume mit der Datenbank, unwiderruflich.

  3. Den Proxy und das Zertifikat. Bei nginx die Seite entfernen und das Zertifikat löschen, bei Caddy den Block aus dem Caddyfile:

    $ sudo rm /etc/nginx/sites-enabled/heinzel-pro /etc/nginx/sites-available/heinzel-pro
    $ sudo nginx -t && sudo systemctl reload nginx
    $ sudo certbot delete --cert-name heinzel.example.com
  4. Die Firewall und die Mail. Die Regeln aus der Installation wieder schließen, und wer Postfix auf dem Host angebunden hat, nimmt das Compose-Netz aus mynetworks:

    $ sudo ufw delete allow 51900:51919/udp
    $ sudo ufw delete allow from 172.30.79.0/24 to any port 25 proto tcp
  5. Auf jedem Server, den Heinzel betreut hat. Die Schlüsselzeile, die auf heinzel-pro endet, aus authorized_keys löschen. Wer dafür den Benutzer heinzel angelegt hat, entfernt ihn mit seiner sudo-Regel:

    $ sudo deluser --remove-home heinzel
    $ sudo rm -f /etc/sudoers.d/heinzel

    VPN-Verbindungen auf Routern und Servern der Gegenseite bleiben stehen, bis Sie sie dort löschen.

  6. Docker, wenn es nur für Heinzel Pro da war. Auf Debian sudo apt purge docker.io docker-compose, dann sudo apt autoremove --purge, nachdem apt-get -s autoremove gezeigt hat, dass es nur Dockers Pakete nimmt. /var/lib/docker und /var/lib/containerd löschen Sie von Hand. Die Firewall-Ketten, die Docker angelegt hat (DOCKER, DOCKER-USER), und die Weiterleitung (net.ipv4.ip_forward) räumt erst ein Neustart des Hosts ab.

Prüfen Sie am Ende, dass nichts mehr lauscht: ss -lntu | grep -E ':4000|:519' bleibt leer.

Fehlersuche im Alltag

Was Sie sehenWoran es liegtWas zu tun ist
Ein Befehl steht auf „unterbrochen, Ergebnis unbekannt“Heinzel Pro wurde neu gestartet, während der Befehl im SSH-Kanal lief: ein Lesebefehl, oder ein Host ohne Home-Verzeichnis für den Job.Den Befehl in der Session noch einmal vorschlagen lassen; ein Befehl, der etwas ändert, läuft als Job und wird abgeholt. Vor dem nächsten Neustart ankündigen.
Ein Server ist rot markiert: Host-Key geändertDer Host wurde neu installiert, oder jemand steht dazwischen.Erst nachfragen, warum. Dann auf der Serverseite „Neuem Host-Key vertrauen“.
Sessions starten, aber jeder Modellaufruf schlägt fehlDer Schlüssel des Anbieters ist abgelaufen, das Kontingent aufgebraucht, oder das Modell wurde vom Anbieter abgeschaltet.Einstellungen, Modellanbieter: Testen. Ein Modell auf dem Weg hinaus ist dort markiert, mit Nachfolger und einem Klick zum Umstellen.
Keine neuen Sessions, ein Banner nennt die LizenzDie Testphase oder der Schlüssel ist abgelaufen, die Kulanzfrist vorbei.Neuen Schlüssel unter Installation, Einstellungen, Lizenz einfügen. Alles Aufgezeichnete bleibt lesbar.
Berichte und Einladungen bleiben aus; „wird wiederholt“ zählt hochDer Mailserver nimmt nicht an: Passwort geändert, Absender abgelehnt, Zertifikat abgelaufen.Installation, Mailversand, Testmail; das Log nennt die Antwort des Servers.
Eine VPN-Verbindung steht nach einem Neustart erst nach Minuten wiederHeinzel ruft die Gegenseite dort an, wo sie zuletzt war; hat sich deren Adresse geändert, muss sie sich selbst melden.Warten. Dauert es länger, sudo wg show heinzel auf der Gegenseite.
Nach einem Upgrade fehlt ein Zugriff, den es gabEin Recht wurde in einer Version zu einem benannten Recht, das ein Administrator abgewählt hatte.Benutzer, Rechte: die Zeile auf „ja“ oder „wie es steht“. Das Changelog der Version nennt solche Schritte.
Die Platte des Hosts läuft vollAlte Images, oder die Datenbank wächst mit Audit-Protokoll und Sessions.docker image ls und alte Versionen entfernen; die Aufbewahrung unter Einstellungen, Allgemein setzen.

Für alles andere: support@heinzel-pro.de mit der Zeile aus dem Log und der Version aus /health.