26 KiB
Betriebshandbuch
Was tun, wenn etwas schiefgeht? Diese Seite ist für den Ernstfall geschrieben, auch für jemanden, der Munin nicht selbst gebaut hat. Jeder Abschnitt beginnt mit dem, was man sieht, und endet mit dem, was man tut.
Die Befehle gehen vom Docker-Aufbau aus dem README aus: Container munin,
Volume unter /data. Ohne Docker gilt dasselbe mit dem eigenen DATA_DIR
statt /data, und die Ausgabe des Prozesses statt docker logs.
Munin läuft entweder mit SQLite (Standard, eine Datei je Workspace) oder
mit PostgreSQL (DB_DRIVER=postgres, ein Schema je Workspace). Wo sich
die Schritte unterscheiden, steht beides da. Welches gilt, zeigt die Zeile
Speicher: beim Start.
Für die PostgreSQL-Befehle: $DATABASE_URL ist dieselbe Adresse wie beim
Server, das Passwort kommt aus PGPASSWORD oder ~/.pgpass.
Auf einen Blick
| Was | Wo |
|---|---|
| Kontroll-Datenbank | DATA_DIR/control.db: Workspaces, welcher Key zu welchem Workspace gehört, Workspace-Admins |
| Ein Workspace | DATA_DIR/workspaces/<workspace-id>.db: Projekte, Einträge, Keys, Protokoll und Suchprotokoll dieses Workspace |
| Zugehörige Hilfsdateien | …db-wal und …db-shm neben jeder Datenbank. Sie gehören im laufenden Betrieb dazu, nie einzeln löschen, solange der Server läuft |
| Logs | Standardausgabe des Prozesses: docker logs munin |
| Sicherungen | BACKUP_DIR/<zeitstempel>/ von scripts/backup.sh, siehe README „Datensicherung“ |
| Markdown-Export | EXPORT_DIR, falls gesetzt. Lesbar, aber keine vollständige Sicherung (keine Keys, keine Historie) |
| Geheimnisse | SESSION_SECRET, ADMIN_TOKEN als Umgebungsvariablen, siehe README „Konfiguration“ |
| Lebenszeichen | GET /live antwortet ok, solange der Prozess läuft (/healthz ist der alte Name) |
| Bereitschaft | GET /ready antwortet ok, wenn die Datenbank antwortet, sonst 503 nicht bereit. Der Grund steht im Log (Bereitschaft: …) |
Mit PostgreSQL:
| Was | Wo |
|---|---|
| Kontroll-Datenbank | Schema munin_control in der Datenbank aus DATABASE_URL |
| Ein Workspace | Schema munin_ws_<id> (Bindestrich der ID als _). Der Name steht als Kommentar daran und unter Workspaces in der Weboberfläche |
| Sicherungen | BACKUP_DIR/<zeitstempel>/control.dump und …/workspaces/munin_ws_<id>.dump |
Welcher Workspace hinter einer ID steckt:
# SQLite
sqlite3 /data/control.db "SELECT id, name FROM workspaces;"
# PostgreSQL: Schema und Name
psql "$DATABASE_URL" -c "SELECT nspname, obj_description(oid, 'pg_namespace') FROM pg_namespace WHERE nspname LIKE 'munin%' ORDER BY 1;"
Läuft er?
curl -s https://<server>/live # Prozess läuft? erwartet: ok
curl -s https://<server>/ready # Datenbank erreichbar? erwartet: ok
docker logs --tail 50 munin
Beim Start schreibt Munin diese Zeilen ins Log. Fehlen sie, ist der Start gescheitert:
Munin läuft auf http://localhost:8080 (3 Workspaces)
Speicher: SQLite — Workspaces: /data/workspaces, Kontrolle: /data/control.db
Schreibbudget: 1000 Aufrufe je Projekt und Stunde (Vorgabe)
Mit PostgreSQL lautet die zweite Zeile etwa
Speicher: PostgreSQL — db.intern:5432/munin (Schemas munin_control, munin_ws_…).
/live prüft nur, ob der Prozess antwortet. /ready prüft zusätzlich die
Kontroll-Datenbank (unter PostgreSQL also auch, ob der Datenbankserver
erreichbar ist), aber nicht jede einzelne Workspace-Datei. Siehe dazu
Datenbank beschädigt und
PostgreSQL ist weg.
Mehr Einzelheiten zeigt dem Plattform-Admin die Seite System
(/admin/system). Ganz oben steht der Kasten Zustand mit allem, was zu
tun ist: Verbindungen knapp, WAL-Datei zu groß, Export fehlgeschlagen oder
überfällig, Sortierung der PostgreSQL-Datenbank falsch, Keys laufen ab oder
sind ungenutzt. Darunter die Aktivität der letzten 30 Tage, Bestand und Platz
je Workspace, Datenbank, Bereitschaft, offene Verbindungen je Workspace gegen
das Budget (mit PostgreSQL), wie oft auf Verbindungen gewartet wurde, Fassung,
Schema-Stand und Laufzeit.
Der Container-Healthcheck im Dockerfile hängt an /live: Fehlt nur die
Datenbank, soll der Container nicht neu starten, das hülfe nicht. Ein
Load-Balancer oder Monitoring gehört an /ready.
Server startet nicht
Man sieht: Der Container beendet sich sofort, /live antwortet nicht.
docker logs munin zeigt die Ursache in der letzten Zeile:
| Meldung | Ursache | Abhilfe |
|---|---|---|
SESSION_SECRET ist nicht gesetzt / zu kurz |
Umgebungsvariable fehlt oder hat weniger als 32 Zeichen | Aus dem Passwortspeicher setzen. Nicht neu erzeugen, wenn es ein altes gibt, sonst sind alle Anmeldungen weg |
ADMIN_TOKEN ist nicht gesetzt / zu kurz |
dasselbe, mindestens 16 Zeichen | dasselbe |
Datenverzeichnis konnte nicht angelegt werden |
Volume fehlt oder gehört nicht dem Benutzer im Container (UID 10001) | chown -R 10001:10001 auf das Volume, oder richtig einhängen |
unable to open database file, permission denied |
dasselbe für eine einzelne Datei, oft nach einem Zurückkopieren als root | Besitzer der Datei korrigieren |
address already in use |
Ein anderer Prozess belegt den Port | anderen Prozess beenden oder PORT ändern |
database disk image is malformed |
beschädigte Datenbank | Datenbank beschädigt |
Mit PostgreSQL kommen diese hinzu:
| Meldung | Ursache | Abhilfe |
|---|---|---|
DB_DRIVER=postgres, aber DATABASE_URL ist nicht gesetzt |
Umgebungsvariable fehlt | setzen, siehe README „Mit PostgreSQL“ |
PostgreSQL ist noch nicht erreichbar (Versuch …) und am Ende PostgreSQL ist nicht erreichbar |
Datenbank läuft nicht, falscher Host/Port, Firewall, falsches Passwort | Die Meldung nennt den Grund. Mit psql "$DATABASE_URL" -c 'SELECT 1' vom selben Rechner gegenprüfen |
zu wenige PostgreSQL-Verbindungen: … max_connections auf mindestens N erhöhen |
max_connections reicht nicht für alle Workspaces |
Beim Datenbank-Admin max_connections auf mindestens N setzen lassen und PostgreSQL neu starten. Munin danach neu starten |
Schema … konnte nicht angelegt werden: permission denied |
Der Benutzer aus DATABASE_URL darf keine Schemas anlegen |
GRANT CONNECT, CREATE ON DATABASE munin TO munin; (oder ihn zum Eigentümer machen), siehe Rechte des Datenbankbenutzers |
Schema … konnte nicht gelöscht werden: must be owner of schema |
Das Schema gehört einem anderen Benutzer, etwa nach einem Zurückspielen als anderer Benutzer | Eigentum übertragen: ALTER SCHEMA munin_ws_<id> OWNER TO munin; (für alles auf einmal REASSIGN OWNED BY <alter> TO munin;, als Superuser) |
Warnung: Die Datenbank sortiert nach "…" statt "C" |
Datenbank ohne LOCALE 'C' angelegt |
Läuft trotzdem, Listen sind nur anders sortiert. Beheben lässt es sich nur durch eine neue Datenbank mit LOCALE 'C' und Zurückspielen einer Sicherung |
Platte voll
Man sieht: Lesen geht noch, Schreiben schlägt fehl. Im Log steht
database or disk is full, Agenten bekommen Fehler bei remember.
SQLite bleibt dabei heil. Ein Schreibvorgang, der nicht mehr passt, wird vollständig zurückgerollt, halbe Einträge gibt es nicht.
- Nachsehen, was wächst:
du -sh /data/* /data/workspaces/* <BACKUP_DIR>/* <EXPORT_DIR> - Platz schaffen, in dieser Reihenfolge:
- alte Sicherungen löschen und
RETENTION_DAYSim Cronjob setzen, falls es fehlt - Docker-Logs begrenzen (
--log-opt max-size=50m)
- alte Sicherungen löschen und
- Nicht die
-wal- oder-shm-Dateien löschen. Darin stehen Änderungen, die noch nicht in der Hauptdatei sind.
Sobald wieder Platz ist, schreibt Munin ohne Neustart weiter.
Mit PostgreSQL betrifft das die Platte des Datenbankservers. PostgreSQL rollt einen Schreibvorgang, der nicht mehr passt, ebenso vollständig zurück. Platz schaffen ist dort Sache des Datenbank-Admins; auf Munins Seite wachsen dann nur noch Sicherungen und Export.
Rechte des Datenbankbenutzers
Munin arbeitet mit einem Datenbankbenutzer (DATABASE_URL) für alles. Er
braucht genau zwei Rechte auf der Datenbank, oder er ist ihr Eigentümer:
GRANT CONNECT, CREATE ON DATABASE munin TO munin;
| Vorgang | Was in PostgreSQL passiert | Nötiges Recht |
|---|---|---|
| Verbinden | Anmeldung an der Datenbank | CONNECT auf der Datenbank |
| Erster Start, Workspace anlegen | CREATE SCHEMA munin_control bzw. munin_ws_<id> |
CREATE auf der Datenbank |
| Workspace öffnen (Migrationen) | CREATE TABLE, CREATE INDEX im Schema |
Eigentum am Schema — hat er, weil er es angelegt hat |
| Normaler Betrieb | SELECT, INSERT, UPDATE, DELETE |
Eigentum an den Tabellen — ebenso |
| Workspace umbenennen | COMMENT ON SCHEMA … |
Eigentum am Schema |
| Workspace löschen | DROP SCHEMA munin_ws_<id> CASCADE |
nur Eigentum am Schema (oder Superuser). Ein eigenes Löschrecht gibt es in PostgreSQL nicht; auch alle Rechte an allen Tabellen reichen nicht |
| Verbindungsbudget prüfen | SHOW max_connections |
keins |
Nicht nötig: Superuser, CREATEDB, CREATEROLE, Rechte am Schema public.
Erprobt am 2026-10-16: Munin mit CONNECT, CREATE auf einer Datenbank, die
einem anderen Benutzer gehört — Start, Workspace anlegen, /ready ok, die
Schemas gehören danach Munins Benutzer. Ein Benutzer mit allen Tabellenrechten,
aber ohne Eigentum, kann das Schema nicht löschen (must be owner of schema).
Sicherung und Wiederherstellung: pg_dump braucht nur Lesezugriff;
pg_restore und das Umbenennen eines Schemas (ALTER SCHEMA … RENAME) laufen
am besten als derselbe Benutzer wie Munin. Dann gehört das zurückgespielte
Schema wieder ihm, und er kann es später auch löschen. Wurde als ein anderer
Benutzer zurückgespielt: ALTER SCHEMA munin_ws_<id> OWNER TO munin; und
ALTER TABLE … OWNER TO munin;, einfacher als Superuser
REASSIGN OWNED BY <anderer> TO munin;.
Bewusst kein zweiter Benutzer für die Verwaltung: siehe AUSBAUSTUFEN.md 4.10, „Entscheidung: ein Datenbankbenutzer“.
PostgreSQL ist weg
Man sieht: /live antwortet ok, /ready aber 503 nicht bereit. Seiten
und MCP-Aufrufe enden mit Fehlern, im Log steht Bereitschaft: Datenbank antwortet nicht … (einmal, beim Wechsel) und Meldungen wie connection refused
oder the database system is shutting down.
- Läuft die Datenbank?
pg_isready -d "$DATABASE_URL"oderpsql "$DATABASE_URL" -c 'SELECT 1'. - Ist sie wieder da, sollten Anfragen von selbst wieder gehen: Go verwirft
abgebrochene Verbindungen und baut neue auf. Im Log steht dann
Bereitschaft: Datenbank antwortet wieder, und/readymeldetok. Noch nicht erprobt. Gehen sie nach einer Minute nicht, Munin neu starten (docker restart munin). Dabei geht nichts verloren, der Suchindex baut sich beim ersten Zugriff neu auf.
Schreibvorgänge, die während des Ausfalls scheiterten, haben nichts geschrieben. Agenten bekommen einen Fehler und versuchen es erneut.
Ein Key ist in falsche Hände geraten
Zum Beispiel in ein öffentliches Repository eingecheckt, in einem Chat gepostet, oder der Laptop ist weg.
- Mit Admin-Rechten anmelden, ins Projekt wechseln, Zugänge → beim betroffenen Key Zurückziehen. Das wirkt sofort, für MCP-Aufrufe und für Sitzungen in der Weboberfläche. Jede Anfrage prüft den Key neu in der Datenbank.
- Unter Protokoll oben bei Wer den Namen des Keys wählen, bei Bedarf den Zeitraum (von, bis) eingrenzen: Was wurde damit getan, von welchen Adressen? Unbekannte Adressen deuten auf Missbrauch hin. Die gefilterte Ansicht hat eine eigene Adresse und lässt sich weitergeben.
- Hat der Key geschrieben, was er nicht sollte: Einträge über ihre Historie auf eine frühere Fassung zurücksetzen, Gelöschtes aus dem Papierkorb holen.
- Dem Team einen neuen Key ausstellen.
Wer den Key nicht kennt, findet ihn über den Workspace: Der Plattform-Admin sieht alle Workspaces, ein Workspace-Admin alle Projekte seines Workspace.
Ein Workspace-Admin-Token (wa_…) ist in falsche Hände geraten
Ein solches Token öffnet den ganzen Workspace.
- Als Plattform-Admin Workspaces → Bearbeiten → Abschnitt Workspace-Admins → Zurückziehen. Das wirkt sofort, auch für eine laufende Sitzung.
- Protokoll (alle) des Workspace prüfen: angelegte oder gelöschte Projekte, neue Keys. Keys, die in der fraglichen Zeit angelegt wurden, zurückziehen.
- Neues Token für die Person ausstellen.
ADMIN_TOKEN ist in falsche Hände geraten
Mit dem ADMIN_TOKEN kann sich jeder als Plattform-Admin anmelden und kommt
an jeden Workspace.
openssl rand -hex 24 # neues ADMIN_TOKEN
- Neuen Wert im Passwortspeicher ablegen und in der Konfiguration des Containers setzen.
- Container neu starten. Alle Sitzungen des Plattform-Admins enden damit
sofort, auch die eines Angreifers, der sich schon angemeldet hat. Jede
Admin-Sitzung trägt einen Fingerabdruck des Tokens, mit dem sie entstanden
ist, und der Server vergleicht ihn bei jeder Anfrage (seit 2026-10-06,
siehe
auth.PlatformSubject). Teams und Workspace-Admins bleiben angemeldet, MCP-Agenten merken nichts. - Prüfen, was in der fraglichen Zeit geschah.
SESSION_SECRET ist in falsche Hände geraten
Mit dem SESSION_SECRET kann jeder gültige Anmelde-Cookies selbst ausstellen,
für jeden Zugang. Da der Fingerabdruck des ADMIN_TOKEN ebenfalls aus dem
SESSION_SECRET berechnet wird, schließt das den Plattform-Admin ein, ohne
dass das Token je bekannt war. Das ist der schwerste Fall.
openssl rand -hex 32 # neues SESSION_SECRET
openssl rand -hex 24 # neues ADMIN_TOKEN, zur Sicherheit gleich mit
- Beide Werte im Passwortspeicher ersetzen und in der Konfiguration des Containers setzen.
- Container neu starten. Alle sind danach aus der Weboberfläche abgemeldet und melden sich mit ihrem Key neu an. MCP-Agenten merken nichts davon, sie nutzen ihre Keys direkt, nicht die Cookies.
- Prüfen, was in der fraglichen Zeit geschah.
Was ist in der fraglichen Zeit geschehen?
- Anmeldungen des Plattform-Admins stehen nur im Server-Log, nicht im
Protokoll:
docker logs munin 2>&1 | grep "Anmeldung Plattform-Admin". Unbekannte Adressen sind ein Warnsignal. Ein selbst ausgestelltes Cookie (geleaktesSESSION_SECRET) hinterlässt diese Zeile allerdings nicht. - In jedem Workspace das Protokoll (alle): neue Projekte, Keys und Workspace-Admins. Die Liste der Workspaces auf unbekannte Einträge prüfen. Was nicht von dir stammt, zurückziehen oder löschen.
Ein Agent dreht durch
Man sieht: Ein Projekt füllt sich mit Unsinn, oder Agenten melden, dass das Schreibbudget erschöpft ist.
Das Schreibbudget (Vorgabe 1.000 Aufrufe je Projekt und Stunde) begrenzt den Schaden bereits. Ein Agent, der in einer Schleife hängt, wird nach spätestens einer Stunde gebremst.
- Nutzung im Projekt zeigt, welcher Key wie viel geschrieben hat.
- Den Key des Agenten zurückziehen (siehe oben) oder das Team bitten, den Agenten zu stoppen.
- Aufräumen über Historie und Papierkorb.
- Braucht ein Projekt dauerhaft mehr: Der Plattform-Admin setzt unter Projekte → Bearbeiten eine eigene Obergrenze.
Einträge versehentlich gelöscht oder überschrieben
Fast immer ohne Sicherung lösbar:
- Überschrieben: Eintrag öffnen → Historie → frühere Fassung wiederherstellen. Jede Fassung bleibt erhalten.
- Gelöscht: Papierkorb im Projekt, 30 Tage lang.
- Endgültig gelöscht oder älter als 30 Tage im Papierkorb: nur noch aus einer Sicherung. Für einzelne Einträge reicht oft der Markdown-Export: Datei heraussuchen, als ZIP packen und über Import im Projekt wieder einlesen.
- Ganzes Projekt gelöscht: nur aus einer Sicherung, siehe unten.
Aus einer Sicherung wiederherstellen
Der Ablauf ist am 2026-10-06 einmal vollständig durchgespielt worden (Sicherung im laufenden Betrieb, Zurückkopieren, Start).
Vorher bedenken
Eine Sicherung setzt alles in der zurückgespielten Datei auf ihren Stand zurück, nicht nur das, was man retten will:
- Einträge und Fassungen nach der Sicherung sind weg.
- Keys, die nach der Sicherung angelegt wurden, gelten nicht mehr.
- Keys, die nach der Sicherung zurückgezogen wurden, gelten wieder. Nach dem Zurückspielen deshalb unter Zugänge nachsehen und erneut zurückziehen. Das ist der gefährlichste Nebeneffekt.
Wer nur ein paar Einträge braucht, spielt die Sicherung besser in ein getrenntes Verzeichnis zurück, startet dort einen zweiten Server auf einem anderen Port, exportiert die Einträge und importiert sie im echten Server. Dann bleibt alles andere unberührt.
Einen Workspace zurückholen
Die Kontroll-Datenbank bleibt dabei, wie sie ist. Die anderen Workspaces merken nichts davon.
# 1. Welche Datei? (ID ↔ Name)
sqlite3 /data/control.db "SELECT id, name FROM workspaces;"
# 2. Sicherung prüfen, bevor man sich auf sie verlässt
sqlite3 <BACKUP_DIR>/<zeitstempel>/workspaces/<id>.db "PRAGMA integrity_check;" # erwartet: ok
# 3. Server stoppen
docker stop munin
# 4. Die Hilfsdateien der alten Fassung entfernen. Nach einem Absturz liegen
# sie noch da, und SQLite würde sonst ihren Inhalt in die zurückgespielte
# Datei übernehmen.
rm -f /data/workspaces/<id>.db-wal /data/workspaces/<id>.db-shm
# 5. Die kaputte oder falsche Datei zur Seite legen, nicht löschen
mv /data/workspaces/<id>.db /data/workspaces/<id>.db.vorher
# 6. Zurückkopieren, Besitzer richtig setzen
cp <BACKUP_DIR>/<zeitstempel>/workspaces/<id>.db /data/workspaces/
chown 10001:10001 /data/workspaces/<id>.db
# 7. Starten und prüfen
docker start munin
curl -s https://<server>/ready
Danach in der Weboberfläche den Workspace öffnen und zurückgezogene Keys
erneut zurückziehen (siehe oben). Läuft alles, kann <id>.db.vorher weg.
Alles zurückholen
Wie oben, aber mit control.db und allen Dateien unter workspaces/, und
mit allen -wal/-shm-Dateien. Zusätzlich zu den Keys gelten dann auch
Workspace-Admins wieder, die nach der Sicherung zurückgezogen wurden.
Einen Workspace zurückholen (PostgreSQL)
Am 2026-10-16 durchgespielt (Sicherung, Zurückspielen über einen bestehenden Workspace, Start auf einer zurückgespielten Datenbank).
Das alte Schema wird nicht gelöscht, sondern umbenannt. Der neue Name beginnt
nicht mit munin_ws_, damit weder Munin noch scripts/backup.sh ihn für
einen Workspace halten.
# 1. Welches Schema? (Schema ↔ Name)
psql "$DATABASE_URL" -c "SELECT nspname, obj_description(oid, 'pg_namespace') FROM pg_namespace WHERE nspname LIKE 'munin_ws_%';"
# 2. Sicherung prüfen: Lässt sie sich lesen?
pg_restore --list <BACKUP_DIR>/<zeitstempel>/workspaces/munin_ws_<id>.dump > /dev/null && echo lesbar
# 3. Server stoppen
docker stop munin
# 4. Das jetzige Schema zur Seite legen, nicht löschen
psql "$DATABASE_URL" -c 'ALTER SCHEMA munin_ws_<id> RENAME TO munin_vorher_ws_<id>;'
# 5. Zurückspielen. Legt das Schema samt Namenskommentar wieder an.
pg_restore --dbname="$DATABASE_URL" --no-owner <BACKUP_DIR>/<zeitstempel>/workspaces/munin_ws_<id>.dump
# 6. Starten und prüfen
docker start munin
curl -s https://<server>/ready
Danach wie oben zurückgezogene Keys erneut zurückziehen. Läuft alles, kann
das alte Schema weg: DROP SCHEMA munin_vorher_ws_<id> CASCADE;
Fehlen pg_restore oder psql auf dem Rechner oder sind sie älter als der
Server, gehen dieselben Befehle über einen PostgreSQL-Container, z.B.
docker exec -i <container> pg_restore … < datei.dump.
Alles zurückholen (PostgreSQL)
Am sichersten in eine neue, leere Datenbank und dann DATABASE_URL
darauf umstellen. Die alte bleibt so unberührt, bis klar ist, dass alles
stimmt:
CREATE DATABASE munin_neu OWNER munin TEMPLATE template0 LOCALE 'C' ENCODING 'UTF8';
for f in <BACKUP_DIR>/<zeitstempel>/control.dump <BACKUP_DIR>/<zeitstempel>/workspaces/*.dump; do
pg_restore --dbname="postgres://munin@<host>:5432/munin_neu" --no-owner "$f"
done
Dann DATABASE_URL auf munin_neu ändern und Munin neu starten. Auch hier
gelten danach zurückgezogene Keys und Workspace-Admins wieder.
Ein gelöschter Workspace
Beim Löschen verschwinden die Datei und sein Eintrag in der
Kontroll-Datenbank. Nur die Datei zurückzukopieren reicht deshalb nicht, der
Server kennt den Workspace nicht mehr. Es braucht auch die control.db aus
derselben Sicherung, und die setzt alles Zentrale zurück: andere Workspaces,
Workspace-Admins, die Zuordnung neuer Keys. Das ist eine bewusste Entscheidung.
Meist ist der Weg über einen getrennten zweiten Server mit Export und Import
der bessere.
Mit PostgreSQL gilt dasselbe: Schema und munin_control müssten zurück.
Der bessere Weg: die ganze Sicherung in eine eigene Datenbank zurückspielen
(siehe oben), einen zweiten Munin mit DATABASE_URL darauf und anderem PORT
starten, dort den Workspace als ZIP herunterladen und im echten Server in
einen neuen Workspace importieren.
Datenbank beschädigt
Man sieht: database disk image is malformed im Log, oder ein Workspace
lässt sich nicht öffnen, während andere gehen. Workspace-Dateien werden erst
beim ersten Zugriff geöffnet, deshalb kann der Fehler auch erst Stunden nach
dem Start auftauchen.
docker stop munin
sqlite3 /data/workspaces/<id>.db "PRAGMA integrity_check;"
Alles außer ok heißt: aus der Sicherung zurückholen.
Nicht versuchen, die Datei zu reparieren. Mit der letzten guten Sicherung ist
man schneller und sicherer am Ziel.
Häufigste Ursache ist eine Datei, die im laufenden Betrieb mit cp statt mit
scripts/backup.sh kopiert und dann zurückgespielt wurde.
Mit PostgreSQL ist eine beschädigte Datenbank Sache des Datenbank-Admins. Betrifft es nur einen Workspace, lässt er sich wie oben einzeln zurückholen.
Eine neue Fassung einspielen
Das Schema der Datenbanken wird beim Start automatisch angepasst
(internal/store/migrations*). Der Weg zurück auf eine ältere Fassung ist
danach nicht garantiert. Deshalb:
scripts/backup.shvon Hand laufen lassen.- Neues Abbild bauen, Container ersetzen.
- Log auf die Startzeilen prüfen,
/ready, einmal in der Weboberfläche suchen und ein MCP-recallabsetzen. - Geht etwas schief: altes Abbild starten und die Sicherung aus Schritt 1 zurückspielen.
Ein Neustart beendet laufende Anfragen sauber (bis zu 10 Sekunden). Agenten verbinden sich danach von selbst neu.
Regelmäßig
| Wann | Was |
|---|---|
| wöchentlich | Läuft die Sicherung? Ein Blick ins Log des Cronjobs und auf den neuesten Ordner in BACKUP_DIR. |
| wöchentlich | Ein Blick auf System → Zustand. Steht dort „Alles in Ordnung“, ist nichts zu tun. |
| monatlich | Marke ungenutzt bei Keys und Workspace-Admins (Übersicht unter Workspaces, Summe unter System → Zustand). Vergessene Zugänge zurückziehen. Keys mit läuft bald ab: noch gebraucht? Dann rechtzeitig ersetzen. Unter System → Aktivität: Projekte ganz ohne Aktivität mit dem Team klären. |
| vierteljährlich | Probe der Sicherung: scripts/sicherung-pruefen.sh <BACKUP_DIR>/<zeitstempel> (siehe unten). Endet es mit PROBE BESTANDEN, ist die Sicherung zurückspielbar. Eine Sicherung, die nie zurückgespielt wurde, ist nur eine Hoffnung. |
| vor jedem Update | Sicherung von Hand, siehe oben |
Probe der Sicherung
Ein Befehl, für beide Datenbanken. Das Skript spielt die Sicherung zurück
(SQLite: in ein Arbeitsverzeichnis samt PRAGMA integrity_check; PostgreSQL:
in eine leere Probe-Datenbank), startet Munin darauf mit eigenem Port und
eigenen Geheimnissen, wartet auf /ready, vergleicht die Zahl der Workspaces
mit der Sicherung und räumt auf. Der echte Server wird nicht berührt.
# SQLite (braucht sqlite3 und Go, oder MUNIN_BIN=/pfad/zu/munin)
scripts/sicherung-pruefen.sh <BACKUP_DIR>/<zeitstempel>
# PostgreSQL: einmalig eine leere Probe-Datenbank anlegen …
psql "$DATABASE_URL" -c "CREATE DATABASE munin_probe OWNER munin TEMPLATE template0 LOCALE 'C' ENCODING 'UTF8';"
# … dann bei jeder Probe
DB_DRIVER=postgres PROBE_DATABASE_URL="postgres://munin@<host>:5432/munin_probe" \
scripts/sicherung-pruefen.sh <BACKUP_DIR>/<zeitstempel>
Ist die Probe-Datenbank nicht leer, bricht das Skript ab, statt etwas zu überschreiben. Das schützt davor, versehentlich die echte Datenbank anzugeben. Am 2026-10-16 ausprobiert: SQLite und PostgreSQL bestanden, eine absichtlich beschädigte SQLite-Sicherung durchgefallen, die echte Datenbank als Probe abgelehnt.
Von Hand, falls das Skript nicht zur Hand ist (SQLite, auf einem beliebigen Rechner mit Go und sqlite3):
probe=$(mktemp -d)
cp -r <BACKUP_DIR>/<zeitstempel>/. "$probe/"
for f in "$probe"/control.db "$probe"/workspaces/*.db; do
echo "$f: $(sqlite3 "$f" 'PRAGMA integrity_check;')"
done
DATA_DIR="$probe" PORT=18080 SESSION_SECRET=$(openssl rand -hex 32) \
ADMIN_TOKEN=probe-probe-probe-1 go run .
# im Browser: http://localhost:18080, Anmeldung mit probe-probe-probe-1
Mit PostgreSQL, in eine eigene Probe-Datenbank auf demselben Server:
psql "$DATABASE_URL" -c "CREATE DATABASE munin_probe OWNER munin TEMPLATE template0 LOCALE 'C' ENCODING 'UTF8';"
probe_url="postgres://munin@<host>:5432/munin_probe"
for f in <BACKUP_DIR>/<zeitstempel>/control.dump <BACKUP_DIR>/<zeitstempel>/workspaces/*.dump; do
pg_restore --dbname="$probe_url" --no-owner "$f"
done
DB_DRIVER=postgres DATABASE_URL="$probe_url" PORT=18080 SESSION_SECRET=$(openssl rand -hex 32) \
ADMIN_TOKEN=probe-probe-probe-1 go run .
# danach aufräumen:
psql "$DATABASE_URL" -c "DROP DATABASE munin_probe;"
Die Probe darf mit eigenen Geheimnissen laufen. Sie braucht weder die echten noch erreicht sie den echten Server.