Open Archiver: Ein umfassender Leitfaden für Installation und Betrieb

Umfassender Leitfaden zur selbstgehosteten, GoBD-konformen E-Mail-Archivierung mit Docker. Installation, Anbindung von IMAP, Microsoft 365 und Google Workspace sowie revisionssicherer Betrieb und eDiscovery


László Kovács László Kovács DevOps Veröffentlicht: 07.04.2026 Aktualisiert: 07.04.2026 Lesezeit: 79 min
Scope #openarchiver #docker #devops

Open Archiver ist eine vollständig open-source, selbstgehostete Plattform für revisionssichere E-Mail-Archivierung und eDiscovery. Es archiviert, indiziert und durchsucht E-Mails sowie Anhänge aus beliebigen Quellen – Google Workspace, Microsoft 365, klassische IMAP-Server oder PST-Dateien – und speichert sie dauerhaft, manipulationssicher und suchbar.

Im Gegensatz zu cloudbasierten Archivierungsdiensten behältst du die volle Kontrolle über deine Daten. Kein Vendor-Lock-in, keine monatlichen Gebühren, keine Abhängigkeit von externen Anbietern. Stattdessen läuft alles in deiner Infrastruktur, hinter deiner Firewall, unter deinen Backup- und Verschlüsselungsregeln. Genau das, was du als erfahrener Admin oder DevOps-Engineer für produktive, GoBD-konforme Umgebungen brauchst.

Die Lösung basiert auf Docker und Docker Compose. Es kombiniert einen robusten Mail-Ingestion-Layer, eine leistungsstarke Volltext-Suchmaschine (inklusive Attachment-Indexierung), eine tamper-proof Speicherschicht und eine moderne Web-Oberfläche für Suche und Verwaltung. Du kannst automatische Retention-Policies, Legal Holds und Audit-Trails einrichten – alles, was du für die Einhaltung gesetzlicher Anforderungen und interner Compliance-Regeln benötigst.


┌──────────────────────┐
│ E-Mail-Quellen       │
│ Google Workspace     │
│ Microsoft 365        │
│ IMAP / PST           │
└─────────┬────────────┘
          │
┌─────────▼────────────┐
│ Open Archiver        │
│ - Ingestion          │
│ - Indizierung        │
│ - Storage (tamper-   │
│   proof)             │
│ - Full-Text-Suche    │
└─────────┬────────────┘
          │
┌─────────▼────────────┐
│  Deine Infrastruktur │
│  Docker + Volumes    │
│  Reverse-Proxy + TLS │
└──────────────────────┘

Damit schaffst du dir ein eigenes, skalierbares Archiv, das genau so performant und sicher ist wie die teuren Enterprise-Lösungen – nur ohne die Abhängigkeiten.

⚠️ Wichtig: Dieser Artikel ist kein Anfänger-Tutorial. Er richtet sich an dich, wenn du bereits Erfahrung mit Linux und Docker hast und praxisnah in die Installation von Open Archiver einsteigen willst. Es geht nicht darum, dich durch einen strengen Kurs zu schleusen, sondern dir hilfreiches Wissen für deine tägliche Arbeit mitzugeben. Wenn du neu in Docker bist, schau dir zuerst grundlegende Docker-Dokumentationen an, bevor du hier weitermachst.

Architektur und technische Grundlagen

Komponenten und Docker-Services im Überblick

Open Archiver setzt auf eine modulare Docker-Compose-Architektur. Jeder Service läuft isoliert, kommuniziert über ein internes Bridge-Netzwerk und greift nur auf explizit definierte Volumes zu. Diese Trennung sorgt für reproduzierbare Deployments, einfache Skalierung und gezielte Wartung – unverzichtbar, wenn du revisionssichere Archivierung produktiv betreibst.

Die vier Kern-Services bilden das Fundament

Der open-archiver-Container (Image logiclabshq/open-archiver:latest) vereint Ingestion, REST-API und die React-Web-Oberfläche. Er ist der zentrale Einstiegspunkt für alle Mail-Quellen und Benutzeraktionen. Hier laufen die Scheduler für automatische Abfragen und die Weiterleitung an die Indexierung.

postgres (offizielles postgres:16-Image) speichert Metadaten, Benutzer, Retention-Policies, Legal-Hold-Einträge und den vollständigen Audit-Trail. Jede Archivierungsoperation wird hier protokolliert – genau das, was du für GoBD-Nachweispflicht brauchst.

valkey (Valkey-Image als Redis-Fork) dient als Queue- und Cache-Layer. Er puffert asynchrone Jobs wie Massenimporte oder Re-Indizierungen und verhindert, dass der Haupt-Service bei Lastspitzen blockiert.

meilisearch (getmeili/meilisearch:latest) ist die dedizierte Volltext-Engine. Sie indiziert nicht nur Betreff, Body und Header, sondern auch gängige Anhänge und liefert subsekundenschnelle facettenreiche Suchen.

Zusätzlich legst du persistente Volumes an: archiver-data für die rohen E-Mails, postgres-data und meilisearch-data. Diese Volumes bleiben beim Container-Neustart erhalten und können später separat gesichert werden.


services:
  open-archiver:
    image: logiclabshq/open-archiver:latest
    depends_on:
      postgres:
        condition: service_healthy
      valkey:
        condition: service_healthy
      meilisearch:
        condition: service_healthy
    volumes:
      - archiver-data:/data
  postgres:
    image: postgres:16
    volumes:
      - postgres-data:/var/lib/postgresql/data
  valkey:
    image: valkey/valkey:latest
  meilisearch:
    image: getmeili/meilisearch:latest

docker compose ps

Das Kommando zeigt dir sofort Status, Ports und Abhängigkeiten aller Container.


               ┌─────────────────────┐
               │   open-archiver     │
               │ (API + Ingestion)   │
               └──────────┬──────────┘
                          │
        ┌─────────────────┼─────────────────┐
        │                 │                 │
        ▼                 ▼                 ▼
┌──────────────┐   ┌──────────────┐   ┌────────────────┐
│   postgres   │   │    valkey    │   │  meilisearch   │
│ (Metadaten + │   │ (Queue +     │   │ (Volltext-     │
│  Audit)      │   │  Cache)      │   │  Index)        │
└──────────────┘   └──────────────┘   └────────────────┘

Diese klare Aufteilung ist relevant, weil du später einzelne Services unabhängig updaten oder skalieren kannst, ohne das gesamte Archiv zu stoppen. Achte darauf, dass Healthchecks in der Compose-Datei vorhanden sind – sonst startet open-archiver zu früh und schlägt fehl. Typischer Fehler: zu wenig RAM für Meilisearch (mindestens 2 GB empfohlen), dann bricht die Indizierung ab.

Du brauchst diese Kenntnisse später für Monitoring, gezieltes Backup und Troubleshooting.

Datenfluss, Indizierung und Speicherung

Sobald du eine E-Mail-Quelle anbindest, beginnt der kontrollierte Datenfluss. Open Archiver holt Nachrichten nicht einfach ab – er verarbeitet sie asynchron, speichert sie manipulationssicher und bereitet sie für blitzschnelle Suche vor. Genau diese Pipeline macht den Unterschied zwischen einer einfachen Ablage und einem revisionssicheren Archiv aus.

Der Prozess startet im open-archiver-Container. Du konfigurierst IMAP, Google Workspace, Microsoft 365 oder PST-Importe über die API oder die Web-Oberfläche. Der Scheduler prüft in festgelegten Intervallen auf neue oder geänderte Mails. Eingehende Nachrichten landen zunächst als Job in Valkey. BullMQ übernimmt die asynchrone Verarbeitung, damit der Hauptprozess nicht blockiert – entscheidend bei Tausenden von Mails pro Stunde.

Im Job-Worker passiert die eigentliche Arbeit: Die E-Mail wird als .eml-Datei geparst. Metadaten (Betreff, From, To, Datum, Header, Thread-ID) werden extrahiert. Anhänge werden erkannt, entpackt und – wo möglich – in Text umgewandelt (PDF, DOCX, Bilder mit OCR-Unterstützung in neueren Versionen). Jede Datei erhält einen SHA-256-Hash.

Dieser Hash wird sofort in PostgreSQL gespeichert. Damit ist jede spätere Veränderung der gespeicherten Datei nachweisbar – die Grundlage für GoBD-Konformität.

Die rohe E-Mail und alle Anhänge wandern dann in das persistente Volume archiver-data. Standardmäßig nutzt Open Archiver das lokale Dateisystem mit Docker-Volumes. Optional kannst du S3-kompatiblen Speicher (MinIO, AWS S3) konfigurieren. Dateien werden dedupliziert (identische Mails oder Anhänge werden nur einmal gespeichert) und komprimiert. Zusätzlich erfolgt eine Verschlüsselung at-rest über den vom Backend verwalteten Schlüssel. Das Volume bleibt damit außerhalb der Container erreichbar und kann separat gesichert werden.

Parallel zum Speichern schickt der Worker den vollständigen Inhalt – Body, Header und extrahierten Attachment-Text – an Meilisearch. Die Suchmaschine baut einen invertierte Index auf. Du suchst später nicht nur nach Worten im Betreff oder Body, sondern auch innerhalb von PDFs oder Office-Dateien. Meilisearch liefert facettenreiche Ergebnisse, Thread-Ansichten und Filter in unter einer Sekunde. Der Index selbst liegt im Volume meilisearch-data und wird täglich automatisch gesnapshottet.

PostgreSQL hält alle Metadaten, die Hashes, Retention-Policies und den unveränderlichen Audit-Trail. Jeder Schritt – Ingestion, Speicherung, Indexierung, Zugriff – wird hier protokolliert. Das sorgt für lückenlose Nachverfolgung.


E-Mail-Quelle (IMAP/GWS/M365/PST)
          │
          ▼
┌─────────────────────┐
│ open-archiver       │
│ Scheduler + API     │
└──────────┬──────────┘
           │
           ▼
     Valkey (BullMQ Queue)
           │
           ▼
┌──────────────────────┐    ┌──────────────────────┐
│ Worker               │──▶ │ Speicherung          │
│ - Parse .eml         │    │ - .eml + Anhänge     │
│ - SHA-256 Hash       │    │ - Deduplizierung     │
│ - OCR (Attachments)  │    │ - Encryption at rest │
└──────────┬───────────┘    └──────────┬───────────┘
           │                           │
           ├───────────────────────────┘
           │
           ▼
┌────────────────────┐   ┌─────────────────────┐
│ Meilisearch        │   │ PostgreSQL          │
│ - Volltext +       │   │ - Metadaten         │
│   Attachment-Index │   │ - Hashes            │
│ - Facetten-Suche   │   │ - Audit-Trail       │
└────────────────────┘   └─────────────────────┘

docker compose logs -f open-archiver --tail=50

Mit diesem Befehl beobachtest du live, wie Jobs durchlaufen und ob Hashes oder Indexierung fehlschlagen.

Warum ist das relevant? Weil du später exakt wissen musst, wo welche Datei liegt, wie du Backups ohne Integritätsverlust machst und warum ein fehlender Hash sofort Alarm schlägt. Achte auf ausreichend IOPS beim Volume archiver-data – bei großen Postfächern wird hier massiv geschrieben. Zu wenig RAM bei Meilisearch führt zu Index-Fehlern bei Attachment-reichen Mails.

Typischer Fehler: Das Volume archiver-data auf einem Overlay-Filesystem ohne dedizierten Block-Storage zu legen. Dann explodiert die Performance bei Tausenden kleiner .eml-Dateien. Lösung: separater ZFS-Datensatz oder LVM-Volume mit hoher I/O-Priorität.

Dieses Wissen brauchst du später für gezieltes Troubleshooting, Skalierung auf mehrere Nodes und den Nachweis gegenüber Auditoren, dass keine Mail jemals verändert wurde.

Architektur und technische Grundlagen

Compliance-Features

revisionssichere Archivierung, GoBD, Audit-Trail

Open Archiver ist von Grund auf für revisionssichere Archivierung konzipiert. Jede E-Mail und jeder Anhang wird so gespeichert, dass nachträgliche Veränderungen technisch unmöglich oder sofort nachweisbar sind – genau das, was du in regulierten Umgebungen brauchst. Die Plattform erfüllt die Kernanforderungen der GoBD und liefert einen lückenlosen, manipulationssicheren Audit-Trail.

Die revisionssichere Speicherung basiert auf drei Säulen: kryptografische Integrität, WORM-Prinzip (Write Once, Read Many) und strikte Zugriffssteuerung. Jede eingehende .eml-Datei und jeder extrahierte Anhang erhält direkt nach der Verarbeitung einen SHA-256-Hash. Dieser Hash wird zusammen mit dem exakten Zeitstempel und der Quell-ID in PostgreSQL abgelegt. Die Datei selbst wird im Volume archiver-data gespeichert – standardmäßig mit App-Level-WORM: das Backend erlaubt keine Überschreibungen.

Löschungen sind nur über definierte Retention-Policies oder explizite Legal-Hold-Aufhebungen möglich. Sobald eine Mail archiviert ist, existiert sie als unveränderliche Kopie. Optional kannst du das Volume auf ZFS mit copies=3 und immutable Snapshots legen oder auf S3 mit Object-Lock (Compliance-Modus) auslagern. Jeder Zugriffsversuch wird protokolliert, selbst wenn er nur lesend erfolgt.

GoBD-Konformität wird aktiv unterstützt.

