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 Anmeldecode für jedes Konto:

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

Der Code gilt einmal und 15 Minuten; Sie geben ihn mit der Adresse auf der Anmeldeseite ein. 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 Anmeldecode. 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, Impressum, und welche Variablen welcher Weg braucht.

Jede Mail hat denselben Rahmen: Anrede mit Namen, wo die Adresse zu einem Konto gehört, sonst „Guten Tag“, als Gruß der Absendername aus MAIL_FROM_NAME, darunter ein Fuß mit Absender, Adresse der Installation, dem Grund, warum die Mail kommt, und dem Impressum. Das Impressum steht auf derselben Seite im Feld Impressum unter jeder Mail; die Vorschau darunter zeigt die Testmail, wie sie ankommt. Bleibt das Feld leer, fehlt das Impressum.

Auf derselben Seite steht der Tagesbericht: Jeden Morgen nach sechs Uhr (Berliner Zeit) bekommt jeder Administrator der Installation den Vortag in Zahlen, in seiner Sprache. Oben stehen vier Kacheln: Konten, Mandanten und Server mit den neuen des Tages groß und dem Stand insgesamt klein darunter (dazu die gelöschten, wenn es welche gab), und die Bestellungen des Tages mit ihrer Summe im Monat und den Kündigungen. Unter den Zahlen steht jede Person dahinter, damit ein Anruf am nächsten Tag aus der Mail beantwortet ist: jedes neue Konto mit Name und Adresse, der Uhrzeit der Registrierung, wie lange es schon im System ist, ob die Adresse bestätigt ist, und jedem seiner Mandanten mit Tarif, Rolle, ob ein Modell verbunden ist und wie viele Server und Sessions es gibt; jedes gelöschte Konto mit Uhrzeit, von wem und nach wie langer Zeit; jeder Tarifwechsel von was zu was und wer ihn gemacht hat; und die gelöschten Mandanten. Eine Zeile darunter zeigt, wie viel gestern gearbeitet wurde: aktive Mandanten, wie viele Personen in den Seiten waren, die Arbeitszeit, wie der Tarif sie zählt, mit dem Anteil des Agenten, begonnene Sessions und ausgeführte Befehle; darunter eine Tabelle der zehn Mandanten mit der meisten Arbeitszeit der letzten sieben Tage, jeder mit Tarif und der Adresse seines Eigentümers. Die neuen Konten stehen ebenso in einer Tabelle. Gezählt wird pro Mandant, nie pro Person: Wer in einem Mandanten wie viel gearbeitet hat, ist Sache des Mandanten. Der Betreff nennt nur die neuen Konten und die neuen Abos des Tages mit ihrer Summe im Monat (ein Jahresabo zählt mit einem Zwölftel); der Tag steht in der ersten Zeile der Mail. Er ist von Anfang an an und wird dort mit einem Haken abgeschaltet. Ein Neustart zur Morgenstunde kostet keinen Bericht: Der nächste Stundenlauf holt ihn nach.

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, nur das Abholen eines verlängerten Schlüssels; 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.
  • Webseiten: eine Seite, die der Agent mit fetch_url liest, von der Installation selbst geholt und, falls eingerichtet, über den Headless-Browser.
  • 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.
  • Lizenzverlängerung: heinzel-pro.de, viermal am Tag, nur mit Lizenzschlüssel. Sie schickt den Schlüssel, den die Installation hat, und sonst nichts; unter Installation, Einstellungen, Lizenz lässt sie sich abschalten.

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
    $ sudo ufw delete allow from 172.30.79.0/24 to any port 11434 proto tcp   # nur mit Ollama auf dem Host
    $ sudo postconf -e "mynetworks = $(postconf -h mynetworks | sed 's| 172.30.79.0/24||')"
    $ sudo systemctl reload postfix
  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.Einrichten, Modelle: 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.
Die Lizenzseite sagt, das Abholen des verlängerten Schlüssels scheitert; eine Mail oder ein Banner sagt es auchDie Installation erreicht heinzel-pro.de nicht: eine Firewall, ein Proxy, kein Internet.HTTPS (Port 443) zu heinzel-pro.de erlauben, oder den Schlüssel aus der Mail jedes bezahlten Zeitraums unter Installation, Einstellungen, Lizenz eintragen.
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.