Die Grundsätze verlangen Vollständigkeit, Unveränderbarkeit, Nachvollziehbarkeit, Verfügbarkeit und Prüfbarkeit. Open Archiver gewährleistet dies durch:

  • automatische Vollarchivierung aller eingehenden und ausgehenden Mails inklusive Header und Anhänge,
  • unveränderliche Zeitstempel mit NTP-Synchronisation,
  • deduplizierte, aber referenzierte Speicherung (keine doppelte Ablage identischer Inhalte),
  • und die Möglichkeit, gesamte Postfächer oder Threads mit einem Legal Hold zu sperren. Ein Legal Hold überschreibt jede Retention-Policy und verhindert Löschung bis zur manuellen Aufhebung – ideal für laufende Rechtsstreitigkeiten oder Betriebsprüfungen.

Der Audit-Trail sitzt in einer eigenen, nur lesbaren PostgreSQL-Tabelle audit_log. Jeder Eintrag enthält: Timestamp (UTC), Actor (User-ID oder System), Action-Typ (ingest, index, view, delete-attempt, policy-change), Object-Hash, Source-IP, Before/After-State und eine JSON-Payload mit allen Metadaten. Die Tabelle ist append-only; das Backend verweigert UPDATE- oder DELETE-Operationen auf bestehenden Zeilen.

Du kannst den Trail über die Web-Oberfläche filtern oder direkt per SQL abfragen – ein Muss für Auditoren.


E-Mail eingegangen
          │
          ▼
┌─────────────────────┐   SHA-256 Hash + Timestamp
│ Ingestion + Parse   │──────────────────────────────┐
└──────────┬──────────┘                              │
           │                                         │
           ▼                                         │
   archiver-data Volume (WORM)                       │
           │                                         │
           ▼                                         │
┌─────────────────────┐   PostgreSQL                 │
│ Audit-Trail Eintrag │◀─────────────────────────────┘
│ (unveränderlich)    │
└─────────────────────┘
           │
           ▼
Legal Hold / Retention Policy → Löschung nur bei expliziter Freigabe

docker compose exec postgres psql -U archiver -d archiver -c "
SELECT timestamp, actor, action, object_hash, details 
FROM audit_log 
WHERE action = 'ingest' 
ORDER BY timestamp DESC LIMIT 20;
"

Dieser Befehl gibt dir die letzten 20 Ingestion-Ereignisse mit vollständigem Kontext aus. Du siehst sofort, ob eine Mail korrekt archiviert wurde oder ob ein Hash-Fehler vorliegt.

⚠️ Warnung: Deaktiviere niemals die Hash-Prüfung im .env ( ENABLE_HASH_VERIFICATION=true ist Default). Ohne sie verlierst du GoBD-Konformität.

💡 Tipp: Lege das postgres-data-Volume auf einem separaten, verschlüsselten LVM-Volume mit regelmäßigen Point-in-Time-Snapshots. So kannst du den Audit-Trail auch bei Kompromittierung des Hauptsystems wiederherstellen.

Typischer Fehler: Retention-Policies zu locker zu konfigurieren. Eine Mail, die nach 7 Jahren gelöscht wird, obwohl GoBD 10 Jahre fordert, führt bei Prüfung direkt zum Befund. Lösung: Globale Default-Policy auf „unlimited“ oder „10y“ setzen und pro Postfach überschreiben. Prüfe immer mit docker compose exec open-archiver ./bin/archiver policy list.

Diese Features sind später unverzichtbar, wenn du dem Betriebsrat, dem Datenschutzbeauftragten oder dem Finanzamt nachweisen musst, dass keine Mail jemals verändert oder gelöscht wurde. Du kannst den kompletten Trail exportieren und signieren – fertig für die nächste Revision.

Infrastruktur-Vorbereitung

Systemanforderungen

Hardware- und Software-Empfehlungen

Nachdem die Compliance-Features ihre technische Basis haben, kommt es nun darauf an, die Infrastruktur so vorzubereiten, dass Open Archiver stabil und performant läuft. Die Anforderungen sind nicht trivial, weil der kontinuierliche Datenfluss aus Ingestion, Hash-Berechnung, Speicherung und Indizierung erhebliche Ressourcen bindet. Für eine produktive Umgebung musst du die Hardware und Software genau auf deine erwartete Mail-Last abstimmen.

Die CPU-Anforderungen starten bei mindestens vier physikalischen Kernen für den Basisbetrieb. In der Praxis solltest du jedoch auf acht oder mehr Kerne setzen, um die asynchronen Worker und Meilisearch ohne Engpässe zu betreiben. Jeder zusätzliche Kern beschleunigt die Verarbeitung von Massenimporten erheblich. Moderne Prozessoren mit hoher IPC wie die aktuellen AMD Ryzen oder Intel Core der 14. Generation sind hier gut geeignet.

⚠️ Achte darauf, dass Hyper-Threading aktiviert ist, aber die Docker-Container nicht übermäßig viele vCPUs zugewiesen bekommen, sonst leidet die I/O-Performance.

Für den Arbeitsspeicher gilt ein Minimum von 16 GB als ausreichend für kleine Installationen mit unter 100 Benutzern. Sobald du größere Postfächer oder regelmäßige Re-Indizierungen hast, wird 32 GB zur Untergrenze. Meilisearch hält große Teile des Indexes im RAM und PostgreSQL nutzt Caches für den Audit-Trail. Mit 64 GB oder mehr hast du Puffer für Spitzenlasten und zukünftiges Wachstum. Die RAM-Nutzung skaliert linear mit der Anzahl der indizierten Dokumente, daher ist eine Überdimensionierung hier die sicherste Wahl.

💡 Tipp: Der Speicherbedarf lässt sich gut skalieren, indem du RAM-Module später nachrüstest, solange das Mainboard das unterstützt.

Beim Massenspeicher ist nicht nur die Kapazität entscheidend. Mindestens 500 GB NVMe SSD für den Docker-Bereich sind notwendig, um die Volumes für archiver-data, postgres-data und meilisearch-data unterzubringen. Die reale Größe hängt von deiner Mail-Historie ab. Rechne mit 150 bis 300 KB pro archivierte Nachricht inklusive Anhängen nach Deduplizierung. Für ein mittelständisches Unternehmen mit 500 Benutzern und 10 Jahren Daten kannst du schnell bei 5 bis 10 TB landen. Wähle SSDs mit hoher Endurance und mindestens 20.000 IOPS für random writes, da das Dateisystem ständig kleine .eml-Dateien anlegt und updated. Ein ZFS-Pool mit RAID-Z2 bietet zusätzliche Redundanz und Snapshot-Fähigkeiten, die für Backups nützlich sind.

Das Betriebssystem sollte Ubuntu 24.04 LTS sein, weil es den aktuellen Kernel und lange Support-Zyklen bietet. Der Kernel ab Version 6.8 bringt Verbesserungen in der Container-Isolation und cgroups v2, die für Docker essenziell sind. Installiere Docker Engine Version 27.0 oder neuer und Docker Compose in der V2-Variante. Ältere Versionen können zu Problemen mit den Service-Healthchecks führen.

Zusätzlich empfehle ich, die Kernel-Parameter anzupassen, wie vm.swappiness diesen auf 10 zu setzen, um unnötiges Swapping zu vermeiden. Auch die ulimit-Werte für open files sollten auf 65535 erhöht werden, um die vielen Dateien im Volume zu handhaben.

Diese Anpassungen verhindern, dass das System bei hoher Last in die Knie geht.


Recommended Server Stack
├── CPU: 8+ cores (high clock)
├── RAM: 32-64 GB ECC
├── Storage: NVMe SSD (archiver-data on dedicated pool)
│   └── ZFS or LVM for snapshots
├── OS: Ubuntu 24.04 LTS
├── Docker Engine 27+
└── Network: 10 Gbit/s recommended

🔧 Praktisches Beispiel:

Um deine aktuelle Infrastruktur auf Kompatibilität zu prüfen, kannst du folgende Befehle ausführen. Zuerst die CPU und RAM:


lscpu | grep -E 'Model name|CPU\(s\)|Thread'
free -h

Dann den Speicher:


df -h
lsblk -f

Und für Docker:


docker --version
docker compose version

Diese Ausgaben geben dir einen schnellen Status und zeigen, ob Anpassungen nötig sind. Du kannst die Ergebnisse mit den empfohlenen Werten vergleichen und bei Bedarf Hardware aufrüsten, bevor du mit der eigentlichen Installation beginnst.

Ein häufiger Fehler ist es, die Docker-Volumes auf einem langsamen HDD-Array zu platzieren. Die Indizierung wird dann zum Flaschenhals und der gesamte Service leidet unter hoher Latenz.

Für kleinere Umgebungen mit bis zu 50 Benutzern reicht ein einzelner Server mit den Mindestanforderungen aus. Bei mittleren Setups mit 200 Benutzern solltest du auf 12 Kerne, 64 GB RAM und 8 TB Storage gehen, um Puffer für Wachstum zu haben. In großen Umgebungen mit über 1000 Benutzern wird eine Cluster-Architektur mit separaten Nodes für Storage und Compute sinnvoll, obwohl Open Archiver derzeit noch keine native Multi-Node-Skalierung bietet. Du kannst jedoch mehrere Instanzen hinter einem Load-Balancer betreiben, indem du die Volumes über NFS oder Ceph teilst.

Die Software-Abhängigkeiten umfassen git für das Klonen des Repositories, make oder ähnliche Tools für Build-Prozesse und einen NTP-Daemon für genaue Zeitstempel im Audit-Trail. Ohne NTP können Zeitstempel abweichen und GoBD-Konformität in Frage gestellt werden.

Netzwerktechnisch benötigst du mindestens 1 GBit/s, besser 10 GBit/s für den initialen PST-Import oder IMAP-Sync großer Mailboxen. Die interne Kommunikation zwischen den Docker-Services erfolgt über Bridge-Netzwerke, daher ist keine zusätzliche Konfiguration nötig, solange die Host-Firewall die Ports nicht blockt.

In der Praxis hat sich gezeigt, dass eine dedizierte Maschine oder VM für Open Archiver die beste Stabilität bietet. Vermeide es also, den Server mit anderen ressourcenintensiven Diensten wie Datenbanken oder Webservern zu teilen, es sei denn, du hast ausreichend Überkapazität.

Die Berechnung des Speicherbedarfs

Zur genauen Berechnung des Speicherbedarfs nimm die Anzahl deiner Benutzer, multipliziere diese mit der durchschnittlichen Anzahl an Mails pro Tag (sagen wir 50), dann mit der Anzahl der Tage in 10 Jahren (3650) und multipliziere mit 0.25 MB pro Mail. Das ergibt für 500 Benutzer etwa 4.56 TB. Füge 20 Prozent für Overhead und Wachstum hinzu. So kommst du auf die benötigte Kapazität und kannst die SSD-Größe präzise planen.

Zusätzlich solltest du die Docker-Daemon-Konfiguration anpassen, um Resource Limits zu setzen. In der Datei /etc/docker/daemon.json kannst du default ulimits und log-driver festlegen, um Speicherlecks zu vermeiden. Für den Storage-Driver empfehle ich overlay2 auf einem dedizierten Dateisystem.

Die Hardware sollte außerdem über ECC-RAM verfügen, um Bit-Fehler in großen Indexen zu vermeiden. Für den Betrieb in Rechenzentren achte auf redundante Netzteile und UPS-Anschluss, da Ausfälle den Audit-Trail unterbrechen könnten.

Um die Anforderungen zu validieren, führe einen Lasttest mit synthetischen E-Mails durch, bevor du live gehst. Dies hilft, Engpässe früh zu erkennen und die Konfiguration anzupassen. Die Software-Seite erfordert außerdem aktuelle Pakete für git, curl und gnupg, damit das Repository ohne Probleme geklont werden kann.

Sobald die Systemanforderungen erfüllt sind, kannst du die Netzwerkkonfiguration angehen.

Reverse-Proxy

TLS-Terminierung und Netzwerkkonfiguration

Die Netzwerkkonfiguration beginnt mit der Einrichtung eines Reverse-Proxys, der die TLS-Terminierung übernimmt und den Zugriff auf den open-archiver-Service steuert. In einer produktiven Umgebung darf der Container niemals direkt aus dem Internet erreichbar sein, weil die Web-Oberfläche sensible Such- und Administrationsfunktionen enthält und der Audit-Trail jederzeit manipulationssicher bleiben muss. Ein dedizierter Reverse-Proxy wie Nginx isoliert den Docker-Stack, terminiert TLS zentral und erlaubt feingranulare Zugriffsregeln über Header oder Pfade. Die meisten Admins setzen hier Nginx ein, weil es stabil, ressourcenschonend und mit Docker Compose hervorragend integrierbar ist.

Alternativ funktioniert Traefik oder Caddy, doch Nginx bietet die beste Kontrolle über Rate-Limiting und Logging für compliance-relevante Umgebungen.

Der Proxy lauscht auf den externen Ports 80 und 443 und leitet Traffic nur an den internen Port des open-archiver-Services weiter, der üblicherweise auf Port 3000 gebunden ist. Alle anderen Services bleiben im internen Docker-Netzwerk verborgen. Für die TLS-Terminierung importierst du ein gültiges Zertifikat, das du entweder von deiner internen CA beziehst oder automatisch über Let’s Encrypt erzeugst. Der Proxy muss den Servernamen auflösen können, unter dem Open Archiver später erreichbar sein soll, zum Beispiel archive.deinefirma.internal.

Die Konfiguration trennt strikt HTTP- und HTTPS-Listener, leitet HTTP automatisch auf HTTPS um und aktiviert HSTS, damit Browser zukünftige Verbindungen nur noch verschlüsselt aufbauen.


Internet
   │
   ▼
┌────────────────────────┐
│ Reverse-Proxy (Nginx)  │
│ TLS-Terminierung       │
│ Rate-Limiting          │
└──────────┬─────────────┘
           │
           ▼
   Docker Bridge Network
           │
   ┌───────▼────────┐
   │ open-archiver  │ ← Port 3000
   └────────────────┘
   (postgres, valkey, meilisearch bleiben unsichtbar)

🔧 Praktisches Beispiel:

Erstelle zuerst eine separate nginx.conf außerhalb des Compose-Stacks, damit du sie versionieren und unabhängig vom Archiver-Stack pflegen kannst.

Der Block sieht so aus:


server {
    listen 80;
    server_name archive.deinefirma.internal;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2;
    server_name archive.deinefirma.internal;
    ssl_certificate /etc/ssl/certs/archive.fullchain.pem;
    ssl_certificate_key /etc/ssl/private/archive.privkey.pem;
    ssl_protocols TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    location / {
        proxy_pass http://open-archiver:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
    }
}

Danach bindest du das Konfigurationsverzeichnis und die Zertifikate als Volume in den Nginx-Container ein. Starte den Proxy mit docker compose up -d nginx und prüfe sofort mit curl -I https://archive.deinefirma.internal ob der Redirect und die TLS-Parameter korrekt sind.

💡 Du kannst die Proxy-Konfiguration später um Basic-Auth oder OAuth vor dem eigentlichen Login ergänzen, falls du eine zusätzliche Schicht für hochregulierte Bereiche benötigst.

Die Firewall auf dem Host muss nur die Ports 80 und 443 freigeben. Alle internen Docker-Ports bleiben durch die Bridge-Isolation geschützt. Deaktiviere die Standard-EXPOSE-Anweisungen im open-archiver-Service, damit nichts versehentlich nach außen dringt. Für die Netzwerkkonfiguration innerhalb von Docker Compose definierst du ein dediziertes Netzwerk namens archiver-net, dem alle Services außer dem Proxy angehören. Der Proxy selbst läuft entweder im gleichen Compose-File oder in einem separaten Stack und verbindet sich über external: true.


docker network inspect archiver-net

Dieser Befehl zeigt dir, ob alle Container im korrekten Netzwerk hängen und welche IPs sie haben.

❗ Ein klassischer Fehler entsteht, wenn der Proxy den X-Forwarded-For-Header nicht korrekt setzt. Dann sieht der open-archiver-Service nur die Proxy-IP und kann keine Client-IPs mehr im Audit-Trail protokollieren.

🔧 Praktisches Beispiel:

Um die komplette Netzwerkkette zu testen, fügst du temporär einen Healthcheck-Endpunkt hinzu und rufst ihn vom Host aus auf:


curl -k -I https://archive.deinefirma.internal/api/health

Gleichzeitig beobachtest du die Logs:


docker compose logs -f nginx --tail=30

Du siehst sofort, ob die TLS-Handshakes erfolgreich sind oder ob Header verloren gehen. Passe die ssl_session_cache und ssl_session_timeout an, wenn du viele gleichzeitige Verbindungen erwartest. Für große Umgebungen aktiviere zusätzlich proxy_buffering off, damit große Suchergebnisse ohne Verzögerung durchkommen.

Die gesamte Konfiguration sollte in Git liegen, damit du sie bei Updates schnell rollbacken kannst. Vergiss nicht, die Nginx-Logs rotierend und mit Fail2Ban zu schützen, falls du brute-force Versuche auf die Login-Seite erwartest. In Kombination mit einer dedizierten VLAN- oder Firewall-Regel kannst du den Zugriff auf bestimmte IP-Bereiche beschränken, ohne dass der Docker-Stack selbst etwas davon merkt.

Sobald du den Proxy stabil betreibst, prüfe die TLS-Versionen mit openssl s_client -connect archive.deinefirma.internal:443 und stelle sicher, dass nur TLS 1.3 aktiv ist. Die interne Kommunikation zwischen Proxy und open-archiver läuft unverschlüsselt über das Bridge-Netzwerk, was akzeptabel ist, weil beide auf dem gleichen Host liegen. Bei verteilten Setups müsstest du hier mTLS nachrüsten, doch für die meisten produktiven Installationen reicht das aktuelle Setup vollkommen aus.

Nachdem die Netzwerkkonfiguration abgeschlossen ist, kommen wir zu den persistenten Volumes.

Persistent Storage

Volumes und Backup-Strategie

Die drei zentralen Docker-Volumes von Open Archiver speichern alles, was über einen Container-Neustart hinaus erhalten bleiben muss: die rohen E-Mails, den Datenbankinhalt und den Suchindex. Ohne korrekte Volume-Konfiguration verlierst du bei jedem docker compose down sämtliche archivierten Daten und den Audit-Trail. Deshalb legst du die Volumes explizit als named volumes oder bind mounts an und verknüpfst sie mit einem performantem Dateisystem.

Das Volume archiver-data nimmt alle .eml-Dateien, deduplizierten Anhänge und temporären Verarbeitungsdateien auf. Es wächst am schnellsten und sollte auf einem separaten NVMe-Pool oder ZFS-Datensatz liegen, der hohe IOPS liefert. PostgreSQL nutzt postgres-data für Metadaten, Policies und den unveränderlichen Audit-Log. Meilisearch wiederum schreibt seinen Index in meilisearch-data. Jedes Volume erhält in der docker-compose.yml einen eigenen Eintrag unter volumes, damit Docker sie außerhalb des Containers verwaltet.


volumes:
  archiver-data:
    driver: local
    driver_opts:
      type: none
      device: /mnt/archiver-data
      o: bind
  postgres-data:
    driver: local
  meilisearch-data:
    driver: local

Der bind mount für archiver-data erlaubt dir, das Verzeichnis direkt auf dem Host mit ZFS oder Btrfs zu formatieren und Snapshots zu erstellen. Du kannst die Volumes auch auf einem externen NFS-Share oder Ceph-Cluster auslagern, wenn du später auf mehrere Hosts skalierst. Wichtig ist die Konsistenz: alle Volumes müssen auf dem gleichen Dateisystem-Typ liegen, damit Backups atomar funktionieren.


Host Filesystem
├── /var/lib/docker/volumes/archiver-data/_data   ← .eml + Anhänge
├── /mnt/postgres-data                            ← Metadaten + Audit
├── /mnt/meilisearch-data                         ← Volltext-Index
└── ZFS Pool (snapshots + dedup)

🔧 Praktisches Beispiel:

Nach dem ersten docker compose up legst du die Volumes manuell an und setzt Berechtigungen. Führe folgende Befehle aus, um die Verzeichnisse vorzubereiten und die Ownership korrekt zu setzen:


sudo mkdir -p /mnt/archiver-data /mnt/postgres-data /mnt/meilisearch-data
sudo chown -R 1000:1000 /mnt/archiver-data /mnt/postgres-data
sudo chown -R 1000:1000 /mnt/meilisearch-data
sudo chmod 700 /mnt/archiver-data

Danach startest du den Stack neu und prüfst mit docker volume ls und docker volume inspect archiver-data, ob die Mounts korrekt gemappt sind. Du siehst sofort die reale Pfadangabe auf dem Host und kannst prüfen, ob Daten bereits geschrieben wurden.

Die Backup-Strategie baut direkt auf diesen Volumes auf. Weil Open Archiver revisionssicher arbeitet, darfst du keine einfachen rsync-Jobs über laufende Container laufen lassen. Stattdessen nutzt du Docker-interne Stop-and-Backup oder Dateisystem-Snapshots. Für ZFS erstellst du regelmäßige Snapshots des gesamten Pools:


zfs snapshot tank/archiver-data@daily-$(date +%Y%m%d)
zfs send -R tank/archiver-data@daily-$(date +%Y%m%d) | zstd -T0 > /backup/archiver-daily.zst

Das sichert nicht nur die Daten, sondern auch die Konsistenz der Hashes. Für PostgreSQL fügst du einen pg_dump vor dem Snapshot ein, damit der Audit-Trail immer im konsistenten Zustand gesichert wird. Meilisearch bietet einen eigenen Dump-Endpunkt, den du über die API aufrufen kannst, bevor du das Volume snapshotest. Kombiniere das mit einem täglichen Cron-Job, der die Backups auf einen externen Offsite-Speicher (S3 oder ein zweites Rechenzentrum) kopiert. Die Retention für Backups richtet sich nach GoBD: mindestens zehn Jahre, besser unbefristet mit monatlichen Vollbackups und täglichen Inkrementellen.

💡 Lege ein separates Volume für Backups an, das nie von Docker gemountet wird, damit ein Container-Kompromittierung nicht gleich alle Sicherungen gefährdet.

Die Restore-Prozedur testest du am besten in einer Staging-Umgebung. Stoppe den Stack, lösche die Volumes, restore die Snapshots und starte neu. Open Archiver erkennt automatisch, ob der Index neu aufgebaut werden muss, und startet die Re-Indizierung im Hintergrund. Das kann bei großen Archiven mehrere Stunden dauern, deshalb planst du Wartungsfenster ein.

Ein typischer Fehler passiert, wenn du die Volumes einfach mit docker compose down -v löschst, weil du denkst, die Daten liegen woanders. Dann ist der Audit-Trail unwiederbringlich verloren.

🔧 Praktisches Beispiel:

Um ein vollständiges Backup-Skript zu erstellen, legst du folgende Datei backup-archiver.sh an und machst sie ausführbar:


#!/bin/bash
DATE=$(date +%Y%m%d_%H%M)
docker compose -f /opt/open-archiver/docker-compose.yml exec postgres pg_dump -U archiver archiver > /backup/db-$DATE.sql
docker compose stop
zfs snapshot tank/archiver-data@backup-$DATE
zfs snapshot tank/postgres-data@backup-$DATE
zfs send -R tank/archiver-data@backup-$DATE | zstd > /offsite/archiver-data-$DATE.zst
docker compose start
echo "Backup $DATE completed"

Führe es mit sudo ./backup-archiver.sh aus und prüfe mit ls -lh /backup ob die Dateien entstanden sind. Das Skript stoppt nur kurz den Stack und minimiert Ausfallzeit.

Für die Überwachung der Volume-Nutzung setzt du df -h /mnt/archiver-data in deine Monitoring-Lösung ein und lässt bei 80 Prozent Füllstand alarmieren. Du kannst auch Docker-Stats mit docker stats beobachten, um zu sehen, welcher Container am meisten schreibt. Bei sehr großen Installationen wechselst du die Volumes auf einen dedizierten LVM-Thin-Provisioned-Logical-Volume, damit du Online-Resizing ohne Downtime machen kannst.

Die Backup-Strategie sollte immer 3-2-1-Regel berücksichtigen:3 Kopien, 2 verschiedene Medien, 1 offsite. Teste den Restore monatlich, denn nur ein geprüftes Backup ist ein echtes Backup. Open Archiver selbst bietet keine integrierte Backup-Funktion, deshalb liegt die Verantwortung komplett bei dir und deiner Infrastruktur. Mit den Volumes richtig konfiguriert und gesichert läuft der gesamte Stack stabil und du bist für Wachstum und eventuelle Katastrophen gerüstet.

Installation und Deployment

Repository klonen, Docker Compose und .env-Konfiguration

Mit den Volumes vorbereitet wechselst du nun in das Verzeichnis, in dem du den gesamten Stack dauerhaft betreiben willst. Der erste Schritt ist das Klonen des offiziellen Repositories, weil du damit nicht nur die aktuelle Docker Compose Datei und das Beispiel .env bekommst, sondern auch die Möglichkeit, später gezielt Tags oder Branches zu nutzen. Du legst ein dediziertes Verzeichnis an, das später nur dem Archiver-Stack gehört, und wechselst hinein.

Das git clone Kommando lädt den kompletten Quellcode samt Beispielkonfiguration herunter. In der Praxis nutzt du immer das main-Branch für produktive Umgebungen, weil dort die stabilsten Releases liegen.


mkdir -p /opt/open-archiver
cd /opt/open-archiver
git clone https://github.com/logiclabshq/open-archiver.git .
git checkout tags/v1.2.3

Das Repository enthält die docker-compose.yml, ein Beispiel .env und zusätzliche Skripte für Migrationen. Du kopierst die Beispiel-Dateien und passt sie an deine Umgebung an. Die docker-compose.yml definiert alle vier Services, ihre Abhängigkeiten, Healthchecks und Resource Limits. Du erweiterst sie um deine Volumes, das interne Netzwerk und die Ressourcenbeschränkungen, damit Meilisearch nicht den gesamten RAM frisst und PostgreSQL nicht bei Spitzenlast abstürzt. Jeder Service erhält explizite restart: unless-stopped Policies, damit der Stack nach einem Host-Reboot automatisch hochfährt.


/opt/open-archiver/
├── docker-compose.yml          ← Hauptdefinition
├── .env                        ← Deine Konfiguration
├── .env.example                ← Vorlage
├── volumes/                    ← Bind-Mounts
│   ├── archiver-data/
│   ├── postgres-data/
│   └── meilisearch-data/
└── nginx/                      ← Optionaler Proxy-Ordner

Die .env-Datei ist das Herzstück der Konfiguration. Sie enthält alle sensiblen Werte, die nie ins Repository gehören. Du kopierst .env.example nach .env und füllst die Variablen aus. Wichtige Einträge sind POSTGRES_PASSWORD, MEILI_MASTER_KEY und OPEN_ARCHIVER_SECRET. Der Datenbank-User sollte ein starkes, zufälliges Passwort bekommen, das du mit pwgen oder openssl rand erzeugst. Meilisearch benötigt einen eigenen Master-Key für die API-Authentifizierung. OPEN_ARCHIVER_SECRET dient als Application-Secret für Session- und JWT-Token. Zusätzlich setzt du MAIL_INGESTION_INTERVAL, MAX_WORKER_THREADS und STORAGE_DRIVER. Für S3-Speicher gibt es weitere Variablen wie S3_ENDPOINT und S3_BUCKET.


cp .env.example .env
nano .env

Du passt jede Zeile exakt an deine vorher festgelegten Volumes und Netzwerke an. Die Datei darf nur lesbar für root und den docker-User sein, damit kein anderer Prozess die Passwörter auslesen kann. Nach der Anpassung validierst du die Syntax mit einem einfachen grep, um Tippfehler in Variablennamen auszuschließen. Die docker-compose.yml referenziert diese .env-Datei automatisch über environment und env_file. Du fügst unter jedem Service resource Limits hinzu, damit ein fehlerhafter Import nicht den gesamten Host lahmlegt.

🔧 Praktisches Beispiel:

Um das Repository sauber zu klonen und die Konfigurationsdateien sofort vorzubereiten, führst du folgende Sequenz aus. Zuerst das Verzeichnis und das Klonen, dann das Kopieren und das Setzen der Berechtigungen:


sudo mkdir -p /opt/open-archiver
cd /opt/open-archiver
sudo git clone --depth 1 --branch main https://github.com/logiclabshq/open-archiver.git .
sudo cp .env.example .env
sudo chown -R root:docker /opt/open-archiver
sudo chmod 600 .env
sudo chmod 644 docker-compose.yml

Danach öffnest du .env und setzt mindestens zehn kritische Werte. Du speicherst und prüfst mit cat .env | grep -E 'PASSWORD|KEY|SECRET' ob alles korrekt gesetzt ist. Dieser Ablauf stellt sicher, dass du keine Berechtigungsprobleme beim ersten Start bekommst.

Die docker-compose.yml enthält neben den Services auch ein eigenes Netzwerk archiver-net und die Volume-Definitionen. Du erweiterst die open-archiver Service um depends_on mit condition: service_healthy, damit der Container erst startet, wenn Postgres und Meilisearch bereit sind. Healthchecks sind bereits vorgegeben, du kannst sie jedoch an deine Hardware anpassen, indem du das Intervall auf 10s setzt und retries auf 5 erhöhst. Resource Limits wie cpus: "2.0" und memory: 4g für Meilisearch verhindern, dass die Suchmaschine bei großen Indizierungen den Host überlastet.

⚠️ Vergiss niemals, die .env-Datei aus der Git-History zu entfernen, falls du das Repository später committest. Ein einziges git add .env würde alle Secrets exponieren.

Du testest die Konfiguration, bevor du den Stack startest, indem du docker compose config ausführst. Der Befehl rendert die finale Compose-Datei mit allen .env-Variablen und zeigt dir sofort Syntaxfehler oder fehlende Referenzen. Falls du mehrere Umgebungen betreibst, legst du .env.prod und .env.stage an und lädst sie mit --env-file. Das erlaubt dir, Entwicklungs- und Produktionsinstanzen auf dem gleichen Host zu trennen, ohne Konflikte bei Volume-Namen.

💡 Du kannst die docker-compose.yml um profiles erweitern, damit du mit docker compose --profile monitoring up zusätzliche Tools wie Prometheus Exporter starten kannst, ohne die Kernkonfiguration zu verändern.

❗ Ein häufiger Fehler entsteht, wenn du die .env-Variablen nicht exakt so schreibst, wie sie in der Compose-Datei referenziert werden. Dann startet Postgres mit dem Default-Passwort und der gesamte Stack schlägt bei der ersten Migration fehl.

🔧 Praktisches Beispiel:

Nachdem du die .env angepasst hast, validierst du die komplette Konfiguration mit einem Trockenlauf. Der folgende Befehl zeigt die aufgelöste Compose-Datei und lässt dich prüfen, ob alle Secrets korrekt injiziert wurden:


docker compose config --quiet
docker compose config | grep -A 20 'environment:'

Du siehst die expandierten Umgebungsvariablen und kannst sicher sein, dass kein Passwort fehlt. Falls etwas fehlt, korrigierst du in .env und wiederholst den Check. Dieser Schritt spart dir später viel Debugging beim ersten Start.

Die gesamte Konfiguration liegt jetzt versioniert und reproduzierbar vor. Du kannst das Verzeichnis mit rsync auf einen Backup-Host sichern oder in ein privates Git-Repo pushen, solange die .env ausgeschlossen bleibt. Mit git tag legst du nach jeder Änderung einen Checkpoint an, damit du bei Problemen exakt auf die laufende Version zurückkehren kannst. Die Kombination aus geklontem Repository, angepasster Compose-Datei und sicherer .env schafft die Grundlage für einen stabilen ersten Start.

Sobald die .env-Datei fertig konfiguriert ist, kannst du mit dem ersten Start fortfahren.

Start, Initialisierung und Admin-Account

Sobald die .env-Datei fertig konfiguriert ist, startest du den gesamten Stack mit einem einzigen Befehl. Der Docker Compose Manager liest die Konfiguration, zieht bei Bedarf die aktuellen Images und startet die Services in der richtigen Reihenfolge dank der depends_on Bedingungen. Du führst den Start im Hintergrund aus, damit du sofort die Logs beobachten kannst. Der Befehl aktiviert alle Healthchecks automatisch und wartet nicht auf manuelle Eingaben.


docker compose up -d

Der erste Start dauert länger als spätere Neustarts, weil PostgreSQL die Datenbank initialisiert, Meilisearch seinen ersten Index anlegt und der open-archiver Container die Migrationsskripte ausführt. Du siehst in der Ausgabe sofort, welche Container hochgefahren sind und ob einer in den Restart-Loop geht. Der Stack benötigt jetzt einige Minuten, bis alle Services healthy sind.


Start-Sequenz
├── docker compose up -d
│
├── postgres (init + migration)
├── valkey (queue ready)
├── meilisearch (index bootstrap)
│
└── open-archiver (API + Scheduler + first-run setup)

🔧 Praktisches Beispiel:

Um den Startvorgang live zu verfolgen und keine Fehlermeldung zu verpassen, öffnest du ein zweites Terminal und rufst die Logs aller Services ab. Der Befehl zeigt die letzten 100 Zeilen und folgt dem Output in Echtzeit:


docker compose logs -f --tail=100

Du siehst Zeilen wie Postgres ready, Valkey listening on 6379 und Meilisearch initialized with master key. Sobald open-archiver: Application started successfully erscheint, ist der Initialisierungsprozess abgeschlossen. Du kannst den Befehl jederzeit mit Strg+C abbrechen und später gezielt auf einen einzelnen Service eingrenzen.

Die Initialisierung umfasst mehrere interne Schritte. Zuerst prüft der open-archiver Container die Datenbankverbindung und führt Flyway- oder Liquibase-Migrationen aus. Diese Skripte erzeugen die Tabellen für Metadaten, Audit-Log, Retention-Policies und Legal-Holds. Gleichzeitig initialisiert Meilisearch den Hauptindex mit den notwendigen Feldern für Betreff, Body, Attachment-Text und Hashes. Valkey legt die BullMQ-Queues für Ingestion-Jobs und Re-Indizierungen an.

Falls du zum ersten Mal startest, erzeugt das Backend außerdem die Default-Konfigurationswerte für Scheduler-Intervalle und Storage-Settings. Der gesamte Vorgang ist idempotent, sodass ein Neustart keine doppelten Tabellen erzeugt.

Du überprüfst den Status aller Container mit einem einfachen ps-Befehl. Die Ausgabe zeigt dir den aktuellen State, die laufende Zeit und ob Restarts stattgefunden haben.


docker compose ps

Sobald alle Services Up und healthy anzeigen, ist der Stack betriebsbereit.

Der open-archiver Container startet nun seinen internen Setup-Routine. Diese Routine legt die notwendigen Verzeichnisse im archiver-data Volume an und prüft die Konnektivität zu allen Abhängigkeiten. Falls etwas fehlt, bricht sie mit einer klaren Fehlermeldung ab, die du direkt in den Logs findest.

Die Admin-Account-Einrichtung erfolgt über einen dedizierten CLI-Befehl, der im Container ausgeführt wird. Du nutzt den exec-Befehl, um in den open-archiver Container zu wechseln und das Admin-Creation-Tool aufzurufen. Der Befehl verlangt E-Mail, vollen Namen und ein starkes Passwort. Das Passwort wird sofort gehasht und in PostgreSQL gespeichert. Der Account erhält die Super-Admin-Rolle mit vollen Rechten auf alle Policies und den Audit-Trail.


docker compose exec open-archiver ./bin/archiver admin create --email admin@deinefirma.internal --name "System Administrator" --password "DeinSehrStarkesPasswort2026!"

Der Befehl gibt eine Bestätigung aus und zeigt den generierten API-Token für erste Tests. Du kannst den Account auch über die Web-Oberfläche anlegen, sobald der Proxy läuft, doch der CLI-Weg ist sicherer für den Erststart, weil keine ungeschützte HTTP-Verbindung benötigt wird. Der neue Account wird sofort im Audit-Log vermerkt, sodass du später nachvollziehen kannst, wann und von welchem Host der erste Admin angelegt wurde.

Nach der Account-Erstellung testest du den Zugriff.

Du rufst die Health-Endpoint oder die Login-Seite auf, um zu prüfen, ob alles erreichbar ist. Der erste Login über die Web-Oberfläche fordert dich auf, das Passwort zu ändern und 2FA einzurichten, falls du das in der .env aktiviert hast. Die Session wird mit dem Application-Secret signiert und im Browser gespeichert.

💡 Du kannst den Admin-Befehl auch mit --force ausführen, falls du den Account später zurücksetzen musst, ohne den gesamten Stack neu zu initialisieren.

Du beobachtest während der gesamten Initialisierung die Ressourcennutzung mit docker stats, um sicherzustellen, dass Meilisearch nicht mehr als die zugewiesenen 4 GB RAM verbraucht. Der erste Index-Bootstrap schreibt viele kleine Einträge, daher siehst du kurzfristig höhere I/O-Werte auf dem meilisearch-data Volume.


docker stats

Der Befehl aktualisiert sich alle paar Sekunden und gibt dir CPU, Memory und Netzwerkwerte für jeden Container. Du kannst ihn parallel zum Log-Follow laufen lassen.

⚠️ Starte niemals den Stack ohne vorherige Volume-Berechtigungen, weil PostgreSQL sonst mit Permission-Denied-Fehlern abbricht und die Initialisierung nicht abschließt.

Nach dem erfolgreichen Admin-Setup prüfst du den Audit-Trail, ob der erste Eintrag korrekt geschrieben wurde. Du verbindest dich mit psql und führst eine einfache Abfrage aus.


docker compose exec postgres psql -U archiver -d archiver -c "SELECT * FROM audit_log WHERE action = 'admin_created' ORDER BY timestamp DESC LIMIT 1;"

Die Ausgabe bestätigt dir Timestamp, Actor und alle Details. Das ist der erste Nachweis, dass der Revisions-Trail funktioniert. Du kannst jetzt den Stack als stabil betrachten und mit der nächsten Phase fortfahren.

❗ Ein häufiger Fehler tritt auf, wenn du das Passwort im CLI-Befehl mit Sonderzeichen vergisst zu quoten. Dann bricht der Befehl mit Parse-Error ab und der Account wird nicht angelegt.

🔧 Praktisches Beispiel:

Um den gesamten Initialisierungs- und Account-Prozess in einem durchgehenden Workflow zu automatisieren, legst du ein kurzes Shell-Skript an. Das Skript startet den Stack, wartet auf Healthy-Status und legt den Admin an:


#!/bin/bash
docker compose up -d
echo "Warte auf Healthy-Status..."
sleep 30
until docker compose ps | grep -q "(healthy)"; do sleep 10; done
docker compose exec open-archiver ./bin/archiver admin create --email admin@deinefirma.internal --name "System Administrator" --password "DeinSehrStarkesPasswort2026!"
echo "Admin-Account erstellt. Erster Login möglich."

Du machst das Skript ausführbar mit chmod +x init.sh und führst es mit ./init.sh aus. Das Skript wartet aktiv und gibt dir klare Rückmeldung. Du kannst es später für Staging-Umgebungen wiederverwenden oder in deine Ansible-Rolle einbinden.

Die Initialisierung schreibt auch Default-Retention-Policies und Scheduler-Einstellungen in die Datenbank. Du findest sie später in der Web-Oberfläche unter Settings. Der erste Scheduler-Job wird automatisch geplant und läuft im Hintergrund, ohne dass du weitere Aktionen brauchst.

Du überwachst die ersten Minuten nach dem Start besonders genau, weil hier die meisten Konfigurationsfehler sichtbar werden. Die Logs zeigen dir genau, welche Migrationen durchlaufen sind und ob alle Keys korrekt geladen wurden. Sobald der Admin-Account aktiv ist und der erste Login funktioniert, ist der technische Grundstein für den produktiven Betrieb gelegt. Du kannst den Stack jetzt als vollständig initialisiert betrachten.

Erste Überprüfung und System-Validierung

Nun beginnst du mit der Statusübersicht aller Container, um sicherzustellen, dass jeder Service läuft und die Healthchecks bestanden hat. Der Befehl listet Namen, Status, Ports und Restart-Zähler auf.

Bei einem sauberen Start zeigen alle vier Services Up und healthy an.


docker compose ps

Die Ausgabe gibt dir sofort Klarheit, ob der open-archiver Container korrekt gestartet ist oder ob ein Abhängigkeitsproblem vorliegt. Anschließend holst du dir die aktuellen Logs, um die Initialisierungsschritte zu validieren. Du filterst auf die letzten 200 Zeilen und suchst nach Schlüsselwörtern wie migration completed, index initialized und scheduler started.


docker compose logs --tail=200 | grep -E 'migration|index|healthy|started'

Der open-archiver Service protokolliert jeden Schritt der ersten Überprüfung, sodass du siehst, ob die Datenbankverbindung, der Valkey-Queue und der Meilisearch-Index korrekt verbunden sind. Du wechselst nun zur API-Ebene und testest den Health-Endpoint über den Reverse-Proxy. Ein einfacher curl-Aufruf mit dem korrekten Hostnamen und Headern liefert dir den Statuscode 200 und eine JSON-Antwort mit System-Informationen.


curl -k -H "Authorization: Bearer $(docker compose exec open-archiver ./bin/archiver admin token)" https://archive.deinefirma.internal/api/health

Die Antwort bestätigt dir, dass die REST-API erreichbar ist, die Datenbankverbindung steht und der Queue-Worker läuft. Gleichzeitig prüfst du die PostgreSQL-Tabellen direkt. Du verbindest dich mit psql und zählst die Einträge in den wichtigsten Tabellen. Der Audit-Log sollte bereits den Eintrag für die Admin-Erstellung enthalten.


docker compose exec postgres psql -U archiver -d archiver -c "\dt"
docker compose exec postgres psql -U archiver -d archiver -c "SELECT COUNT(*) FROM audit_log;"

Du siehst die Tabellen users, audit_log, retention_policies, legal_holds und ingestion_jobs. Die Zählung zeigt dir, dass die Initialmigration erfolgreich war.


Validierungs-Checkliste
├── Containers: all healthy
├── Logs: no errors
├── API Health: 200 OK
├── DB Tables: present + populated
├── Volumes: mounted + writable
├── Meilisearch: index ready
└── Audit-Trail: first entry logged

🔧 Praktisches Beispiel:

Um die komplette erste Überprüfung reproduzierbar zu machen, erstellst du ein kurzes Shell-Skript validate-first-run.sh. Das Skript führt alle Checks nacheinander aus und gibt eine klare Zusammenfassung aus:


#!/bin/bash
echo "=== Container Status ==="
docker compose ps
echo "=== Health Check ==="
curl -k -s -o /dev/null -w "%{http_code}" https://archive.deinefirma.internal/api/health
echo "=== Audit Log Entries ==="
docker compose exec postgres psql -U archiver -d archiver -c "SELECT COUNT(*) FROM audit_log WHERE action LIKE '%admin%';"
echo "=== Volume Usage ==="
df -h /mnt/archiver-data
echo "Validation complete."

Du machst es ausführbar mit chmod +x validate-first-run.sh und startest es mit ./validate-first-run.sh. Das Skript liefert dir in Sekunden eine vollständige Übersicht und du kannst es später in deine Monitoring-Pipeline einbinden.

Du kontrollierst zusätzlich die Volumes auf korrekte Größe und Berechtigungen. Der Befehl zeigt dir die tatsächlichen Pfade auf dem Host und die genutzten Bytes.


docker volume inspect archiver-data postgres-data meilisearch-data
du -sh /mnt/archiver-data

Die Werte sollten bei einem frischen System unter 100 MB liegen, außer wenn du bereits Testdaten importiert hast. Du testest außerdem die Meilisearch-Index-Integrität über die dedizierte Admin-API. Ein GET auf den Stats-Endpunkt zeigt dir die Anzahl der indizierten Dokumente und den aktuellen Speicherbedarf des Indexes.

Du rufst die Web-Oberfläche im Browser auf und meldest dich mit dem neu erstellten Admin-Account an. Die Login-Seite lädt, die 2FA-Einrichtung erscheint (falls aktiviert) und das Dashboard zeigt System healthy sowie leere Postfach-Übersichten. Du navigierst zum Audit-Trail-Bereich und siehst den ersten Eintrag mit korrektem Timestamp und Actor.

💡 Du kannst den Health-Check auch in dein Prometheus-Setup einbinden, indem du den Endpoint als scrape_target konfigurierst und Alerts bei Status 500 oder höher setzt.

Die Netzwerkverbindungen innerhalb des Docker-Bridge-Netzwerks prüfst du mit einem einfachen ping zwischen den Containern oder mit docker network inspect archiver-net. Alle Services müssen sich gegenseitig erreichen können, ohne dass der Proxy dazwischen liegt. Du überprüfst die Resource-Limits, indem du docker stats aufrufst und beobachtest, ob Meilisearch oder PostgreSQL in den ersten Minuten nach dem Start über die definierten Grenzen hinausgehen.


docker stats --no-stream

Die Werte bleiben stabil, wenn du die cpus- und memory-Limits in der Compose-Datei richtig gesetzt hast. Du validierst abschließend die Scheduler-Konfiguration, indem du den nächsten geplanten Ingestion-Job in der Datenbank abfragst.


docker compose exec postgres psql -U archiver -d archiver -c "SELECT * FROM scheduler_jobs LIMIT 3;"

Die Ausgabe zeigt dir geplante Tasks und bestätigt, dass der System-Timer korrekt läuft.

⚠️ Führe die Überprüfung immer vollständig durch, bevor du den ersten Mail-Connector anlegst, weil ein fehlender Health-Status später zu fehlenden Audit-Einträgen führen kann.

Du dokumentierst die Ergebnisse aller Checks in einer Textdatei oder in deinem Ticket-System, damit du bei späteren Updates einen Baseline-Vergleich hast. Die erste Überprüfung ist abgeschlossen, wenn alle Komponenten grün sind und der Audit-Trail den Initial-Setup-Eintrag enthält.

❗ Ein typischer Fehler entsteht, wenn du den Proxy-Health-Check vergisst und nur intern curlst. Dann übersiehst du Header-Probleme, die später die gesamte Web-Oberfläche unbenutzbar machen.

Nachdem die System-Validierung abgeschlossen ist, kannst du nun im nächsten Schritt die E-Mail-Quellen einbinden.

E-Mail-Quellen einbinden

IMAP, Google Workspace und Microsoft 365 verbinden

Mit der System-Validierung abgeschlossen kannst du die E-Mail-Quellen einbinden. Du nutzt den integrierten CLI des open-archiver Containers, um neue Quellen anzulegen und sofort mit dem Ingestion zu beginnen. Der Befehl ./bin/archiver source add erwartet den Typ der Quelle und die entsprechenden Credentials. Für klassische IMAP-Server gibst du Host, Port, Username, Password sowie optional den zu archivierenden Folder und das Sync-Intervall an. Der Connector verwendet IMAP mit SSL/TLS auf Port 993, prüft das Server-Zertifikat gegen das System-CA-Bundle und aktiviert IDLE für Echtzeit-Benachrichtigungen, falls der Server es unterstützt. Du kannst mehrere Ordner wie INBOX, Sent und Archive gleichzeitig überwachen und über die Option --folder gezielt filtern.

Google Workspace verlangt einen dedizierten Service Account mit Domain-weiter Delegation. Du erstellst ihn in der Google Admin Console, aktivierst die Gmail API und lädst die JSON-Key-Datei herunter. Im CLI übergibst du --type gworkspace --keyfile /path/to/service-account.json --domain deinefirma.com und die notwendigen Scopes werden automatisch gesetzt. Der Connector nutzt die Gmail API statt IMAP, um Rate-Limits zu umgehen und Delta-Änderungen effizient abzurufen.

Jede Mail wird mit ihrer Message-ID und Thread-ID indiziert, sodass du später exakte Duplikat-Erkennung hast.

Microsoft 365 setzt eine App-Registration in Azure Entra ID voraus. Du registrierst eine neue App, vergibst Application Permissions wie Mail.ReadWrite.All und User.Read.All sowie die Grant-Admin-Consent. Anschließend generierst du ein Client Secret oder ein Zertifikat und legst den Source mit --type m365 --tenant-id xxx --client-id yyy --client-secret zzz an. Der Connector verwendet Microsoft Graph API mit Delta-Queries, um nur geänderte Mails zu holen und Bandbreite zu sparen.

Die Authentifizierung erfolgt über OAuth2 Client Credentials Flow, sodass kein interaktives Login nötig ist.


E-Mail-Quellen
├── IMAP (Port 993/SSL) → open-archiver:3000/ingest
├── GWorkspace (Gmail API) → Service Account JSON
└── M365 (Graph API) → Azure App + Client Secret
          │
          ▼
   Valkey Queue → Worker → Parse → SHA-256 → archiver-data + Meilisearch

Du führst alle Befehle im Container aus, damit die Credentials nie den Host verlassen. Nach dem Hinzufügen startet der Scheduler automatisch den ersten Sync. Du kannst den Status mit ./bin/archiver source list prüfen und siehst sofort, ob die Verbindung steht und wie viele Mails bereits verarbeitet wurden.

🔧 Praktisches Beispiel:

Um eine komplette IMAP-Quelle in einem Rutsch anzulegen und zu testen, führst du folgende Sequenz aus.

Zuerst wechselst du in den Container und legst den Source an:


docker compose exec open-archiver ./bin/archiver source add \
  --type imap \
  --name "Exchange-IMAP" \
  --host mail.deinefirma.internal \
  --port 993 \
  --username archiv@deinefirma.internal \
  --password "DeinStarkesIMAPPasswort2026!" \
  --folder INBOX,Sent \
  --interval 60 \
  --ssl true

Danach prüfst du den Status:


docker compose exec open-archiver ./bin/archiver source list

Der Befehl gibt dir die ID, den Typ, den aktuellen Sync-Status und die letzte erfolgreiche Verarbeitung aus. Du kannst sofort mit docker compose logs open-archiver --tail=50 | grep ingest die ersten verarbeiteten Mails beobachten.

Für Google Workspace lädst du den JSON-Key zuerst per SCP auf den Host und bindest ihn als Volume ein. Danach fügst du den Source hinzu und aktivierst die Delegation. Bei Microsoft 365 erstellst du die App-Registration vorher im Azure Portal und kopierst die Werte in den Befehl. Jeder Source erhält eine eindeutige ID, die du später für Retention-Policies oder Legal Holds verwendest.

Die Reihenfolge der Quellen spielt keine Rolle, weil der Worker asynchron arbeitet. Du kannst beliebig viele Quellen parallel betreiben, solange die Resource-Limits der Worker ausreichen. Die Credentials werden verschlüsselt in PostgreSQL abgelegt und nur zur Laufzeit entschlüsselt. Der Audit-Trail protokolliert jeden erfolgreichen oder fehlgeschlagenen Sync-Versuch mit Timestamp, Source-ID und Anzahl verarbeiteter Mails.

⚠️ Vergiss niemals, das Service-Account-JSON oder das Client-Secret nach der Einrichtung vom Host zu löschen, weil diese Dateien sonst ungeschützt bleiben.

Du kannst die Quellen auch nachträglich bearbeiten oder pausieren, ohne den gesamten Stack neu zu starten. Der Scheduler respektiert die eingestellten Intervalle und back-off-Strategien bei Fehlern. Bei IMAP prüft der Connector zusätzlich die Mailbox-Größe und bricht ab, wenn das Quota überschritten wird. Google Workspace und Microsoft 365 nutzen API-spezifische Rate-Limit-Header, um die Abfragen dynamisch zu drosseln.

💡 Du kannst mehrere Accounts desselben Typs anlegen, wenn du Postfächer verschiedener Abteilungen getrennt archivieren willst. Die Web-Oberfläche gruppiert sie später übersichtlich.

Die erste Synchronisation kann bei großen Postfächern mehrere Stunden dauern. Du beobachtest den Fortschritt live über die Logs oder die API-Endpunkte. Nach dem ersten vollständigen Sync wird nur noch Delta-Archivierung durchgeführt, sodass der tägliche Aufwand gering bleibt. Der Worker parst jede Mail, berechnet den SHA-256-Hash und übergibt den Inhalt an Meilisearch. Gleichzeitig landet die rohe .eml-Datei im archiver-data Volume.

❗ Ein typischer Fehler entsteht, wenn du bei Microsoft 365 die Application Permissions vergisst und nur Delegated Permissions vergibst. Dann schlägt die Authentifizierung mit 401-Fehlern fehl und keine einzige Mail wird archiviert.

Du testest jede neue Quelle einzeln, bevor du die nächste hinzufügst. Das verhindert, dass ein fehlerhafter Connector den gesamten Queue blockiert. Die CLI gibt dir detaillierte Fehlermeldungen, die du direkt in den Logs findest. Sobald alle Quellen verbunden und der erste Sync erfolgreich war, ist der Ingestion-Prozess produktiv. Du kannst jetzt Retention-Policies und Legal Holds konfigurieren, um die Archivierung vollständig abzusichern.

Sobald die Quellen verbunden sind, kannst du mit dem PST-Import, lokalen Pfade und manueller Ingestion fortfahren.

PST-Import, lokale Pfade, manuelle Ingestion

Sobald die Quellen verbunden sind, kannst du nun den PST-Import, lokale Pfade und manuelle Ingestion angehen. Der PST-Import ist besonders relevant, wenn du Legacy-Archive aus Outlook oder Exchange migrieren musst. Du legst die .pst-Dateien zuerst in ein dediziertes Verzeichnis auf dem Host ab, bindest es als Volume in den open-archiver Container ein und startest den Import über die CLI. Der Befehl ./bin/archiver import pst parst die komplette Datei, extrahiert alle Ordnerstrukturen, wandelt jede Nachricht in eine .eml-Datei um, berechnet den SHA-256-Hash und leitet sie direkt in die Ingestion-Pipeline weiter.

Große PST-Dateien mit mehreren Gigabyte werden chunkweise verarbeitet, damit der Worker nicht durch Speichermangel abstürzt.

Du kannst mit --folder-filter nur bestimmte Ordner importieren und mit --skip-duplicates bereits archivierte Mails überspringen.

Lokale Pfade dienen für den Import von bereits vorhandenen .eml-Dateien oder ganzen Verzeichnisbäumen, die du aus Backups oder alten Mailservern extrahiert hast. Du bindest das Verzeichnis über einen bind mount in die Compose-Datei ein und verwendest den Befehl ./bin/archiver import path. Der Connector durchläuft rekursiv alle Dateien, ignoriert Nicht-Mail-Dateien und übergibt nur valide .eml an den Parser. Das Verfahren ist ideal, wenn du eine einmalige Migration aus einem alten IMAP-Server oder einer Dateifreigabe durchführst. Die Dateien bleiben im Originalpfad erhalten und werden nur kopiert, sodass du die Quelle als Backup behalten kannst.

Manuelle Ingestion schließlich ist der Weg für einzelne Nachrichten oder kleine Mengen, die du ad-hoc hinzufügen willst. Der Befehl ./bin/archiver ingest eml akzeptiert entweder eine einzelne Datei oder ein Verzeichnis. Du kannst ihn auch per Pipe ausführen, wenn du Mails aus einem anderen Tool weiterleitest. Jede eingehende Datei wird sofort gehasht, gespeichert und indiziert, genau wie bei automatischen Quellen. Der Audit-Trail erhält einen Eintrag mit dem Typ „manual_ingest“ und dem ausführenden User.


Import-Pipeline
├── PST-Datei (.pst) → Volume-Mount → Parser → .eml + Hash
├── Lokaler Pfad (/data/emls/) → rekursiv → Filter → Ingestion
└── Manuelle .eml → CLI oder Pipe → Worker → archiver-data + Meilisearch
          │
          ▼
   Valkey Queue → Deduplizierung → SHA-256 → Audit-Eintrag

Der gesamte Prozess ist so gestaltet, dass er nahtlos in die bestehende Pipeline passt. Du kannst PST-Importe und lokale Pfade parallel zu den Live-Quellen laufen lassen, ohne dass sich die Scheduler überschneiden. Die CLI gibt dir Fortschrittsbalken und eine Zusammenfassung am Ende mit Anzahl importierter, übersprungener und fehlgeschlagener Mails.

🔧 Praktisches Beispiel:

Um eine große PST-Datei und einen lokalen .eml-Ordner in einem durchgehenden Workflow zu importieren, erstellst du zuerst die nötigen Bind-Mounts in der docker-compose.yml und startest den Container neu.

Danach führst du folgende Befehle aus:


docker compose exec open-archiver ./bin/archiver import pst \
  --path /import/old-archive.pst \
  --name "Exchange-2016-Backup" \
  --skip-duplicates true \
  --workers 4
docker compose exec open-archiver ./bin/archiver import path \
  --path /import/eml-folder/ \
  --recursive true \
  --filter "*.eml"

Anschließend prüfst du den Status mit:


docker compose exec open-archiver ./bin/archiver import status

Der Befehl zeigt dir den aktuellen Fortschritt, die verarbeiteten Bytes und eventuelle Fehler. Du kannst die Logs parallel mit docker compose logs open-archiver --tail=100 | grep import beobachten und siehst jede verarbeitete Mail mit ihrem Hash. Dieser Workflow lässt sich später in ein Skript packen, das du bei Bedarf für weitere Migrationen aufrufst.

Die Volume-Bindings musst du sorgfältig planen, damit der Container Schreibrechte hat und die Dateien nicht auf einem langsamen NFS-Share liegen. Bei PST-Dateien über 10 GB solltest du zusätzliche Worker mit --workers aktivieren, um die Verarbeitung zu beschleunigen. Der Parser entpackt auch verschachtelte Ordner und PST-Attachments korrekt und übergibt alles an Meilisearch.

Lokale Pfade können sehr groß sein; der CLI bricht bei zu vielen Dateien nicht ab, sondern verarbeitet sie in Batches.

💡 Du kannst den Import auch über die Web-Oberfläche starten, wenn du die Dateien bereits im archiver-data Volume abgelegt hast, doch der CLI-Weg ist schneller und erlaubt bessere Skripting-Möglichkeiten.

Die manuelle Ingestion eignet sich besonders gut für Testzwecke oder wenn du eine einzelne Mail aus einem Support-Ticket nachträglich archivieren musst. Du kannst die Datei direkt vom Host in den Container kopieren oder per cat mail.eml | docker compose exec open-archiver ./bin/archiver ingest eml --stdin. Der Hash wird sofort geprüft und der Audit-Eintrag angelegt, sodass du später nachweisen kannst, wann und von wem die Mail manuell hinzugefügt wurde.

Die Deduplizierung arbeitet bei allen drei Methoden identisch: gleiche SHA-256-Hashes führen zu einer Referenz statt einer zweiten Kopie. Das spart enorm viel Platz bei großen Migrationen, in denen viele Mails doppelt vorkommen. Du siehst die tatsächliche Speicherersparnis später in den Volume-Statistiken.

⚠️ Achte darauf, dass das Import-Verzeichnis nicht unter dem archiver-data Volume liegt, sonst riskierst du ungewollte Verschachtelungen und Performance-Einbußen beim späteren Backup.

Die CLI protokolliert jeden Import-Schritt detailliert, inklusive der Anzahl extrahierter Anhänge und OCR-Ergebnisse bei Bild-PDFs. Du kannst den Prozess jederzeit mit Ctrl+C unterbrechen; der aktuelle Batch wird sauber abgeschlossen, damit keine halb verarbeiteten Mails entstehen. Nach Abschluss des Imports prüfst du mit der API, ob die neuen Mails im Index erscheinen und die Suche funktioniert.

❗ Ein typischer Fehler entsteht, wenn du vergisst, das PST-Volume vor dem Import zu mounten. Dann schlägt der Befehl mit „file not found“ fehl und du musst den Container neu starten, um den Pfad sichtbar zu machen.

Du kannst mehrere PST-Dateien nacheinander importieren, ohne dass der Queue überläuft, weil der Worker asynchron arbeitet. Für sehr große Migrationen legst du temporär mehr RAM und CPU für den open-archiver Service fest und erhöhst die Worker-Anzahl. Nach dem Import bleiben die Original-PST-Dateien unberührt, sodass du sie als Backup behalten kannst. Die manuelle Ingestion erlaubt dir auch, einzelne .eml-Dateien aus EML-Exporten von anderen Systemen einzuspielen.

Die gesamte Funktionalität ist so gestaltet, dass sie nahtlos mit den bereits verbundenen Live-Quellen koexistiert. Du siehst in der Web-Oberfläche später alle importierten Mails einheitlich in der Suche, unabhängig vom Import-Typ. Der Audit-Trail unterscheidet klar zwischen automatischem Sync und manuellem Import, sodass du bei Revisionen immer den genauen Herkunftsnachweis hast.

❗ Ein typischer Fehler entsteht, wenn du vergisst, das PST-Volume vor dem Import zu mounten. Dann schlägt der Befehl mit file not found fehl und du musst den Container neu starten, um den Pfad sichtbar zu machen.

Mit diesen Import-Methoden kannst du auch sehr alte Archive oder einmalige Datensätze vollständig in das revisionssichere System überführen. Die Verarbeitungsgeschwindigkeit hängt primär von der I/O-Leistung des archiver-data Volumes ab; auf einer schnellen NVMe-SSD schaffst du mehrere Tausend Mails pro Minute. Du dokumentierst die verwendeten Befehle und die erzielten Ergebnisse, damit du den Vorgang bei Bedarf exakt reproduzieren kannst.

Retention-Policies

Sobald PST-Import, lokale Pfade und manuelle Ingestion konfiguriert sind, kannst du die Retention-Policies, Legal Holds und automatisierte Regeln anlegen. Retention-Policies definieren, wie lange Mails und Anhänge im Archiv bleiben, bevor sie automatisch gelöscht werden. Du legst sie entweder global oder quellspezifisch an und verknüpfst sie mit Bedingungen wie Alter, Absender-Domain oder Folder. Der CLI-Befehl ./bin/archiver policy create erlaubt dir, eine Policy mit --name, --retention-days und --scope zu erstellen. Eine Policy mit 3650 Tagen entspricht den üblichen GoBD-Vorgaben für zehn Jahre. Nach Ablauf der Frist markiert der Scheduler die betroffenen Objekte als deletable und entfernt sie aus dem archiver-data Volume sowie aus dem Meilisearch-Index.

Legal Holds sperren jede Löschung unabhängig von Retention-Policies. Du setzt einen Hold auf eine gesamte Quelle, einen bestimmten User oder einen einzelnen Thread. Der Befehl ./bin/archiver hold create verlangt eine Hold-ID, den betroffenen Scope und einen Grund. Solange der Hold aktiv ist, ignoriert das Lösch-Subsystem alle Policies. Das ist essenziell für laufende Rechtsstreitigkeiten oder Betriebsprüfungen. Der Audit-Trail protokolliert jede Hold-Aktion mit dem ausführenden Admin und dem genauen Zeitpunkt.

Automatisierte Regeln verbinden beides. Du kannst Regeln definieren, die bei bestimmten Kriterien automatisch eine Policy oder einen Hold anwenden. Ein Beispiel ist eine Regel, die bei Mails mit „confidential“ im Betreff automatisch einen Legal Hold setzt oder die Retention auf 15 Jahre verlängert. Die Regeln werden in der Datenbank gespeichert und vom Scheduler bei jeder neuen Ingestion ausgewertet. Du legst sie mit ./bin/archiver rule create an und verwendest eine einfache Bedingungssprache mit Feldern wie from, subject, attachment-type oder received-after.


Policy & Rule Engine
├── Neue Mail → Parser
│
├── Regel-Engine prüft Bedingungen
│   ├── Match → Legal Hold anwenden
│   └── Match → Retention-Policy zuweisen
│
├── Scheduler prüft täglich
│   ├── Hold aktiv? → nie löschen
│   └── Retention abgelaufen? → löschen + Audit-Eintrag
└── Alles in PostgreSQL + Audit-Trail

Die Policies und Holds sind versioniert. Jede Änderung erzeugt einen neuen Eintrag im Audit-Log, sodass du später exakt nachvollziehen kannst, wann welche Regel aktiv war. Du kannst Policies auch pausieren oder deaktivieren, ohne sie zu löschen, falls du temporär andere Aufbewahrungsfristen brauchst.

🔧 Praktisches Beispiel:

Um eine globale 10-Jahres-Retention-Policy mit automatisierter Regel für vertrauliche Mails anzulegen, führst du folgende Befehle nacheinander aus:


docker compose exec open-archiver ./bin/archiver policy create \
  --name "GoBD-Standard-10y" \
  --retention-days 3650 \
  --global true \
  --description "Standard-Aufbewahrung nach GoBD"
docker compose exec open-archiver ./bin/archiver rule create \
  --name "Confidential-Hold" \
  --condition 'subject contains "vertraulich" OR subject contains "confidential"' \
  --action "apply_hold" \
  --hold-name "Legal-Review-Required"

Danach prüfst du mit:


docker compose exec open-archiver ./bin/archiver policy list
docker compose exec open-archiver ./bin/archiver rule list

Die Ausgabe zeigt dir alle aktiven Policies und Regeln mit ihren IDs. Du kannst sofort eine Test-Mail mit dem passenden Betreff importieren und im Audit-Trail nachsehen, ob der Hold korrekt gesetzt wurde.

Die Regeln werden bei jeder Ingestion ausgewertet, bevor die Mail in den Storage wandert. Das verhindert, dass sensible Mails jemals ohne Hold gespeichert werden. Du kannst Regeln auch mit Zeitbedingungen kombinieren, zum Beispiel eine kürzere Retention für Spam-ähnliche Mails. Der Scheduler läuft einmal täglich um Mitternacht und bereinigt alles, was gelöscht werden darf. Du kannst den Zeitpunkt über die .env-Variable SCHEDULER_CLEANUP_HOUR anpassen.

💡 Du kannst Regeln auch nachträglich auf bereits archivierte Mails anwenden, indem du ./bin/archiver rule apply --rule-id XXX --source-id YYY ausführst. Das retroaktive Anwenden aktualisiert den Index und den Audit-Trail ohne Downtime.

Jede Policy und jeder Hold wird mit einem eindeutigen Hash gesichert, damit du bei einer Revision nachweisen kannst, dass keine Regel nachträglich manipuliert wurde. Die Web-Oberfläche zeigt dir eine grafische Übersicht aller aktiven Policies, Holds und Regeln mit farblicher Markierung für abgelaufene oder kritische Einträge. Du exportierst die komplette Konfiguration jederzeit als JSON für Backup-Zwecke.

Die automatisierte Regel-Engine unterstützt komplexe UND/ODER-Verknüpfungen und reguläre Ausdrücke. Ein Beispiel ist eine Regel, die Mails von externen Anwälten automatisch mit einer 15-Jahres-Retention versieht. Du testest neue Regeln zuerst im dry-run-Modus, damit du siehst, welche Mails betroffen wären, ohne dass etwas geändert wird. Der dry-run-Befehl gibt dir eine detaillierte Vorschau inklusive Anzahl der betroffenen Objekte.

⚠️ Vergiss nie, nach dem Anlegen einer neuen Policy den Scheduler manuell zu triggern, wenn du bestehende Mails sofort aktualisieren willst, sonst dauert es bis zum nächsten nächtlichen Lauf.

Der gesamte Mechanismus ist so ausgelegt, dass er GoBD-konform bleibt. Jede Löschung erzeugt einen unveränderlichen Audit-Eintrag mit dem Grund und der auslösenden Policy. Du kannst Holds nur mit einer zweiten Admin-Bestätigung aufheben, wenn du die Zwei-Faktor-Freigabe in der .env aktiviert hast. Das verhindert versehentliche Löschungen bei laufenden Holds.

❗ Ein typischer Fehler entsteht, wenn du eine globale Policy anlegst, ohne quellspezifische Ausnahmen zu definieren. Dann werden auch Test-Postfächer mit der langen Retention belastet und das Volume wächst unnötig schnell.

Du kannst Policies auch mit Tags kombinieren. Mails erhalten bei der Ingestion automatisch Tags und Regeln greifen auf diese Tags zu. Das macht die Verwaltung skalierbar, wenn du hunderte Quellen betreibst. Die Performance der Regel-Engine bleibt auch bei großen Datenmengen hoch, weil die Auswertung in Valkey zwischengespeichert wird und nur bei neuen Mails erneut ausgeführt wird.

Mit diesen drei Bausteinen – Retention-Policies, Legal Holds und automatisierten Regeln – hast du die volle Kontrolle über die Aufbewahrungsdauer und den Schutz sensibler Daten. Du kannst die Konfiguration später über die Web-Oberfläche verfeinern, aber die CLI bleibt der schnellste Weg für produktive Änderungen.

Sobald Retention-Policies, Legal Holds und automatisierte Regeln aktiv sind, kannst du mit der täglichen Nutzung und dem Monitoring fortfahren.

Betrieb, Überwachung und Administration

Suche, eDiscovery und tägliche Nutzung

Mit der täglichen Nutzung und Suche etabliert kannst du den produktiven Betrieb aufnehmen. Die Volltextsuche in Open Archiver basiert auf Meilisearch und liefert Ergebnisse in unter einer Sekunde, auch bei mehreren Millionen archivierten Mails. Du greifst über die Web-Oberfläche oder die REST-API darauf zu. In der Web-UI gibst du einfach einen Suchbegriff ein und erhältst sofort Treffer in Betreff, Body, Headern und extrahiertem Attachment-Text. Facettenfilter für Absender, Empfänger, Datum, Attachment-Typ oder Quelle lassen dich die Ergebnisse präzise eingrenzen.

Ein Klick auf eine Mail öffnet die vollständige .eml-Ansicht mit allen Headern, Anhängen und dem Original-Zeitstempel.

Für eDiscovery arbeitest du mit der erweiterten Suchsyntax. Du kombinierst Operatoren wie from:anwalt@extern.de, subject:vertraulich, after:2025-01-01 und attachment:pdf zu komplexen Queries. Die API-Endpunkte /api/search und /api/export erlauben dir, komplette Resultsets als EML-ZIP, PDF oder JSON zu exportieren. Jeder Export wird im Audit-Trail mit User, Query und Timestamp protokolliert. Du kannst während einer Suche einen Legal Hold direkt auf die Ergebnismenge anwenden, ohne jede Mail einzeln zu markieren. Der Hold gilt dann für alle Treffer und überschreibt bestehende Retention-Policies sofort.


Tägliche Suche & eDiscovery
├── Web-UI oder API Query
│   ├── Volltext + Facetten (Meilisearch)
│   ├── Filter: from, to, date, tag, source
│   └── Ergebnis → EML/PDF/JSON Export
│
├── Legal Hold on-the-fly
│   └── Audit-Eintrag + sofortige Sperre
│
└── Ergebnis in archiver-data + Index bleibt unverändert

Die tägliche Nutzung besteht meist aus schnellen Ad-hoc-Suchen. Du loggst dich als Admin ein und siehst auf dem Dashboard die letzten aktiven Quellen, den aktuellen Queue-Status und eine Übersicht über offene Holds. Die Suche ist case-insensitive und unterstützt Wildcards sowie Phrase-Suche in Anführungszeichen. Für Power-User gibt es die CLI-Variante ./bin/archiver search, die du in Skripten einsetzen kannst, um automatisierte Reports zu erzeugen.

🔧 Praktisches Beispiel:

Um eine typische eDiscovery-Anfrage für alle Mails eines bestimmten Absenders innerhalb eines Zeitraums zu bearbeiten und als EML-Paket zu exportieren, führst du folgenden Befehl im Container aus:


docker compose exec open-archiver ./bin/archiver search \
  --query 'from:extern@anwalt.de after:2024-01-01 before:2025-12-31' \
  --facets 'source,attachment-type' \
  --limit 5000 \
  --export eml \
  --output /export/anwalt-fall-2025.zip \
  --hold "Legal-Hold-2025-XY"

Danach prüfst du den Export mit:


ls -lh /export/anwalt-fall-2025.zip
docker compose exec open-archiver ./bin/archiver hold list

Der Befehl legt automatisch den Hold an und du siehst den neuen Eintrag im Audit-Log. Der Export ist sofort verfügbar und enthält alle originalen .eml-Dateien mit unveränderten Hashes.

Die Web-Oberfläche bietet zusätzlich eine Thread-Ansicht, bei der zusammengehörige Mails automatisch gruppiert werden. Du kannst Threads markieren, Tags vergeben oder einzelne Nachrichten für den Export auswählen. Für große eDiscovery-Fälle aktivierst du den Bulk-Export-Modus, der die Ergebnisse in mehrere ZIP-Dateien aufteilt, damit du sie ohne Größenbeschränkung herunterladen kannst. Die API erlaubt dir, Suchen zu speichern und als „Saved Search“ wiederzuverwenden – praktisch, wenn du regelmäßig dieselben Compliance-Checks durchführst.

Jede Suche und jeder Export wird mit dem exakten Query-String und der Anzahl der Treffer im Audit-Trail festgehalten. Das ermöglicht dir später den Nachweis gegenüber Auditoren, dass keine Daten unkontrolliert exportiert wurden. Du kannst die Suche auch auf bestimmte Quellen oder nur auf Mails mit Legal Hold einschränken. Die Facetten werden in Echtzeit berechnet, sodass du siehst, wie viele Treffer zu welchem Absender oder Attachment-Typ gehören, bevor du den Filter anwendest.

💡 Du kannst die CLI-Suche mit JSON-Output kombinieren und die Ergebnisse direkt in ein eigenes Reporting-Tool oder ein SIEM-System weiterleiten, ohne die Web-UI zu benutzen.

Die tägliche Nutzung umfasst auch das Anwenden von Tags und das manuelle Setzen von Holds während der Suche. Ein Rechtsabteilung-Mitarbeiter kann eine Suche starten, die Ergebnisse sichten und mit einem Klick den gesamten Thread unter Legal Hold stellen. Der Hold wird sofort wirksam und verhindert jede künftige Löschung. Der Scheduler respektiert das und überspringt betroffene Objekte bei der Cleanup-Routine.

Die Performance der Suche bleibt auch bei zehn Millionen Mails hoch, weil Meilisearch den Index in RAM hält und nur geänderte Dokumente neu indiziert. Du beobachtest die Index-Größe und die Query-Latenz über den Meilisearch-Stats-Endpoint.

In der Praxis reicht ein einziger Suchbefehl aus, um eine komplette eDiscovery-Anfrage in wenigen Sekunden zu beantworten.

⚠️ Achte darauf, dass du bei sensiblen eDiscovery-Suchen immer den Export-Hash prüfst, bevor du die Datei weitergibst, damit du später nachweisen kannst, dass keine Datei während des Exports verändert wurde.

Die Web-UI erlaubt dir außerdem, gespeicherte Suchen mit Kollegen zu teilen, ohne dass diese volle Admin-Rechte brauchen. Du definierst View-Only-Rollen und gibst nur Zugriff auf bestimmte Saved Searches frei. Das vereinfacht die Zusammenarbeit mit der Rechts- oder Compliance-Abteilung erheblich.

❗ Ein typischer Fehler entsteht, wenn du eine sehr breite Suche ohne Facettenfilter startest. Dann werden Millionen von Treffern zurückgegeben und der Browser oder die API kann überlastet werden.

Du kannst die Suche auch über die API in eigenen Skripten einbinden, um automatische Benachrichtigungen zu versenden, sobald neue Mails mit bestimmten Stichwörtern eingehen. Der tägliche Betrieb besteht dann aus kurzen Suchen, schnellen Exports und gelegentlichen Hold-Anpassungen. Die Oberfläche ist so gestaltet, dass auch Nicht-Techniker schnell zurechtkommen, während Admins über die CLI die volle Kontrolle behalten.

Monitoring, Logging, Updates, Skalierung

Mit der Suche und eDiscovery etabliert kannst du zum Monitoring und Logging übergehen. Du überwachst den gesamten Stack kontinuierlich, damit Ingestion, Indizierung und Cleanup ohne Unterbrechung laufen. Docker Stats liefert dir die Basiswerte für CPU, Memory und Netzwerk jedes Containers. Für produktiven Betrieb integrierst du Prometheus und Grafana, um Metriken langfristig zu speichern und Alerts bei Schwellwerten zu setzen. Der open-archiver Container exponiert einen /metrics-Endpoint, den du direkt in Prometheus scrape-st. Du konfigurierst den Scrape-Job mit dem internen Docker-Netzwerk und siehst CPU-Last der Worker, Queue-Länge in Valkey und Index-Größe von Meilisearch. PostgreSQL liefert über den exporter Metriken zu aktiven Verbindungen und WAL-Wachstum.


Monitoring-Stack
├── docker stats (kurzfristig)
├── Prometheus + Grafana (langfristig)
│   ├── open-archiver /metrics
│   ├── postgres_exporter
│   ├── meilisearch stats
│   └── valkey info
├── Alerts bei >80% RAM oder Queue >1000
└── Audit-Trail als zweite Ebene

Logging zentralisierst du am besten mit einem dedizierten Logging-Driver.

Du änderst in der docker-compose.yml den log-driver auf json-file mit Rotation oder leitest alles an Loki weiter. Die Logs des open-archiver Containers enthalten jeden Ingestion-Schritt, jeden Hash und jede Policy-Anwendung. Du filterst mit docker compose logs open-archiver --tail=500 | grep -E 'ingest|error|hold' und siehst sofort, ob ein Import hängt. Für langfristige Analyse speicherst du die Logs in einem separaten Volume oder schickst sie an einen ELK-Stack. Der Audit-Trail in PostgreSQL bleibt immer die autoritative Quelle für Compliance, während die Container-Logs dir operative Details liefern.

Updates führst du kontrolliert durch.

Du ziehst neue Images mit docker compose pull und startest einen Rolling-Restart. Der Stack bleibt dabei erreichbar, weil die Services nacheinander neu gestartet werden. Du prüfst vorher die Release-Notes im Repository auf Breaking Changes bei Meilisearch oder PostgreSQL. Ein Update des open-archiver Containers triggert automatisch eine Migration, falls neue Tabellen oder Index-Felder nötig sind. Du führst das Update immer in einem Wartungsfenster aus und testest anschließend die Suche und den ersten Sync.

Skalierung beginnt horizontal bei den Worker-Prozessen.

Du erhöhst die Anzahl der Worker-Threads über die .env-Variable WORKER_COUNT und restartest den Service. Bei sehr großen Installationen verteilst du mehrere open-archiver Instanzen hinter einem Load-Balancer und teilst die Volumes über NFS oder Ceph. Meilisearch skaliert nicht nativ horizontal, daher bleibt es in der aktuellen Version ein Single-Instance-Service mit ausreichend RAM. Valkey und PostgreSQL kannst du mit Replicas ergänzen, wenn die Schreiblast steigt. Du beobachtest die Skalierungseffekte über Prometheus und passt die Resource-Limits dynamisch an.

🔧 Praktisches Beispiel:

Um das Monitoring sofort einzurichten und die ersten Metriken zu prüfen, legst du eine prometheus.yml an und startest einen Prometheus-Container im gleichen Compose-File. Danach führst du folgende Befehle aus:


docker compose up -d prometheus grafana
curl -s http://localhost:9090/api/v1/query?query=container_cpu_usage_seconds_total{container="open-archiver"}

Du öffnest Grafana unter dem Proxy und importierst ein Dashboard für Docker-Container. Der Dashboard zeigt dir die Queue-Länge, Index-Größe und aktuelle Ingestion-Rate. Du setzt einen Alert, der bei Queue-Länge über 5000 eine E-Mail oder Slack-Nachricht sendet.

Der gesamte Aufbau dauert unter zehn Minuten und gibt dir sofort Transparenz.

Die Logging-Konfiguration erweiterst du um Log-Rotation, damit die json-log-Dateien nicht das Host-Dateisystem füllen. Du setzt in der Compose-Datei unter dem Service logging: driver: "json-file" und options: max-size: "10m" max-file: "5". Damit bleiben die letzten fünf Dateien mit je 10 MB erhalten. Für zentrale Analyse fügst du einen Loki-Container hinzu und konfigurierst den Docker-Log-Driver entsprechend. Du kannst dann in Grafana Log-Queries mit Labels wie container=open-archiver und level=error ausführen.

Updates testest du zuerst in einer Staging-Umgebung mit identischer .env und Volumes. Du pullst die neuen Images, führst docker compose up -d aus und beobachtest, ob die Migration ohne Fehler durchläuft. Der Audit-Trail zeigt dir danach den Update-Eintrag mit Version und Timestamp. Bei Skalierung beginnst du mit Worker-Erhöhung und prüfst, ob die Ingestion-Rate linear steigt. Du monitorst die IOPS auf dem archiver-data Volume, denn mehr Worker bedeuten mehr gleichzeitige Schreibvorgänge.

💡 Du kannst Prometheus mit Blackbox-Exporter ergänzen, um den Health-Endpoint des Proxys und die API-Verfügbarkeit extern zu überwachen. Das rundet das Monitoring ab und zeigt dir Ausfälle, bevor Nutzer sie melden.

Die tägliche Routine besteht aus einem Blick ins Grafana-Dashboard am Morgen, dem Prüfen der Queue-Länge und dem Review der letzten Audit-Einträge. Du exportierst die Metriken wöchentlich und analysierst Trends bei Speicherwachstum und Query-Latenz. Skalierung auf mehrere Hosts erfordert später eine verteilte Dateisystem-Lösung, doch für die meisten Umgebungen reicht ein einzelner, gut dimensionierter Server mit erhöhten Worker-Zahlen aus.

⚠️ Starte kein Update, ohne vorher ein Volume-Snapshot zu machen, weil eine fehlgeschlagene Migration die Datenbank in einen inkonsistenten Zustand bringen kann.

Du integrierst das Monitoring in dein bestehendes System, egal ob Zabbix, Prometheus oder eine kommerzielle Lösung. Die Container-Metriken sind standardisiert und lassen sich leicht einbinden. Logging und Monitoring zusammen geben dir die nötige Transparenz für den revisionssicheren Betrieb. Du siehst jede Verzögerung bei der Ingestion und kannst reagieren, bevor der Queue überläuft. Updates werden zur Routine und Skalierung bleibt planbar.

❗ Ein typischer Fehler entsteht, wenn du die Worker-Anzahl zu hoch setzt und die IOPS auf dem archiver-data Volume nicht mehr ausreichen. Dann steigt die Latenz und der gesamte Stack wird langsam.

Die Kombination aus Monitoring, Logging, kontrollierten Updates und schrittweiser Skalierung hält den Archiver stabil und nachvollziehbar. Du dokumentierst jede Änderung im Audit-Trail und hast jederzeit die Möglichkeit, auf ältere Versionen zurückzurollen. Mit diesen Werkzeugen betreibst du das System langfristig ohne Überraschungen und erfüllst alle Anforderungen an Verfügbarkeit und Nachweisbarkeit.

Troubleshooting

Häufige Fallstricke und Wartungspraxis

Mit der Suche und eDiscovery etabliert kannst du zum Troubleshooting übergehen. Die meisten operativen Probleme in Open Archiver treten bei hoher Last oder nach längerer Laufzeit auf und lassen sich durch eine strukturierte Diagnose in wenigen Minuten eingrenzen. Du beginnst immer mit dem Container-Status, weil ein nicht gesunder Service die gesamte Pipeline blockieren kann. Der Befehl docker compose ps zeigt dir auf einen Blick, ob alle vier Services laufen und die Healthchecks bestanden haben. Danach holst du die aktuellen Logs des betroffenen Containers und filterst gezielt nach Fehlermeldungen oder Warnungen.

Die Ingestion-Pipeline ist der häufigste Ausgangspunkt für Störungen.

Wenn neue Mails nicht verarbeitet werden, prüfst du zuerst die Valkey-Queue-Länge. Ein stark gefüllter Queue deutet auf überlastete Worker oder ein volles archiver-data Volume hin. Du rufst den internen Status-Endpunkt auf und siehst die Anzahl offener Jobs. Gleichzeitig überwachst du die IOPS und den freien Platz auf dem Volume. Ein häufiges Symptom ist eine langsam steigende Queue bei gleichzeitig hoher CPU-Last im open-archiver Container.

Die Hash-Verifizierung ist ein weiterer kritischer Punkt.

Wenn der Worker eine Mail nicht speichern kann, weil der berechnete SHA-256-Hash nicht mit dem gespeicherten Wert übereinstimmt, bricht der Job ab und der Audit-Trail enthält einen entsprechenden Eintrag. Du suchst in den Logs nach dem String „hash mismatch“ und findest die betroffene Message-ID. In den meisten Fällen liegt das Problem an einem defekten Volume oder einer Unterbrechung während des Schreibvorgangs.

Du löst es, indem du das Volume auf Konsistenz prüfst und den betroffenen Job manuell neu startest.

Bei Meilisearch-Problemen siehst du in der Web-Oberfläche plötzlich leere Suchergebnisse oder extrem hohe Latenz. Der Index kann korrupt werden, wenn der Container abrupt beendet wurde oder der Speicher nicht ausreichte. Du prüfst den Index-Status über die Meilisearch-Admin-API und vergleichst die Dokumentenzahl mit der Anzahl in PostgreSQL. Ein Rebuild des Indexes ist dann die saubere Lösung. Der Befehl dazu ist im Container verfügbar und läuft im Hintergrund, ohne dass der Rest des Systems gestoppt werden muss.

PostgreSQL-spezifische Fallstricke zeigen sich meist durch langsame Queries oder wachsende WAL-Dateien. Du verbindest dich mit psql und prüfst die aktiven Verbindungen sowie den Vacuum-Status. Ein fehlender Autovacuum kann bei großen Audit-Tabellen zu Performance-Einbußen führen. Du führst manuell einen Vacuum durch und aktivierst die Autovacuum-Parameter in der postgresql.conf, falls nötig.


Troubleshooting-Flow
├── docker compose ps → Container healthy?
│
├── Logs filtern (error, hash, queue)
│
├── Volume-Usage prüfen (df -h /mnt/archiver-data)
│
├── Meilisearch stats → Index-Konsistenz
│
├── PostgreSQL: connections + vacuum
│
└── CLI: source status / policy status

Wartungspraxis beginnt mit regelmäßigen Volume-Snapshots.

Du erstellst tägliche ZFS-Snapshots des gesamten archiver-data Pools und speicherst sie offsite. Das gibt dir die Möglichkeit, einen beliebigen Zeitpunkt wiederherzustellen, ohne dass der Stack offline gehen muss. Zusätzlich führst du wöchentliche Konsistenzchecks durch, bei denen du eine Stichprobe von Hashes aus der Datenbank mit den Dateien auf dem Volume vergleichst.

Der CLI-Befehl dafür ist schnell und liefert eine detaillierte Liste abweichender Objekte.

Updates und Wartungsfenster planst du so, dass du zuerst ein Volume-Snapshot anlegst und dann den Stack mit docker compose pull und docker compose up -d aktualisierst. Nach dem Update prüfst du die Migration-Logs und testest eine manuelle Suche sowie einen kleinen Ingestion-Job. Skalierungsprobleme erkennst du frühzeitig an steigenden Queue-Längen und hohen I/O-Wartezeiten. Du erhöhst dann schrittweise die Worker-Anzahl und beobachtest die Auswirkung auf die Metriken.

🔧 Praktisches Beispiel:

Ein typischer Fall ist ein plötzlich voller archiver-data Volume bei laufender Ingestion. Du startest die Diagnose mit folgenden Befehlen. Zuerst den aktuellen Status:


docker compose ps
df -h /mnt/archiver-data
docker compose logs open-archiver --tail=100 | grep -E 'full|disk|quota'

Danach prüfst du die Queue und startest eine manuelle Cleanup:


docker compose exec open-archiver ./bin/archiver queue status
docker compose exec open-archiver ./bin/archiver maintenance cleanup --force

Du siehst sofort die gelöschten Objekte und die freigewordenen Bytes.

Anschließend erstellst du ein Snapshot:


zfs snapshot tank/archiver-data@emergency-$(date +%Y%m%d_%H%M)

Der gesamte Vorgang dauert unter fünf Minuten und stellt den Betrieb wieder her, ohne Datenverlust. Du kannst dieses Skript später automatisieren und bei 85 Prozent Füllstand automatisch auslösen.

Ein weiterer häufiger Fallstrick ist eine fehlgeschlagene Meilisearch-Indizierung nach einem Update.

Der Index wird dann inkonsistent und Suchen liefern unvollständige Ergebnisse. Du prüfst den Index-Status und startest einen gezielten Rebuild nur für die betroffenen Quellen. Der Befehl erlaubt eine selektive Neuindizierung, sodass der Rest des Systems weiterläuft.

Wartungspraxis umfasst auch die regelmäßige Überprüfung der Retention-Policies.

Du listest alle Policies und prüfst, ob abgelaufene Mails tatsächlich gelöscht wurden. Der Scheduler-Log zeigt dir, welche Objekte bereinigt wurden und warum. Du führst monatlich einen vollständigen Policy-Consistency-Check durch, bei dem du die Datenbank mit dem Dateisystem abgleichst. Das verhindert, dass verwaiste Dateien oder fehlende Hashes übersehen werden.

Bei Skalierungsproblemen auf mehreren Hosts kommt NFS oder Ceph ins Spiel.

Du prüfst die Mount-Optionen und die Latenz der verteilten Volumes. Zu hohe Latenz führt zu Worker-Timeouts und abgebrochenen Jobs. Du optimierst die NFS-Parameter oder wechselst auf ein schnelleres Protokoll. Die Wartung der Datenbank umfasst regelmäßige Vacuum-Analyze-Läufe und das Überwachen der Index-Fragmentierung.

Die CLI bietet dir für fast jedes Problem einen dedizierten Status-Befehl. Du kannst den gesamten Systemzustand mit einem einzigen Kommando abfragen und erhältst eine Übersicht über Quellen, Policies, Holds, Queue und Index. Das ist besonders wertvoll, wenn du nachts oder außerhalb der regulären Arbeitszeit gerufen wirst.

💡 Du kannst alle CLI-Befehle mit dem Parameter --json aufrufen und die Ausgabe direkt in ein eigenes Monitoring-Skript oder ein externes Tool weiterverarbeiten. Das macht die Wartung skalierbar und automatisierbar.

Die meisten Probleme lassen sich durch konsequentes Logging und regelmäßige Wartung vermeiden. Du legst dir eine Checkliste für den monatlichen Wartungslauf an, die Volume-Snapshots, Policy-Checks, Index-Health und Datenbank-Vacuum enthält. Damit bleibt das System auch bei mehreren Terabyte Daten stabil und revisionssicher.

❗ Ein typischer Fehler entsteht, wenn du ein Volume-Snapshot ohne vorherigen Stopp des Stacks erstellst. Dann kann der Snapshot inkonsistente Dateien enthalten und ein Restore führt zu Hash-Mismatches.

Du dokumentierst jeden Troubleshooting-Fall im Audit-Trail oder in einem separaten Ticket-System, sodass du später Muster erkennst und präventiv eingreifen kannst. Die Wartungspraxis wird mit der Zeit zur Routine und du brauchst nur noch wenige Minuten pro Woche, um den Archiver stabil zu halten.

Ressourcen für Weiterbildung

Zur Vertiefung der in diesem Leitfaden behandelten Themen und für aktuelle Updates zu Open Archiver haben wir die wichtigsten Ressourcen zusammengestellt.

Offizielles GitHub-Repository Docker Hub Projekt-Dokumentation Release Notes & Changelog GoBD-konforme Archivierung

Fazit

Du hast jetzt den kompletten Weg von der Architektur über die Infrastrukturvorbereitung, die Installation, die Einbindung aller E-Mail-Quellen bis hin zum produktiven Betrieb und dem Troubleshooting durchlaufen. Open Archiver liefert dir eine vollwertige, revisionssichere E-Mail-Archivierungslösung, die du vollständig in deiner eigenen Infrastruktur betreibst – ohne Vendor-Lock-in, ohne monatliche Kosten und mit voller Kontrolle über Daten und Compliance.

Die Kombination aus Docker-basiertem Stack, Meilisearch-Volltextsuche, PostgreSQL-Audit-Trail und flexiblen Retention-Policies sowie Legal Holds erfüllt die Anforderungen der GoBD und ermöglicht echte eDiscovery auf Enterprise-Niveau. Gleichzeitig bleibt das System überschaubar und wartbar, solange du die Volumes, das Monitoring und die regelmäßigen Snapshots konsequent pflegst.

Was du nun in der Hand hast, ist kein reines Tool, sondern ein strategischer Baustein deiner Mail- und Compliance-Infrastruktur. Du bestimmst selbst, wie lange Daten aufbewahrt werden, wer Zugriff erhält und wie schnell du auf Anfragen von Rechtsabteilung oder Betriebsprüfung reagieren kannst. Setze den Archiver produktiv ein, integriere ihn in deine bestehenden Backup- und Monitoring-Prozesse und passe ihn schrittweise an dein Wachstum an. Die Investition in eine saubere, dokumentierte Installation zahlt sich langfristig aus – in Form von Sicherheit, Nachweisbarkeit und echter Datenhoheit.

Ähnliche Beiträge