# Dokumentation app-backup v2 **Aktualisiert am 21. September 2026:** Die Platzbereinigung schützt mit `SPACE_KEEP_LOCAL=1` mindestens eine vollständig geprüfte, als hochgeladen markierte lokale Sicherung; reguläre Retention und Speicherreserve bleiben separat bestehen. Manifest und Abschlussmarker werden vor lokaler Entfernung bytegenau remote verglichen. Siehe [Speicherstrategie und Fehlerbehebung](SPEICHER.md). Seit 16. September 2026: Backup, Platzbereinigung und Update-Abnahme prüfen remote nur Dateiexistenz und Größen; Archive werden erst beim monatlichen Restore-Test vollständig geladen. Der Abschlussmarker wird weiterhin bytegenau bestätigt. Die neue Option `--enable-ai-rem` nimmt auf Gemini die Daten und Konfiguration samt Writer-Stopps wieder auf. Installation auf Gemini steht aus. Aktuelle Befehle: [Update-Anleitung](UPDATE_ANLEITUNG.md). Stand des Reviews: 10. September 2026. Untersucht wurde der Gitea-Stand `099688e` einschließlich Backup, Restore, Installer, Konfiguration, systemd und bisheriger README. Die Erweiterung wird als zusammengehöriges Paket installiert; einzelne Skripte dürfen nicht ohne die gemeinsamen Bibliotheken aktualisiert werden. ## Ergebnis der Codeanalyse | Befund im Ausgangsstand | Auswirkung | Umsetzung | | --- | --- | --- | | **Kritisch:** `app-restore.sh` enthielt nur Initialisierung/Hilfsfunktionen und griff am Ende auf das undefinierte `RUN_DIR` zu. | Die dokumentierten Restore-Aufrufe waren nicht implementiert. | Vollständige Prüfung, Entpacken, Restore-Plan und explizite Ausführung. | | **Kritisch:** Dumps/Kopien wurden teilweise in `&& … || true` bzw. Warnungszweigen ausgeführt. | Ein Lauf konnte trotz fehlender DB oder Dateien erfolgreich erscheinen; Bash-Fehlerbehandlung wurde in solchen Kontexten unterdrückt. | Direkte Aufrufe, `set -Eeuo pipefail`, `inherit_errexit`; Fehlercodes werden weitergegeben. | | **Kritisch:** Nextcloud-Wartungsmodus wurde nach dem DB-Dump vor der Dateikopie ausgeschaltet. WordPress blieb live. | Datenbank und Dateien konnten unterschiedliche Zustände abbilden. | Gemeinsames Ruhefenster über sämtliche Dumps und Dateikopien. | | **Hoch:** Maildienste hatten keine zuverlässige Wiederanlaufbehandlung; Fehler und rsync-Teilergebnisse wurden ignoriert. | Unvollständige Mailsicherung oder unbeabsichtigte Dienstausfälle. | Zustandsabhängige Stopps, geprüfter Wiederanlauf, Exit-Code und Wiederanlaufdatei. | | **Hoch:** Crypt-Prüfung ließ u. a. `no_data_encryption=true` und reine Namensverschleierung zu. Restore verlangte dagegen explizite Optionen, obwohl sichere Defaults gültig sind. | Inhalt konnte trotz Remote-Typ `crypt` unverschlüsselt bleiben; gültige Remotes konnten abgelehnt werden. | Gemeinsame Prüfung von Typ, Inhalt, Dateinamen und Verzeichnisnamen; Defaults werden berücksichtigt. | | **Hoch:** `--checksum` und Remote-Listing ersetzten keine Prüfung des entschlüsselten Inhalts. | Transfererfolg war kein belastbarer Rücklesetest. | Lokale Inhaltsprüfung; seit 16.09. Remote-Datei-/Größenprüfung vor dem Marker, vollständige Remote-Inhaltsprüfung beim Monats-Restore. | | **Hoch:** Retention lief vor dem neuen Backup und löschte remote einzelne alte Objekte rekursiv. | Alte Wiederherstellungspunkte konnten verloren gehen, bevor ein neuer verfügbar war; Sätze konnten unvollständig werden. | Reguläre Retention nach einem neuen erfolgreichen Lauf; zusätzliche lokale Platzbereinigung nur mit erneuter Remote-Datei-/Größenprüfung und Mindestanzahl. | | **Hoch:** Drei nicht als Vorlage gekennzeichnete DB-Dateien enthielten mögliche echte Zugangsdaten. | Geheimnisse im aktuellen Baum und in der Git-Historie. | Dateien entfernt, ausschließlich Vorlagen, `.gitignore`; Passwortrotation bleibt Betriebsaufgabe. Keine Historienumschreibung. | | **Hoch:** Gitea war im ausgelieferten Config-Beispiel nicht eingeschaltet; `APP_DATA_PATH` allein deckt externe Repositories/custom nicht ab. | Erwartete Gitea-Daten konnten fehlen. | Sichtbarer Aktivierungsschalter und zusätzliche Pfade; erforderliche Verzeichnisse fehlen nicht stillschweigend. | | **Mittel:** Konfigurierbare WordPress-Excludes wurden nicht vollständig angewendet; Nextcloud `data/` war fest angenommen. | Falscher Umfang bei abweichender Verzeichnisstruktur. | Ableitung relativer Excludes aus den konfigurierten Pfaden, auch beim Restore. | | **Mittel:** Unkomprimierte Zwischenarchive, zstd `-19 -T0`, flache Ablage und fehlendes Manifest. | Hohe IO-/CPU-/Speicherlast; keine sichere Zuordnung vollständiger Läufe. | Streaming-Kompression, begrenzte Threads, eigene Laufordner und strukturierte Metadaten. | | **Mittel:** Installer aktivierte den Timer sofort und überschrieb Unit-Dateien ohne Rückweg. | Ungeprüfte Erstläufe; individuelle Zeitpläne konnten verloren gehen. | Config-/Timer-Erhalt, `.dist`-Vorlagen, gemeinsame Sperre, Rollback-Paket, frische Timer zunächst aus. | | **Mittel:** Eigene Logdatei je Lauf plus `logrotate`/`notifempty`. | Rotierte Dateien konnten wegen leerer Nachfolgedateien dauerhaft liegen bleiben. | Altersbasierte Bereinigung neuer Laufprotokolle nach erfolgreichen Backups. | Die vollständige Rückleseprüfung findet seit 16.09.2026 beim monatlichen Restore-Test statt. Tägliche Backups und Update-Abnahmen laden keine Archive zurück. Gleich große Remote-Beschädigungen können deshalb erst beim Restore erkannt werden. ## Sicherheitsziel und Grenzen Das Paket soll Fehler sichtbar machen, vollständige Sicherungssätze erhalten und eine nachprüfbare Wiederherstellung ermöglichen. Es kann keine absolute Datensicherheit garantieren. Folgende Ebenen sind getrennt zu betrachten: 1. **Vertraulichkeit:** Das Crypt-Remote verschlüsselt Inhalt und Namen vor dem Upload. Lokales Staging, lokale Archive und entpackte Restores sind Klartext und benötigen ein verschlüsseltes Servervolume, sichere Dateirechte und Zugriffsschutz. 2. **Integrität:** SHA-256, Kompressions-/Tar-Prüfung, Manifest und monatliche Rückleseprüfung erkennen fehlende bzw. veränderte Bestandteile. Das Manifest ist nicht digital signiert. Wer Archive und Manifest gemeinsam kontrolliert, kann neue passende Prüfsummen erzeugen. 3. **Konsistenz:** Alle relevanten Writer müssen während der Dumps/Kopien ruhen. Nicht erfasste Cronjobs, externe DB-Schreiber, systemd-Sockets, Container, S3-Speicher oder Remote-Mounts werden nicht automatisch eingefroren. Ein erfolgreicher Dateivergleich beweist keine Anwendungskonsistenz. 4. **Verfügbarkeit:** OneDrive mit demselben löschberechtigten Konto ist keine unabhängige, unveränderliche Kopie. Für Schutz vor kompromittiertem Server/Konto, Ransomware oder versehentlicher Löschung eine weitere getrennte bzw. offline/immutable Sicherung vorsehen. 5. **Wiederherstellbarkeit:** Ein getesteter Restore mit Anwendungskontrollen ist erforderlich. Passende Anwendungsversionen, Systempakete, UID/GID, Datenbankrechte und außerhalb des Umfangs liegende Schlüssel/Konfigurationen müssen verfügbar sein. Crypt verschlüsselt Daten beim Zugriff über das Crypt-Remote; der darunterliegende OneDrive-Zugriff umgeht diese Schicht. Rclone speichert Crypt-Passwörter standardmäßig nur verschleiert in seiner Konfiguration. Passwort **und gegebenenfalls Salt/zweites Passwort** müssen unabhängig vom gesicherten Server verfügbar sein. Nicht allein innerhalb desselben verschlüsselten Backups aufbewahren. Dateigrößen und Zeitinformationen sind nicht vollständig verborgen. Quelle: [rclone Crypt](https://rclone.org/crypt/). ## Architektur und Ablauf ```mermaid flowchart TD A[Config und gemeinsame Sperre] --> B[Quellen / Rechte / DB-Verbindungen / Crypt / Speicher prüfen] B --> C[Wartungsmodus und aktive Writer stoppen] C --> D[Datenbanken dumpen und Dateien ins private Staging kopieren] D --> E[Ursprünglichen Dienstzustand wiederherstellen] E --> F[Je Komponente komprimieren und Tar-Inhalt prüfen] F --> G[Manifest und lokaler COMPLETE-Marker] G --> H[Archive und Manifest über Crypt hochladen] H --> I[Remote-Dateiexistenz und Größen prüfen] I --> J[COMPLETE zuletzt hochladen und zurücklesen] J --> K[Retention vollständiger Sätze / Status / Bericht] ``` `ENABLE_UPLOAD=false` beendet den Datenpfad nach dem vollständigen lokalen Satz und führt nur lokale Retention aus. Dies ist kein Offsite-Backup. Upload und Remote-Prüfung melden ihre Phase ausdrücklich. Mit `RCLONE_STATS_INTERVAL="1m"`, `--stats-one-line` und `--stats-log-level NOTICE` erscheinen periodische rclone-Statistiken auch ohne ausführliches Debug-Logging. Sie gehen nach stderr; Marker und Dateilisten auf stdout bleiben maschinell auswertbar. Alte Konfigurationen erhalten den Intervallstandard automatisch. Eine Anpassung wirkt erst beim nächsten rclone-Prozess. Die bestehende I/O-Leerlauffrist `RCLONE_TIMEOUT="1h"` ist keine Gesamtlaufzeitbegrenzung. Für Remote-Dateiprüfungen und monatliche Downloads gelten eigene kürzere Leerlauffristen, eine Prozessfrist je Versuch und mehrere vollständige Versuche. Dort zeigen mehrzeilige Statistiken aktive Prüfungen und ein Ergebnis pro Datei den tatsächlichen Fortschritt. Details: [Rückleseprüfung](REMOTE_READBACK.md). Beim Betreiberlauf am 10.09.2026 ab 15:30 Uhr wurden die geschützte Kopierphase, Dienstwiederaufnahme und ein vollständiger lokaler Satz bestätigt. Danach meldete rclone 1.74.3 HTTP 409 `resourceModified` bei einem OneDrive-Objekt. Der zweite Kopierversuch war um 15:58:03 erfolgreich; die Rückleseprüfung bestätigte um 15:59:37 alle 15 Dateien ohne Unterschiede. Anschließend bestand die separate Remote-Restore-Prüfung für 14 Archive; der Installer meldete `SUCCESS` und aktivierte den Backup-Timer. Der konkrete Auslöser des vorübergehenden Konflikts bleibt unbekannt. Die Unterscheidung zwischen HTTP-Wiederholungen und Wiederholung des gesamten Kopierauftrags sowie die Diagnose eines stillen Journals stehen in der [Update-Anleitung](UPDATE_ANLEITUNG.md#onedrive-http-409-resourcemodified-und-stilles-journal). Dateikopien erhalten mit `rsync -aHAX --numeric-ids` Eigentümer, Modus, Symlinks, Hardlinks innerhalb einer Komponente, ACLs und Extended Attributes. Tar erhält diese Metadaten einschließlich SELinux. Plattform-/Dateisystemunterstützung ist Voraussetzung. Externe Symlink-Ziele werden nicht automatisch gesichert; absolute Symlinks als einzelne Einträge bleiben erhalten. Geräte, FIFOs und andere Spezialdateien werden von der Archivprüfung abgelehnt. Zusätzliche Pfade sind deshalb Datenverzeichnisse, kein Ersatz für ein vollständiges Systemimage. Vor jedem Backup und bei `--check` prüft rsync das Auflisten der Quellwurzel sowie das Übertragen ihrer Verzeichnisattribute in einen privaten temporären Prüfbereich unter `STAGING_ROOT`. Alle Kindobjekte sind von der Attributprobe ausgeschlossen; Datei-Inhalte werden nicht kopiert. Die Probe wird danach entfernt. Das erkennt beispielsweise eine SELinux-Ablehnung von `opendir` oder `relabelfrom` vor dem Wartungsmodus. Es belegt nicht den Zugriff auf jede untergeordnete Datei oder jedes dort abweichende Attribut. Der echte Kopierlauf bleibt strikt fehlergeprüft. Die rsync-Kindprozesse erhalten keine geerbten Update-/Backup-Sperrdeskriptoren; der übergeordnete Prozess hält seine Sperren unverändert weiter. Backup-Dienst und abgekoppelter Update-Auftrag starten über `/bin/bash` mit `SELinuxContext=system_u:system_r:unconfined_service_t:s0`. Das erlaubt dem privilegierten Backup, unterschiedliche Web-, Mail- und Home-Labels zu lesen und Sicherheitsattribute zu erhalten. **Diese Prozesse und ihre Kindprozesse sind damit bewusst nicht durch SELinux eingeschränkt**; die übrigen Dienste bleiben ihrer bestehenden Policy unterworfen. Das ist keine eigens eingeschränkte Backup-Policy. Es werden keine globalen rsync-Booleans, Quell-Labels oder Dateizugriffsrechte geändert. Ein verweigerter Kontext wird nicht mit dem systemd-Präfix `-` übergangen. Bei deaktiviertem SELinux ignoriert systemd die Kontexteinstellung. Auf Gemini wurde am 10.09.2026 unter Enforcing die Startkette Bash → rsync mit diesem Kontext und erfolgreichem Auflisten von `/var/www/html` bestätigt. Der direkte Start von rsync als Unit-Einstiegspunkt scheiterte dagegen mit 203/EXEC; deshalb verwenden beide Units ausdrücklich Bash. Dieser Lesetest bestätigt noch keine vollständige Attributübertragung oder Sicherung. Referenzen: [systemd SELinuxContext](https://github.com/systemd/systemd/blob/v256/man/systemd.exec.xml), [Fedora-SELinux-Domain und Einstiegspunkte](https://github.com/fedora-selinux/selinux-policy/blob/rawhide/policy/modules/system/unconfined.te). Die Kompression schreibt direkt aus tar in `.partial`-Dateien. Erst erfolgreiche Pipelines werden umbenannt. zstd nutzt standardmäßig Stufe 6 und zwei Threads. `sync -f` vor Fortsetzung bzw. Retention fordert die Persistierung des lokalen Satzes an; Hardware-/Storagefehler bleiben eine eigene Ebene. ## Format und Komponenten Ein neuer Lauf heißt beispielsweise `appbackup_2026-09-10_02-30-00_a1b2c3d4`. Zeitangaben im Namen sind UTC; der systemd-Zeitplan nutzt die Serverzeitzone. Der Zufallssuffix verhindert Namenskollisionen. ```text /var/backups/app-backup/archives/appbackup_.../ meta.tar.zst db-wordpress.tar.zst db-nextcloud.tar.zst wordpress.tar.zst nextcloud.tar.zst nextcloud-data.tar.zst ... manifest.json COMPLETE UPLOADED # nur lokal, nach erfolgreicher Remote-Datei-/Größenprüfung und Markerbestätigung ``` | Komponente | Inhalt / Ziel aus lokaler Config | | --- | --- | | `meta` | Laufkennung, Format und Liste der Quellen; keine vollständige Config-Datei | | `db-wordpress`, `db-nextcloud`, `db-gitea` | Ein SQL-Dump je aktivierter Anwendung | | `wordpress` | `WP_DIR`, ohne konfigurierte untergeordnete Nextcloud-Pfade und `WP_EXCLUDES` | | `nextcloud` | `NC_DIR`, ohne konfiguriertes untergeordnetes `NC_DATA_DIR` | | `nextcloud-data` | `NC_DATA_DIR`, sofern aktiviert | | `gitea`, `gitea-etc` | `GITEA_DATA_DIR`, `GITEA_ETC_DIR` | | `gitea-repos`, `gitea-custom` | Optionale explizite externe Repository-/Custom-Pfade | | `mail`, `postfix`, `dovecot` | Zentraler Mailstore (bei `ENABLE_MAIL_STORE=true`) und beide Konfigurationsverzeichnisse | | `mail-home-0`, `mail-home-1`, … | Reihenfolge von `MAIL_HOME_DIRS`, mit Excludes und Dateisystemgrenze | | `extra-0`, `extra-1`, … | Reihenfolge von `EXTRA_PATHS` | Das Manifest enthält Formatversion, Laufkennung, Host, Erstellungszeit und je Archiv Label, Quellbezeichnung, erlaubten Tar-Wurzelpfad, Dateiname, Größe und SHA-256. Der `COMPLETE`-Marker bindet sich an den SHA-256-Wert dieses Manifests. Ein lokaler vollständiger Satz kann existieren, obwohl der Upload danach fehlgeschlagen ist; erst `UPLOADED` belegt den erfolgreichen Remote-Abschluss dieses lokalen Laufs. Indexbasierte Pfade müssen ihre Reihenfolge behalten. Bei `extra-*` und `mail-home-*` lehnt der automatische Restore eine vom Manifest abweichende Pfadzuordnung ab. Für bewusst geänderte Ziele zuerst entpacken und die Zuordnung manuell prüfen. ## Voraussetzungen und Konfiguration Zielumgebung: Rocky Linux 10 mit systemd und nativen Anwendungen. Die automatische Datenbankbehandlung ist auf MariaDB/MySQL ausgerichtet; PostgreSQL, SQLite-Livekopien und Container-Volume-Snapshots benötigen ein eigenes Verfahren. Erforderliche Werkzeuge: Bash ≥ 4.4 (`inherit_errexit`), Python ≥ 3.9, GNU coreutils/findutils/tar, `flock`, `rsync`, `gzip`/`zstd`, `systemctl`; bei Nextcloud `sudo` und CLI-PHP; für Datenbanken `mariadb-dump`/`mariadb` oder kompatible MySQL-Clients. Ein konfiguriertes Crypt-Remote benötigt rclone mit den verwendeten Crypt-Flags und `check --size-only`; die Integrationstests laufen auch mit rclone 1.60.1. Für den tatsächlichen Server einen aktuell gepflegten rclone-Stand einsetzen und `--check` ausführen. Ein Ausgangspunkt auf Rocky, abhängig von aktivierten Paketquellen und vorhandener PHP-Version: ```bash sudo dnf install bash python3 tar rsync zstd gzip util-linux coreutils findutils sudo mariadb ``` Rclone und das zur vorhandenen Nextcloud-Installation passende CLI-PHP aus euren gepflegten Paketquellen bereitstellen. Kein automatischer Paket-/PHP-/Anwendungsupgrade durch den Installer. `/etc/app-backup/app-backup.conf` ist ausführbare Bash-Konfiguration: ausschließlich root darf sie verändern. DB-CNF und rclone-Konfiguration müssen reguläre, root-eigene Dateien ohne Gruppen-/Fremdrechte sein (`600`); die Hauptkonfiguration darf nicht gruppen-/weltbeschreibbar sein, empfohlen ebenfalls `600`. Arbeitsverzeichnisse gehören root und werden auf `700` gesetzt. Direkte Symlinks als Arbeitsverzeichnisse und überschneidende Quellen/Arbeitsbereiche werden abgelehnt. Übergeordnete Verzeichnisse müssen ebenfalls administrativ geschützt sein. Die CLI-Option `--config FILE` erlaubt getrennte Backup- und Restore-Einstellungen. Die Config hat Vorrang vor geerbten Umgebungswerten. Auf `DRY_RUN=true` oder alte `RESTORE_DB`-/`RESTORE_FILES`-Umgebungsvariablen nicht mehr verlassen: Die dokumentierten CLI-Schalter sind maßgeblich. ### Crypt und Schlüssel Beispiel der relevanten Remote-Optionen, ohne Zugangsdaten: ```ini [OneDriveCrypt] type = crypt remote = OneDrive:GESCHUETZTER_BACKUP_ORDNER filename_encryption = standard directory_name_encryption = true no_data_encryption = false ``` Vorhandene Passwörter/Salt und Zuordnung **nicht neu generieren**, wenn bestehende Backups weiter lesbar bleiben sollen. Sichere Defaults dürfen im Config-Block fehlen. `off`, `obfuscate` und deaktivierte Inhaltsverschlüsselung werden abgelehnt. Die Befehle setzen zusätzlich sichere Crypt-/TLS-Flags. Remote-Definitionen über `RCLONE_CONFIG_*`-Umgebungsvariablen sind nicht unterstützt; `RCLONE_CONFIG_PASS` für eine verschlüsselte Konfigurationsdatei ist ausgenommen. Die Config-Ausgabe wird direkt intern geparst und nicht protokolliert. `RCLONE_REMOTE_BASE` muss auf ein benanntes Crypt-Remote mit eigenem nichtleerem Unterpfad zeigen, beispielsweise `OneDriveCrypt:gemini`. Dessen verschlüsselter Basisordner muss bereits erreichbar sein. Kein Fallback auf das unverschlüsselte OneDrive-Remote. Den Zugriff immer mit derselben root-Konfiguration prüfen: ```bash sudo rclone --config /root/.config/rclone/rclone.conf lsd OneDriveCrypt: ``` `rclone check --size-only` prüft Dateiexistenz und Größe ohne Archivdownload. Das Paket verwendet es vor dem abschließenden Marker. Die Remote-Inhalte werden erst beim monatlichen Restore-Test anhand der SHA-256-Werte im Manifest geprüft. Quelle: [rclone check](https://rclone.org/commands/rclone_check/). Exportierte `RCLONE_*`-Variablen sind ausschließlich für die vom Paket ausdrücklich unterstützten Einstellungen und `RCLONE_CONFIG_PASS` erlaubt. Andere Optionen werden vor dem ersten rclone-Aufruf und vor dem Writer-Stopp mit ihrem Namen, ohne ihren Wert, abgewiesen. Auch `RCLONE_SIZE_ONLY` bleibt als globale Umgebungsvariable verboten: Der Größenvergleich wird ausschließlich am Check-Aufruf gesetzt. Zusätzliche Filter, Dry-run- und TLS-Overrides aus der Umgebung werden ebenfalls abgewiesen. Einstellungen über die dokumentierte Backup-Konfiguration und Remote-Definitionen verwalten; eine absichtlich manipulierte root-eigene Bash-Konfiguration liegt außerhalb dieser Absicherung. Quelle: [rclone 1.74.3 Inhaltsprüfung](https://github.com/rclone/rclone/blob/v1.74.3/fs/operations/check.go). ### Upload-Fehlerbehandlung und Betriebsgrenzen | Ereignis | Verhalten und Konsequenz | | --- | --- | | Vorübergehender Upload-Fehler | rclone wiederholt den Kopierauftrag bis zu `RCLONE_RETRIES=6` Versuche, soweit der Fehler wiederholbar ist; Wartezeit standardmäßig `RCLONE_RETRIES_SLEEP=30s`, zusätzlich gegebenenfalls Provider-Backoff. Kein erneuter DB-Dump oder Writer-Stopp innerhalb dieser Versuche. | | Upload endgültig fehlgeschlagen | Fehlerexit; lokaler vollständiger Satz bleibt erhalten; Staging wird nach erfolgreichem Dienstwiederanlauf bereinigt. Kein neuer Remote-Abschlussmarker und keine nachgelagerte Altersretention. | | Remote-Dateiprüfung scheitert oder hängt | Bis zu drei Metadaten-Prüfanläufe, jeweils maximal 3600 Sekunden plus bis zu 10 Sekunden Abbruchfrist, 30 Sekunden Pause dazwischen. Jeder Versuch prüft alle ausgewählten Dateinamen und Größen erneut. Bleibt ein Fehler, Fehlerexit und kein Abschlussmarker. Keine Archivdownloads; gleich große Beschädigungen erkennt erst der Restore. | | Abschlussmarker kann nicht geschrieben oder bestätigt werden | Fehlerexit; kein lokales `UPLOADED`, kein neuer Erfolgsstatus, keine Retention. Der Vergleich prüft die exakten Bytes einschließlich abschließender Zeilenumbrüche, passend zum strikten Restore-Parser. Ist der Marker schon geschrieben und nur seine Bestätigung fehlgeschlagen, kann der Remote-Satz vollständig vorliegen; der Lauf meldet konservativ trotzdem einen Fehler. | | Wiederholungen erfolgreich und Remote-Dateiprüfung bestanden | Remote-Marker wird zuletzt geschrieben und zurückgelesen; erst danach gilt der Upload als erfolgreich. Ein früheres `ERROR` im Log allein ist kein Endergebnis. | | Prozess hängt oder wird abgebrochen | systemd begrenzt den regulären Backup-Dienst auf 12 Stunden, den Update-Auftrag auf 24 Stunden, mit 5 Minuten Stop-Frist. Direkte CLI-/Foreground-Aufrufe besitzen diese systemd-Gesamtfrist nicht. | HTTP 409 benötigt nach dem beobachteten erfolgreichen Wiederholungsversuch keinen zusätzlichen allgemeinen Retry-Wrapper. Dieser würde Versuche vervielfachen und dauerhafte Fehler länger verdecken. Der Auslöser des OneDrive-Konflikts bleibt unbekannt. Häufen sich Konflikte, den betroffenen Objektnamen, Zeitpunkte, rclone-Version, andere Zugriffe auf den Backup-Ordner und die vollständigen Versuchsergebnisse prüfen. Keine automatischen Remote-Löschungen oder Überschreibungen zur Konfliktbehebung. `--immutable` schützt vor Änderungen durch diesen Kopierauftrag, ist aber kein serverseitiger WORM-/Ransomware-Schutz gegen andere berechtigte Zugriffe. Die sechs Gesamtversuche gelten nicht pauschal für jeden rclone-Befehl: `check` verwendet in rclone 1.74.3 keine Wiederholung des ganzen Befehls. Deshalb verwendet app-backup einen eigenen begrenzten Wrapper für Remote-Datei-/ Größenvergleiche (Backup und Platzbereinigung). Er greift auch mit alten Konfigurationen. Uploads und manuelle `app-restore.sh`-Downloads behalten ihre bisherigen Einstellungen. Der separate monatliche Restore-Test verwendet seit dem Folgefix ebenfalls begrenzte Downloads; siehe [RESTORE_TEST.md](RESTORE_TEST.md). Ein verbleibender Prüffehler beendet das Backup sichtbar. Quellen: [Kopierversuche](https://github.com/rclone/rclone/blob/v1.74.3/cmd/cmd.go), [Check-Aufruf](https://github.com/rclone/rclone/blob/v1.74.3/cmd/check/check.go), [Immutable](https://rclone.org/docs/#immutable). Ein automatischer späterer Upload eines bereits lokal gesicherten, aber nicht veröffentlichten Satzes ist bislang nicht implementiert. Nach ausgeschöpften Versuchen bleibt er zur gezielten Prüfung erhalten; der nächste Timer-Lauf erstellt einen neuen Satz. Wiederholte Fehler können deshalb lokalen Speicher belegen. Erfolgsalter, Fehlerexit, Speicher und Recovery-Dateien unabhängig überwachen; eine erfolgreiche Mailübergabe an den lokalen Mailserver beweist noch keine Zustellung. Eine fachliche Restore-Probe und separat gesicherte Zugangsdaten sind weiterhin erforderlich. Der erfolgreiche Paketlauf allein rechtfertigt keine uneingeschränkte Zusage für beliebige Serverkonfigurationen oder Katastrophenfälle. ### Datenbanken und Writer Es wird je aktivierter Anwendung ein Dump benötigt, sofern `ENABLE_DB_DUMPS=true`. Fehlende DB-Namen oder CNF-Dateien brechen ab. Der Dump enthält Routinen, Events, Trigger und Binärdaten. Leere Ausgaben sowie Fehler beim Schreiben werden abgelehnt. Für gemischte Storage Engines nutzt das Paket `--lock-all-tables --quick`. **Die globale Lesesperre kann während jedes Dumps Schreibzugriffe auf alle Datenbanken desselben Servers blockieren.** Eine reine `--single-transaction`-Sicherung schützt insbesondere nicht alle nichttransaktionalen Tabellen. Zusätzlich müssen Anwendungsschreiber über das gesamte Ruhefenster angehalten sein, damit Dateikopien zu den Dumps passen. Quelle: [MariaDB mariadb-dump](https://mariadb.com/docs/server/clients-and-utilities/backup-restore-and-import-clients/mariadb-dump). Die konkreten Rechte hängen von Serverversion, Views, Routinen und Sperrmethode ab. Der Backup-Account benötigt Leserechte sowie Rechte für Views, Trigger, Events, Routinen und die globale Sperre. Für `--lock-all-tables` ist insbesondere globales `RELOAD` erforderlich; Rechte nur auf `wordpress.*` reichen dafür nicht. `--check` prüft Anmeldung, DB-Zugriff und dieses globale Recht über lesende Privileg-Metadaten. Bei MariaDB wird auch `SELECT * FROM mysql.proc LIMIT 0` geprüft: `--routines` benötigt diesen Lesezugriff. Die Abfrage liefert keine Routineninhalte. Die Prüfung führt kein `FLUSH` aus und nimmt keine globale DB-Lesesperre. Authentifizierungsdaten aus `SHOW GRANTS` werden weder ausgegeben noch gespeichert. Direkte Grants und bei MariaDB aktivierte, auch verschachtelte Rollen werden berücksichtigt; lediglich zugewiesene, aber nicht aktivierte Rollen reichen nicht. Andere Rollenmodelle oder nicht auswertbare Rollennamen benötigen einen direkt nachweisbaren Grant für das Backup-Konto. Die Prüfung belegt **nicht sämtliche objekt- und versionsabhängigen Dump-Rechte**; der vollständige Probelauf bleibt erforderlich. Zusätzlich zum administrativen Testkonto prüfen die Integrationstests einen eingeschränkten Backup-Account mit gezielten Grants und MariaDB-Rollen. Quelle: [MariaDB FLUSH / RELOAD](https://mariadb.com/docs/server/reference/sql-statements/administrative-sql-statements/flush-commands/flush). Für einen Restore einen separaten Account mit DDL-/DML-Rechten auf die Ziel-Datenbanken verwenden. Importe sind nicht als Ganzes transaktional. Eine leere vorbereitete Zieldatenbank vermeidet, dass Tabellen oder andere Objekte zurückbleiben, die im Backup nicht vorkommen. Das Skript löscht keine vollständige Datenbank und legt keine Datenbanken/Accounts automatisch an. Nextcloud: Daten, Config, Apps/Themes und Datenbank gehören zusammen. Das konfigurierte Datenverzeichnis auf dem Server ermitteln: ```bash sudo -u apache php /var/www/html/nextcloud/occ config:system:get datadirectory ``` Nextcloud empfiehlt den Wartungsmodus während der Sicherung. Das Paket hält ihn über Dump und Kopie hinweg und erhält einen schon vorher aktiven Wartungsmodus. Zusätzlich tatsächliche Cron-/Worker-Dienste anhalten; `maintenance:mode` ist keine Sperre aller bereits laufenden OS-Prozesse. Quelle: [Nextcloud Backup](https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html). Gitea: `APP_DATA_PATH`, `[repository] ROOT`, Custom-Verzeichnis und `app.ini` prüfen; externe Objektablagen, externes SSH-Home und weitere individuell konfigurierte Pfade zusätzlich erfassen. Gitea verlangt für konsistente Sicherungen einen Stillstand, weil Datenbank, Dateien und Repositories zusammengehören. Quelle: [Gitea Backup and Restore](https://docs.gitea.com/administration/backup-and-restore/). `QUIESCE_SERVICES=(nextcloud-cron.timer nextcloud-cron.service ...)` enthält reale zusätzliche Writer. Timer zuerst, zugehörigen Service danach nennen. Gestartet wird in umgekehrter Reihenfolge. Bereits inaktive Dienste bleiben inaktiv; ein nicht zuordenbarer Übergangszustand führt zum Abbruch. Cronjobs aus crontab/externen Schedulern und socket-aktivierte Writer müssen separat berücksichtigt werden. Dienste können während einer Dateikopie länger ausfallen, insbesondere bei großen Mail-/Nextcloud-Beständen. `MAIL_BACKUP_MODE=live` und abgeschaltete Stopps sind bewusste Ausnahmen mit Konsistenzrisiko. Das Log weist darauf hin. Home-Verzeichnisse werden mit `--one-file-system` kopiert: Daten anderer Mounts fehlen dann absichtlich. Mailqueues, zusätzliche Spamfilter-/DKIM-Verzeichnisse oder Maildatenbanken sind nur gesichert, wenn sie explizit im Umfang stehen. Ein einfacher `systemctl stop` verhindert keinen späteren Start durch andere Aufträge. Auf Gemini starteten am 10.09.2026 um 15:15:00 Uhr Apache und PHP-FPM während der bis mindestens 15:16:33 laufenden Nextcloud-Datenkopie erneut. Die Starts erfolgten vor der Wiederaufnahme durch das Backup. Dieser Lauf erfüllt die vereinbarte Konsistenzbedingung nicht; eine erfolgreiche Hash-/Remote-Prüfung kann dies nicht nachträglich beweisen. Der auslösende Auftrag ist noch unbekannt. Vor dem Stoppen erhält nun jeder konfigurierte Writer eine eigene temporäre systemd-Ergänzung unter `/run/systemd/system/UNIT.d/zzzz-app-backup-LAUF.conf`. `ConditionPathExists=!/run/app-backup-guard-ZUFALL` verhindert den Start, solange das zugehörige Verzeichnis besteht. Das gilt auch für zuvor inaktive konfigurierte Writer und für Dienste mit eigener Unit-Datei unter `/etc`. Bestehende Maskierungen werden nicht verändert. Die Laufzeitergänzung muss nach `daemon-reload` als letztes Drop-in gemeldet werden; andernfalls bricht der Lauf ab, damit spätere Condition-Resets die Sperre nicht unbemerkt überschreiben. Zusätzlich schützt `systemctl stop UNIT --job-mode=replace-irreversibly` den laufenden Stop-Auftrag vor konkurrierenden Reloads und Starts. Die Startbedingung allein verhindert dessen Verdrängung nicht; dieser Konflikt trat am 15.09.2026 zwischen Apache und dem ai-rem-Healthcheck auf. Der Jobmodus gilt für den laufenden Auftrag und lässt den späteren Wiederanlauf nach Freigabe der Startsperren zu. Echte Stopfehler und explizite Job-Abbrüche führen weiterhin zum Backupfehler mit Wiederanlaufbehandlung. Verlauf und Regressionstest sind in der [Update-Anleitung](UPDATE_ANLEITUNG.md#nachtrag-vom-15092026-healthcheck-verdrängt-apache-stop) dokumentiert. Nach dem Anhalten aller Writer, vor und nach den Dumps/Dateikopien und unmittelbar vor der Freigabe prüft das Backup Dienstzustand, eigene Sperrdateien und die systemd-Zeitpunkte `ActiveEnterTimestampMonotonic` und `InactiveExitTimestampMonotonic`. Eine erkannte Aktivierung, veränderte Sperre oder nicht prüfbarer Zustand führt zum Fehler, bevor Archive als vollständiger Satz veröffentlicht werden. Diese Kontrollen ersetzen keine vollständige Ereignisaufzeichnung und schützen nicht vor nicht konfigurierten Prozessen, direktem Programmstart außerhalb systemd oder gezieltem administrativem Entfernen der Absicherung zwischen zwei Kontrollen. Die systemd-Startbedingung verhindert reguläre Startaufträge während des Fensters. Quelle: [systemd-Startbedingungen](https://github.com/systemd/systemd/blob/v256/man/systemd.unit.xml). Bei erfolgreicher Kopie und bei normalen Backupfehlern entfernt der Wiederanlauf ausschließlich die eigenen Drop-ins, lädt systemd neu und entfernt das zugehörige Sperrverzeichnis. Erst danach werden die zuvor aktiven Dienste gestartet; deren aktiver Zustand wird überprüft. Scheitert die Freigabe, bleiben Wiederanlaufdaten erhalten und es wird kein Erfolg gemeldet. Bei teilweise ausgeführtem Restore bleiben die Startsperren zusammen mit dem Wartungsmodus zur manuellen Prüfung bestehen. `ENABLE_MAIL_STORE=true` bleibt der kompatible Default: `MAIL_DIR` muss existieren. Bei Mailboxen ausschließlich in Benutzerverzeichnissen kann explizit `ENABLE_MAIL_STORE=false` gesetzt werden. Dann sind aktivierte, nicht leere `MAIL_HOME_DIRS` zwingend; fehlende Homes lassen die Vorprüfung scheitern. Postfix/Dovecot-Konfigurationen und Dienststopps bleiben erhalten. Der Restore verwendet dieselbe Komponentenzuordnung und stellt Home-Mailboxen über `mail-home-N` wieder her. Die Vollständigkeit der Benutzerliste sowie Excludes, Symlinks und Mountgrenzen muss der Betreiber prüfen. Der Paket-Updater bietet bei fehlendem zentralem Verzeichnis einen Pfaddialog oder eine ausdrückliche Homes-Bestätigung; Details stehen in [UPDATE_ANLEITUNG.md](UPDATE_ANLEITUNG.md). ### Speicher, Retention und Protokolle Der Ablauf und die Löschgrenzen sind in [SPEICHER.md](SPEICHER.md) beschrieben. Vor echten Backups und der Update-Vorprüfung werden eindeutig erkannte alte Arbeitsreste bereinigt und Quellen mit identischen rsync-Excludes sowie lesenden DB-Größenabfragen inventarisiert. Die Platzschätzung enthält Reserven für Dumps, Dateisystem-Metadaten und Wachstum. `MIN_FREE_GB` (Default 10 GiB) bleibt zusätzlich frei. Erst nach erfolgreicher Platzbeschaffung werden Writer angehalten. Reicht Scratch-Bereinigung nicht, werden ältere geeignete lokale Sätze unter Wahrung von `SPACE_KEEP_LOCAL` (Default und Minimum eins, seit 21.09.2026) lokal vollständig validiert und remote auf Dateiexistenz und passende Größen geprüft. Remote-Manifest und Abschlussmarker müssen zusätzlich bytegenau übereinstimmen. Dabei werden keine Archive geladen. Die normale Retention bleibt separat bei `RETENTION_KEEP_LAST` (Default drei). Bei weiterem Platzmangel nennt das Log das Defizit und die geschützten Archivbestände mit belegten Bytes und Schutzgrund. Nur danach darf ihre lokale Kopie entfernt werden. Offsite-Dateien bleiben unverändert. Diese Platzbereinigung gilt auch bei `--no-retention` während einer Update-Abnahme und kann das lokale Aufbewahrungsalter unterschreiten. Jede erfolgreich komprimierte Komponente wird vor dem Entfernen ihrer Arbeitskopie vollständig geprüft. Bei Fehlern werden Staging und unvollständige Archive nach erfolgreicher Dienstwiederherstellung ebenfalls entfernt. Vollständige lokale Sätze ohne Upload-Bestätigung, ungelöste Recovery-Zustände, manuelle Restores, historische Formate und unbekannte Ordner bleiben geschützt. Ein erkanntes altes Staging ohne Recovery-Journal kann auch aus vorherigen Paketständen bereinigt werden. Die Bedarfsschätzung ist keine harte Quote; externe Schreiber oder ungewöhnlich wachsende Dumps können weiterhin einen Abbruch verursachen. Remote-`--verify-only` benötigt jeweils nur ein komprimiertes Archiv; die lokale Variante prüft direkt ohne Kopie. Reguläre v2-Restores planen zusätzlichen Platz vor Download/Kopie und Entpacken ein und nutzen dieselbe lokale Platzbereinigung. Ein echter Restore benötigt weiterhin Platz für den vollständigen Arbeitsbestand; Fehlerdaten werden dort für eine manuelle Prüfung erhalten. Reguläre Altersretention: - Läuft erst nach Abschluss des neuen Backups und gegebenenfalls der kompletten Remote-Prüfung. - Erkennt nur v2-Laufnamen und einen zum Manifest passenden Abschlussmarker. - Löscht ganze Sätze, keine einzelnen Archivinhalte. - Berücksichtigt das UTC-Datum im Laufnamen und behält die jüngsten `RETENTION_KEEP_LAST` geeigneten Sätze; mindestens zwei sind vorgeschrieben, Default drei. - Lokale Sätze ohne `UPLOADED` werden bei aktiviertem Upload nicht gelöscht. Bei bewusst deaktiviertem Upload sind vollständige lokale Sätze löschbar. - Alte v1-Läufe, unbekannte Verzeichnisse und unvollständige Uploads bleiben erhalten. Erkannte lokale Scratch-Reste werden durch die getrennte Platzbereinigung behandelt. - Remote-Retention ist in neuen Konfigurationen aus. Das automatische Update setzt `ENABLE_REMOTE_RETENTION=false`; eine spätere Aktivierung erfolgt bewusst in der Konfiguration. Retention prüft die Metadaten alter Sätze, nicht erneut deren gesamte Nutzdaten. Ein `COMPLETE`-Marker beweist den erfolgreichen Abschluss zum Erstellungszeitpunkt, keine ewige Verfügbarkeit. Regelmäßige unabhängige Rücklese-/Restore-Proben bleiben erforderlich. Remote-Metadaten, die sich nicht lesen/validieren lassen, werden konservativ ausgelassen; fehlgeschlagene eigentliche Löschungen führen zu einem fehlgeschlagenen Lauf. Neue Backup-/Restore-Logs liegen unter `/var/log/app-backup` und werden nach `LOG_RETENTION_DAYS` (Default 30) bei einem erfolgreichen Backup entfernt. Historische v1-Protokolle bleiben zur manuellen Prüfung. Das systemd-Journal unterliegt seiner eigenen Aufbewahrung. Das Mail-Reporting ist zusätzlich; eine fehlende Mailzustellung wird protokolliert, sie ersetzt kein unabhängiges Monitoring. ## Betrieb und Monitoring ### Ausführliche Mailberichte `ENABLE_MAIL_REPORT=true` sendet nach jedem regulär beendeten Backup einen Bericht über den lokalen sendmail-kompatiblen MTA. Die bisherigen Optionen `MAIL_TO`, `MAIL_FROM`, `MAIL_SUBJECT_PREFIX` und `MAIL_INCLUDE_LOG_TAIL_LINES` werden wieder ausgewertet und bei einem Update beibehalten. Defaults: Empfänger `johannes`, Absender `app-backup@` bei leerem/fehlendem `MAIL_FROM`, Betreffpräfix `[app-backup]`, 200 Protokollzeilen. `MAIL_SENDMAIL_BIN` ist standardmäßig `/usr/sbin/sendmail`; `MAIL_SEND_TIMEOUT` begrenzt dessen Aufruf auf 30 Sekunden (zulässig 1–300). Der Log-Auszug darf 0–10000 Zeilen umfassen. Seit 12.09.2026 überschreibt `MAIL_HOSTNAME` den Hostnamen im Betreff und im `Host:`-Feld des Berichts. Für Gemini in `/etc/app-backup/app-backup.conf` setzen: ```bash MAIL_HOSTNAME="gemini.jr-it-services.de" ``` Der Betreff folgt weiterhin dem Schema `[app-backup] SUCCESS/FAIL HOST LAUF`, zum Beispiel `[app-backup] SUCCESS gemini.jr-it-services.de appbackup_2026-…`. Ein leerer oder fehlender Wert fällt auf `socket.getfqdn()` zurück. Die Einstellung gilt für Erfolgs- und Fehlermails und bleibt beim Paketupdate erhalten. Vor dem Update gesetzt, wirkt sie bereits im Abnahmebackup des neuen Pakets. Spätere Änderungen werden beim nächsten Lauf eingelesen; ein Dienstneustart entfällt. Systemhostname, Archivnamen und Manifest-Hostname bleiben unverändert. Auch der Absender bleibt unabhängig: Ein explizites `MAIL_FROM` wird beibehalten; bei leerem Wert wird weiterhin `app-backup@` verwendet. Header-Zeilenumbrüche im Override werden wie bei den anderen Mailheadern abgewiesen; ein Berichtsfehler verändert den Backup-Exit-Code nicht. Der UTF-8-Bericht enthält Zeit, Host, Status, Exit-Code, Dauer, Lauf-ID und letzte Phase, Konfigurations-/Arbeits-/Archivpfade, Kompression, Crypt-Ziel und getrennten Uploadstatus. Dieser wird erst nach erfolgreicher Remote-Datei-/Größenprüfung und bytegenauer Markerbestätigung `OK`. Der Bericht nennt den begrenzten Prüfumfang und verweist für die Inhaltsprüfung auf den Restore-Test. Bei deaktiviertem Upload steht ausdrücklich „nur lokal“; bei Fehlern bleibt die nicht abgeschlossene Uploadphase sichtbar. Ein Fehler bei nachgelagerter Retention kann deshalb korrekt `Status: FAIL` bei bereits verifiziertem Upload ergeben. Die Dateikopien protokollieren außerdem rsync-Statistiken zu Dateianzahl und übertragenen Bytes; laufende rclone-Statistiken erscheinen im gleichen Log. Die Größen werden nach Wiederaufnahme der Dienste und vor erfolgreicher Staging-Bereinigung erfasst: belegter Speicher pro vorhandener Komponente und insgesamt, dazu Archiv-Dateigrößen und deren Summe. Komponentengrößen werden einzeln gemessen; bei gemeinsamen Hardlinks müssen sie sich nicht zum Gesamtwert addieren. Es werden keine Live-Quellen erneut kopiert. Fehlende Messwerte heißen „nicht verfügbar“, nicht fälschlich `0`. Bei Abbruch können aufgeführte Archive unvollständig sein; Status und letzte Phase sind maßgeblich. Die Messung erfolgt mit `du`, je Aufruf auf 30 Sekunden begrenzt, und verlängert nicht die Dienstpause. Am Schluss von Backup- und Restore-Test-Mail steht eine aktuelle Platzübersicht nach der Bereinigung. Sie enthält die gesamte Laufzeit, die belegten Bytes des lokalen Arbeitsbereichs, der Archive, des Staging, des Restore-Arbeitsbereichs, des Restore-Tests und der Protokolle/Berichte. Der freie Platz wird für jeden betroffenen lokalen Datenträger einmal ermittelt, auch wenn Archive oder Logs auf einer anderen Partition liegen. Teilbereiche sind im Arbeitsbereich enthalten und dürfen nicht nochmals zu dessen Gesamtwert addiert werden. Die Messung nutzt `du -sx` (belegte Blöcke, keine Symlink-Ziele oder eingehängte Fremddatenträger); fehlende oder nicht rechtzeitig messbare Werte erscheinen als „nicht verfügbar“. Remote-Cloud-Speicher und produktive Quelldaten sind nicht Teil dieser lokalen Platzbilanz. Vor dem Lesen des Log-Auszuges wird der Logger beendet und auf vollständiges Schreiben gewartet. Mailheader werden über die Python-Mailbibliothek erzeugt; eingeschleuste Header-Zeilenumbrüche werden abgewiesen. `sendmail -oi -t` schützt gegen vorzeitiges Nachrichtenende durch eine einzelne Punktzeile im Log. Konfigurationsinhalte und Zugangsdaten werden nicht für den Bericht gelesen; enthaltene Dateinamen/Pfade und der Log-Auszug sind dennoch vertraulich. Die MIME-Nachricht bleibt mit Modus `600` unter `$LOG_DIR/report_LAUF.eml` erhalten, auch bei MTA-Fehlern oder Zustellungs-Timeout. Alte Berichte werden wie Logs nach `LOG_RETENTION_DAYS` bei erfolgreichem regulärem Backup bereinigt; der Update-Abnahmelauf überspringt diese Retention. Ein Mailfehler ist eine protokollierte Warnung und ändert den Backup-Exit-Code nicht. Erfolgreiche Übergabe an den MTA bestätigt noch keine Zustellung im Postfach. `--check`, Restore und reine Restore-Prüfungen erzeugen keine Backup-Mail. Sehr frühe Fehler vor der Laufinitialisierung und SIGKILL/Stromausfall können keinen Abschlussbericht erzeugen. Beim Update bezieht sich die Mail auf dessen Backup-Teil; `SUCCESS` in der Update-Ergebnisdatei bestätigt zusätzlich den anschließenden separaten Remote-Restore-Prüflauf und die Timer-Aktivierung. Unabhängige Überwachung bleibt deshalb erforderlich. ### Status und Zeitplan prüfen ```bash sudo /usr/local/sbin/app-backup.sh --check sudo systemctl start app-backup.service sudo systemctl status app-backup.service --no-pager sudo journalctl -u app-backup.service --since today --no-pager sudo cat /var/backups/app-backup/last-backup.json sudo cat /var/backups/app-backup/last-successful-backup.json systemctl list-timers app-backup.timer ``` Exit `0` bedeutet, dass die ausgeführten erforderlichen Schritte erfolgreich waren. Jeder Fehlercode ist ein Fehler; `130`/`143` kennzeichnen INT/TERM. `last-backup.json` wird beim Beenden geschrieben und zeigt während eines laufenden Backups noch den vorherigen beendeten Lauf. `last-successful-backup.json` wird ausschließlich nach einem vollständig erfolgreichen Backup aktualisiert. `--check` aktualisiert diese Erfolgsmarkierung nicht. Sehr frühe Fehler vor der Laufinitialisierung/Sperrübernahme können nur auf stderr bzw. im Journal erscheinen; deshalb sowohl Servicefehler als auch Alter des letzten Erfolgs überwachen. Bei `ENABLE_UPLOAD=false` belegt ein Erfolg nur eine lokale Sicherung; für Offsite-Überwachung zusätzlich die Upload-Konfiguration und `UPLOADED` des gemeldeten Satzes berücksichtigen. Für den täglichen Zeitplan z. B. bei mehr als 36 Stunden ohne erfolgreichen Lauf alarmieren, zusätzlich freien Speicher, offene Recovery-Dateien und wachsende Partial-Verzeichnisse prüfen. Diese Überwachung läuft idealerweise außerhalb des gesicherten Servers. `TimeoutStartSec=12h` und `TimeoutStopSec=5min` sind Ausgangswerte und müssen zum ersten gemessenen Vollbackup mit Upload und Remote-Dateiprüfung passen. Ein geplanter Backupabbruch erfolgt über `systemctl stop app-backup.service`. Normale INT-/TERM-Behandlung versucht die ursprünglichen Dienste wiederherzustellen. SIGKILL, Kernelabsturz oder Stromverlust können keine Shell-Traps ausführen. Dafür liegt `recovery-LAUFNAME.json` mit vorher aufgezeichnetem Wartungs-/Dienstzustand und Arbeitsordner im Workdir. Diese Datei ist eine manuelle Wiederanlaufhilfe, kein automatischer Neustartauftrag. Das System startet nicht blind Anwendungen über einen teilweise restaurierten Datenbestand. Backup-Unit und Update-Auftrag verwenden `KillMode=control-group`: Beim Stoppen erhalten auch laufende Dump-, rsync-, Kompressions- und rclone-Prozesse TERM. Dadurch können die wartenden Shells ihre Fehlerbehandlung ausführen, statt bis zum regulären Ende eines langen Uploads zu warten. Die tee-Prozesse ignorieren INT/TERM und enden mit dem Schließen ihrer Eingabe; so bleibt die Protokollierung während der Wiederaufnahme erhalten. Die fünfminütige Stop-Frist bleibt bestehen. Sie garantiert keine Wiederherstellung bei blockierten Kernelzugriffen oder fehlgeschlagenen Dienststarts. Quelle: [systemd-Signalzustellung](https://github.com/systemd/systemd/blob/v256/man/systemd.kill.xml). Die Wiederanlaufdatei enthält außerdem `service_guard_files` und `service_guard_directory`. Bei manueller Wiederherstellung nur die darin genannten eigenen Drop-in-Dateien entfernen, `systemctl daemon-reload` ausführen und danach das zugehörige leere Sperrverzeichnis mit `rmdir` entfernen. Keine fremden Drop-ins oder Maskierungen löschen. Erst nach Prüfung des Datenzustands die aufgezeichneten Dienste und den Wartungsmodus wiederherstellen. Die Sperren liegen unter `/run` und überleben keinen Neustart; ein Reboot ist deshalb keine sichere Reparatur eines teilweise ausgeführten Restores. ## Wiederherstellung ### Prüfphase Ein Restore arbeitet zuerst auf einer privaten lokalen Kopie. Bei einer Remote-Quelle wird über dasselbe geprüfte Crypt-Remote heruntergeladen. Auch der Prüflauf benötigt daher lokalen Speicher und verursacht Download-Traffic. Vor Anwendung werden alle Archivgrößen und SHA-256-Werte mit dem Manifest verglichen, Kompressionsströme geprüft und sämtliche Tar-Einträge untersucht. Absolute Memberpfade, `..`, doppelte Einträge, unerwartete Komponentenwurzeln, Spezialdateien sowie Schreibpfade durch Symlinks/Hardlinks werden abgewiesen. Jede Komponente wird danach getrennt entpackt. Die Zielpfade stammen ausschließlich aus der lokalen vertrauenswürdigen Konfiguration, nicht aus dem Backup. - Ohne Ausführungsoption oder mit `--dry-run`: prüft und zeigt den Plan; keine Dienste, Dateien in Anwendungszielen oder Datenbanken werden geändert. - `--verify-only`: prüft den gesamten v2-Satz ohne Zuordnung zu Anwendungen. - `--extract-only`: prüft und entpackt, behält den angegebenen privaten Ergebnisordner. - `--apply --yes`: schreibt ausgewählte Inhalte in bestehende Zielverzeichnisse und Datenbanken. - `--components wordpress,db-wordpress`: explizite Komponentenauswahl, nur für in der Config aktivierte Komponenten. - `--no-db`, `--no-files`: schließen die jeweilige Kategorie aus. Auch bei einer Auswahl muss der vollständige Satz prüfbar sein. Fehlt eine laut Config erforderliche ausgewählte Komponente, wird vor Eingriffen abgebrochen. ### Ausführung und Nachkontrolle Einen echten Restore zunächst auf einem isolierten Testsystem mit eigener `restore.conf`, passender App-/DB-Version und vorbereiteten Zielverzeichnissen durchführen. Einen bekannten aktuellen Datenbestand separat sichern, bevor bestehende Anwendungen überschrieben werden. Für einen echten Restore mit aktivierter Nextcloud ist verwalteter Wartungsmodus vorgeschrieben. Alle in der Config aktivierten Anwendungen und zusätzlichen Writer werden für die Konsistenz angehalten, auch bei einer kleineren Komponentenauswahl. Dateien werden mit Metadaten zurückkopiert. Standardmäßig bleiben zusätzliche Zieldateien erhalten. `RESTORE_STRICT_DELETE=true` entfernt zusätzliche Dateien innerhalb ausgewählter Zielkomponenten; konfigurierte Excludes bleiben geschützt. Keine atomare Rücksetzung der gesamten Anwendung: Ein Fehler nach Beginn des Zurückschreibens kann einen Teilzustand hinterlassen. Nach Nextcloud-Code-/DB-Restore setzt das Skript den Wartungsmodus erneut und führt `occ maintenance:data-fingerprint` aus. Einen vor dem Restore bereits aktiven Wartungsmodus lässt es an. `maintenance:repair` oder ein vollständiger `files:scan` werden nicht pauschal ausgeführt; sie müssen zum konkreten Wiederherstellungsfall passen. Der Fingerprint informiert Clients über den wiederhergestellten Datenzustand. Quelle: [Nextcloud Restore](https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html). Nach einem erfolgreichen Restore prüfen: - WordPress: Startseite, Admin-Anmeldung, Uploads und Stichproben in Beiträgen. - Nextcloud: `occ status`, Anmeldung, Dateiinhalt/Download, Freigaben, Apps und einen kontrollierten Client-Test. - Gitea: Anmeldung, Repository-Clone, LFS/Attachments, kontrollierter Push; bei geänderten Installationspfaden ggf. Git-Hooks nach Gitea-Anleitung neu erzeugen. - Mail: Zustellung, IMAP-Zugriff, Queue, Rechte und Konfigurationstest von Postfix/Dovecot. - Datenbanken: relevante Tabellenzahlen, definierte fachliche Stichproben, Events/Routinen und Zeichensatz. - SELinux: Labels prüfen; `restorecon` nur passend zu den gewünschten Zielpfadregeln anwenden. Ein Umzug auf andere Pfade kann andere Labels erfordern. Bei einem Teilfehler: Log und `recovery-*.json` lesen, Arbeitsdaten erhalten, Fehlerursache beheben und Restore kontrolliert wiederholen oder auf einen separaten Ausgangssnapshot zurückgehen. Angehaltene Anwendungen bleiben für diese Prüfung gestoppt. Bereits gestartete Dienste bei einem Fehler im Wiederanlauf müssen gesondert kontrolliert werden. Es gibt keinen automatischen Datenbank-/Datei-Rollback. ### Alte Sicherungen v1-Split-Archive haben weder manifestgebundene Prüfsummen noch einen verlässlichen Abschlussmarker. Sie werden nur mit `--legacy-unverified --extract-only` geöffnet. Der Parser akzeptiert die bisherigen datierten Dateinamen und Komponentenwurzeln; jeder Archivinhalt wird geprüft und separat entpackt. **Vollständigkeit und ursprünglicher Inhalt lassen sich damit nicht nachträglich beweisen.** Nur die Dateien **eines** alten Laufs in einen eigenen Ordner kopieren. Alte DB-Dumps liegen gemeinsam unter `db/`, Gitea unter `files/gitea-data`, Mail-Homes ggf. gesammelt unter `files/mail-home`. Nach Prüfung manuell in vorbereitete Testziele kopieren und DB-Dumps mit einem passenden Client importieren. Alte monolithische Formate vor den Split-Archiven unterstützt dieser Entpackmodus nicht. Alte Sicherungen erst nach einer erfolgreichen neuen Offsite-Sicherung und echten Wiederherstellungsprobe freigeben. ## Automatisiertes Transferpaket Ergänzend zum manuellen Installer gibt es ein selbstentpackendes `.run`-Paket mit integrierter Payload-Prüfsumme und separater SHA-256-Datei. Es wird aus einer expliziten Dateiliste gebaut; echte Zugangsdaten, Git-Historie und Sicherungsdaten sind ausgeschlossen. Anleitung: [UPDATE_ANLEITUNG.md](UPDATE_ANLEITUNG.md). `update.sh` erstellt einen privaten Migrationsordner und eine Kandidatenkonfiguration. Der erste Check nutzt geschützte Kopien der Zugangsdaten. Installation und Abnahmebackup laufen dann unter einer gemeinsamen, von Kindprozessen überprüften Dateisperre. Der Starter übergibt den langen Ablauf an eine systemd-Unit; das Ergebnis bleibt im Migrationsordner erhalten. Ein kombinierter Rollback stellt Paketdateien und die vorherige Hauptkonfiguration wieder her. Die Übergabe nutzt `systemd-run --no-block` mit `Type=oneshot`: Der Starter wartet nur auf die Annahme des Auftrags, gibt danach zuerst die Backup-Sperre und dann die Update-Sperre frei. Der Worker erwirbt beide selbst und darf nur mit einem weiterhin als `QUEUED` freigegebenen Migrationsstand beginnen. Bei einer fehlgeschlagenen Übergabe wird diese Freigabe unter gehaltener Sperre entzogen. Der Worker richtet Fehlerprotokoll und EXIT-Trap vor dem maximal 60-sekündigen Warten auf die Update-Sperre ein. Erst `SUCCESS` belegt das Gesamtresultat. Das behebt das gegenseitige Warten in den früheren Paketen: Ohne `--no-block` wartete der Starter auf den oneshot-Abschluss, während der Worker auf seine Sperre wartete. Ursache, Diagnose und Wiederanlauf beschreibt die Update-Anleitung. Der Abnahmelauf nutzt `app-backup.sh --no-retention`: keine reguläre Altersretention; die lokale Platzbereinigung bleibt erlaubt. Nach Upload, Remote-Datei-/ Größenprüfung und Markerbestätigung werden die konfigurierten Timer aktiviert. Die Update-Abnahme startet keinen Archivdownload und keinen Offsite-Restore. Entpacken und isolierter DB-Import finden beim Monatslauf statt. Remote-Retention bleibt nach dieser Migration bewusst aus, bis die fachliche Restore-Abnahme erfolgt ist. Direkt erkennbare Nextcloud-systemd-Writer werden übernommen. Bei klassischen Cronjobs fragt das Paket nach einer vorübergehenden Pause des gesamten Cron-Dienstes; dies kann andere fällige Cronjobs betreffen und erfolgt deshalb nicht stillschweigend. Nach dem Writer-Stopp wartet die Sicherung auf noch laufende direkte Nextcloud- `cron.php`-/`occ`-Prozesse (maximal `WRITER_DRAIN_TIMEOUT`, Default 300 Sekunden). Nextcloud dokumentiert systemd-Timer als Alternative zu crontab: [Nextcloud Hintergrundjobs](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/background_jobs_configuration.html). ## Installation, Update und Rückweg Die ausführbare Schrittfolge steht in [README: bestehende Installation aktualisieren](README.md#bestehende-installation-aktualisieren). Der Installer prüft Shell-/Python-Syntax, wartet nicht auf laufende Sicherungen, sondern lehnt ein Update bei gehaltener Sperre oder aktivem Backup-Service ab. Vor Änderungen werden ersetzte Dateien samt Inventar unter `/var/backups/app-backup-update-XXXXXXXX` gesichert. Neue Dateien werden über temporäre Geschwisterdateien installiert und atomar umbenannt. Das Gesamtpaket ist keine atomare Dateisystemtransaktion; während des Austauschs wird die gemeinsame Sperre gehalten und der Timer angehalten. Bei einem Teilfehler bleibt er gestoppt; das Rollback-Skript stellt das vorherige Inventar wieder her. | Pfad | Updateverhalten | | --- | --- | | `/usr/local/sbin/app-backup.sh`, `app-restore.sh` | Ersetzen, alte Version im Rollback-Paket | | `/usr/local/lib/app-backup/` | Gemeinsame Bibliothek und Python-Prüfer installieren | | `/etc/app-backup/app-backup.conf` | Behalten; neue Vorlage als `.conf.dist` | | `/etc/app-backup/*.cnf` | Bestehende Zugangsdaten behalten; ausschließlich `.example` installieren | | `/etc/systemd/system/app-backup.service` | Ersetzen; vorhandene systemd-Drop-ins bleiben bestehen und müssen geprüft werden | | `/etc/systemd/system/app-backup.timer` | Bestehenden Zeitplan behalten; Vorlage als `.timer.dist` | | `/etc/logrotate.d/app-backup` | Alte Rotationsregel durch Hinweis auf Laufprotokollbereinigung ersetzen | | `/usr/local/share/doc/app-backup/` | README und diese Dokumentation installieren | Der Installer setzt den Timer bei einer Neuinstallation nicht automatisch in Betrieb. `--enable-timer` ist optional für bereits vorbereitete Installationen. Ein zuvor aktiver Timer wird nach einem erfolgreichen gewöhnlichen Update wieder gestartet; beim v1→v2-Wechsel vorher ausdrücklich stoppen, Optionen abgleichen und neu testen. Durch `Persistent=true` kann ein verpasster Termin beim Wiedereinschalten unmittelbar nachgeholt werden. Das Rollback-Paket enthält eine geschützte Kopie der Hauptkonfiguration als Referenz. `rollback.sh` verändert die aktuell erhaltene Hauptkonfiguration nicht automatisch. Es setzt ersetzte Paketdateien zurück und entfernt Dateien, die vor dem Update nicht vorhanden waren. Selbst vorgenommene Config-Änderungen und Backup-/Restore-Daten bleiben separate Entscheidungen. Mit einem alten defekten Restore ist der Rückweg zur alten Software keine Verbesserung der Datenrettung. ### Zugangsdaten aus alten Commits Die Entfernung der drei DB-Dateien aus dem aktuellen Baum löscht deren Historie nicht. Betroffene DB-Accounts und Passwörter am Server identifizieren, Passwörter wechseln, die aktiven CNF-Dateien und gegebenenfalls weitere berechtigte Anwendungen anpassen und einen vollständigen Dump testen. Keine Werte in Issues, Chat, Logs oder neue Commits kopieren. Den Gitea-Repositoryzugriff und vorhandene Klone prüfen. Eine koordinierte Historienbereinigung wäre ein eigener Eingriff mit Auswirkungen auf alle Klone; sie ersetzt keine Passwortrotation und ist nicht Teil dieses Updates. ## Prüfung und Abnahme ### Monatliche automatisierte Restore-Probe Die neue [Restore-Test-Betriebsanleitung](RESTORE_TEST.md) beschreibt den vollständigen Offsite-Test mit isoliertem MariaDB-Import. Ein neuer Scheduler verwendet die gemeinsame Backup-/Restore-/Update-Sperre. Archive werden einzeln geprüft und entpackt; komprimierte Eingaben werden vor DB-Imports entfernt. Temporäre Dateien und DB-Container werden bei Erfolg, Fehler und Abbruch bereinigt; systemd und eine Boot-Einheit übernehmen die Nachbereinigung. Standard sind ein adaptives Arbeitsbudget (`RESTORE_TEST_MAX_WORK_GB=0`) und 2 GiB freie Reserve. Eine positive Budgetgrenze bleibt optional möglich. Die Überwachung ist keine harte Dateisystemquote. Ein nicht passender Sicherungssatz führt zu FAIL, nicht zu einer unbemerkten Teilprüfung. Protokolle und Berichte bleiben klein; das vom Installer vorbereitete passende DB-Image bleibt als Abhängigkeit installiert. Der Test ergänzt die bisherige technische Archivprüfung um tatsächliches Entpacken und DB-Import mit Tabellenprüfung. Fachliche Anwendungswiederherstellung auf einer isolierten VM und unabhängige Überwachung des letzten Erfolgs bleiben separate Abnahmen. Aktivierung und Prüfung auf Gemini erfolgen nach Installation. ### Entwicklungstests Historische Verifikation vom 15.09.2026 für den Code-Stand `3730ab4676a5`: 119 Fälle im Standardlauf, davon 111 erfolgreich und acht planmäßig übersprungen (906,3 Sekunden). Die drei Benutzer-systemd-Fälle bestanden separat: alter Reload-Konflikt reproduziert, geschützter Stop gegen Reload und Start erfolgreich, Dienst anschließend inaktiv. Fünf optionale Docker-Integrationstests wurden in diesem abschließenden Lauf nicht erneut ausgeführt. ShellCheck und Shell-Syntaxprüfung bestanden. Die folgenden älteren Prüfnachweise bleiben historische Nachweise; die neue Serverabnahme steht aus. Reproduzierbar mit `tests/Dockerfile` und `tests/run.sh`. Der Container bekommt das Repository nur lesbar und benötigt während der Tests kein Netzwerk. Die MariaDB läuft ohne TCP-Port in einem temporären Verzeichnis. systemd/occ werden simuliert; alle eigentlichen Archive, Dateikopien, Dumps/Imports und Crypt-Operationen sind real. Verifikation am 12.09.2026: **Standardlauf erfolgreich** (81 Fälle, davon 80 erfolgreich und ein separater Docker-Test planmäßig übersprungen; 704,8 Sekunden). Zusätzlich bestanden alle **19 gezielten Restore-Tests** mit echten DB-Containern und der Paketupdate-/Rollback-Test mit aktiviertem Restore-Timer. Die gezielten Tests prüfen insbesondere Platzgrenzen, ungültiges SQL, SIGTERM, SIGKILL, defekte Statusdateien, einen unerreichbaren Docker-Daemon und anschließende vollständige Bereinigung. Shell-Syntax, ShellCheck und systemd-Unit-Prüfung ohne Befund. Basis-Testumgebung: Debian 13.6, MariaDB 11.8.6, rclone 1.60.1-DEV und ShellCheck 0.10.0. Test-Mails werden nur lokal erfasst. Die tatsächliche Rocky-/OneDrive-/MTA-Abnahme des neuen Monatslaufs auf Gemini steht nach Installation noch aus. Die Regressionstests für `RCLONE_SIZE_ONLY` und zusätzliche Marker-Zeilenumbrüche scheiterten vor den jeweiligen Korrekturen mit einem fälschlich erfolgreichen Backup und bestanden danach. Upload-/Markerfehler werden an den Befehlsgrenzen injiziert, während Crypt-Übertragung und Inhaltsvergleich real bleiben. Die Tests bilden keine OneDrive-API nach; das tatsächliche HTTP-409-Wiederholungsverhalten ist durch den Betreiberlauf und den rclone-1.74.3-Quellcode separat belegt. Geprüfte Fälle umfassen: - Ausführliche UTF-8-Erfolgs-/Fehlermails mit erhaltenen Header-Einstellungen, Größen vor Bereinigung, finalem Fehlerauszug und korrektem Upload-Prüfstatus. - MTA-Fehler und begrenzter Versand-Timeout verändern den Backup-Exit-Code nicht; die lokale Nachricht bleibt erhalten. Header-Injection wird abgewiesen; Prüfläufe/Restore versenden keine Backup-Mail. - Paketupdate installiert die Berichtskomponente, erhält alte Mail-Einstellungen und erzeugt beim Abnahmebackup einen Bericht über die MTA-Attrappe. - Endgültiger Upload-Fehler trotz bereits übertragener Bytes: vollständiger lokaler Satz, vorheriger Erfolgsstatus und alte lokale/entfernte Retention-Kandidaten bleiben erhalten. - Fehlende Remote-Dateien, falsche Größen, Marker-Schreibfehler, abweichender Marker-Inhalt und zusätzliche Zeilenumbrüche verhindern Upload-Erfolg und Retention. Gleich große Beschädigungen erkennt seit 16.09. erst der Restore. - Nicht unterstützte rclone-Umgebungsoptionen werden vor Remote-Zugriff und Writer-Stopp abgewiesen; unterstützte exportierte Einstellungen bleiben nutzbar. - Periodische NOTICE-Statistiken bei gedrosseltem echten Crypt-Upload, erfolgreicher Remote-Dateiprüfung und separatem Remote-Restore; die Statistik verändert den maschinell gelesenen Abschlussmarker nicht. - Fremde Startversuche während realer rsync-Kopien werden durch das systemd-Testmodell übersprungen; zuvor inaktive Writer bleiben nach dem Lauf inaktiv. - Aktive sowie zwischenzeitlich gestartete und wieder gestoppte Writer verhindern die Veröffentlichung eines vollständigen Satzes. - Nicht geladene Startsperre, Erhalt vorhandener Maskierungen und fehlgeschlagene Sperrfreigabe mit erhaltener Wiederanlaufdatei. - Bei teilweise ausgeführtem Restore bleiben eigene Startsperren erhalten. `systemd-analyze` prüft die reale Condition-Auswertung und Unit-Syntax separat, ohne einen Dienst zu starten. - TERM an die gesamte simulierte Unit-Prozessgruppe während blockierter Kopie beziehungsweise blockiertem Upload: zeitnahes Fehlerergebnis, Dienstwiederaufnahme, freigegebene Startsperren, erhaltenes Protokoll und gestoppter Backup-Timer. - Verweigerter rsync-Quellzugriff bricht das Update vor Installation und Dienst-Stopp ab. - Verweigerte Verzeichnisattribute werden vor Wartungsmodus erkannt; der private Prüfbereich wird entfernt. - Attributprobe kopiert keine Kindobjekte; rsync erbt bei Prüfung, Backup und Restore keine Update-/Backup-Sperrdeskriptoren. - Fehlendes globales `RELOAD` bei eingeschränktem Konto: Vorprüfungsabbruch ohne Installation/Writer-Stopp, anschließend erfolgreicher Paketlauf nach gezieltem Grant. Aktive verschachtelte MariaDB-Rollen werden anerkannt, inaktive Rollen abgewiesen. - Fehlender Zugriff auf MariaDB-Routinen-Metadaten vor Wartungsmodus erkannt; nach gezielter Berechtigung tatsächlicher Dump mit gespeicherter Prozedur geprüft. - Fehlender Mailstore ohne Live-Änderung, explizite Pfadkorrektur, Homes-only-Update samt tatsächlichem Maildatei-Restore und Ablehnung unvollständiger Home-Konfigurationen. - Shell-Syntax und ShellCheck der Paketdateien. - MyISAM-Dump und tatsächlicher SQL-Import samt Dateiwiederherstellung. - gzip/zstd, korrekte Excludes, vorhandener Wartungsmodus und inaktive Dienste. - Lokaler Crypt-Upload mit Remote-Dateiprüfung und separatem Restore aus diesem Remote. - Fehler bei Dump, rsync, Kompression, Diensten und Remote-Dateiprüfung. - Fehlende/beschädigte Archive, Pfadtraversal, Link-Pivots, unzulässige Tar-Dateitypen. - Gemeinsame Sperre, TERM-Abbruch, Prüflauf ohne falsche Erfolgsmeldung. - Selektiver Restore mit Löschung und Schutz ausgeschlossener Nextcloud-Daten. - Lokale/Remote-Retention mit Mindestanzahl und Erhalt alter/unvollständiger Läufe. - Paketupdate mit Erhalt von Config/Zeitplan und ausführbarem Rollback. - Selbstentpackendes Transferpaket mit erfolgreichem Komplettlauf und kombiniertem Konfigurations-/Paket-Rollback. - Abbruch vor Änderungen bei Vorprüfungsfehlern, gestoppter Timer bei fehlgeschlagenem Abnahmebackup, Ablehnung beschädigter Pakete. - Übergabe an einen vom Starter unabhängigen Prozess: Das systemd-run-Testmodell wartet bei `Type=oneshot` ohne `--no-block` auf das Worker-Ende. Der Regressionstest scheiterte am bisherigen gegenseitigen Sperrwarten und besteht nach dem Fix. - Abgewiesene systemd-Übergabe, zurückgezogene Freigabe bei bereits eingereihtem Auftrag und persistente Fehlerprotokolle bei Sperr-Timeout; jeweils ohne Installationsänderung oder Timer-Stopp. - Ausdrückliche Cron-Policy und Writer-Wartezeit/Timeout. **Technischer Paketlauf auf Gemini bestätigt:** Das vom Betreiber am 10.09.2026 übermittelte Protokoll bestätigt für `appbackup_2026-09-10_13-30-17_ff95c1ac` alle drei DB-Dumps, die durch Startsperren geschützte Kopie unter SELinux Enforcing, Dienstwiederaufnahme, OneDrive-Upload, vollständiges Rücklesen und die separate Remote-Restore-Prüfung von 14 Archiven. Der Installer meldete anschließend `SUCCESS` und einen aktiven Backup-Timer. Die frühere Abfrage von `result.txt` mit `RUNNING` lag vor diesem Abschluss. Der alte Lauf `appbackup_2026-09-10_13-12-34_43314337` mit zwischenzeitlichem Writer-Neustart wird dadurch nicht nachträglich als konsistent bestätigt. **Weiter offen:** Eine fachliche Wiederherstellung mit startenden Anwendungen auf einem separaten Testsystem, die vollständige Writer-/Pfadabdeckung sowie die Bewertung von Laufzeit und Speicherbedarf. `--verify-only` prüft den heruntergeladenen Sicherungssatz, verändert aber keine Anwendungen und ersetzt diese Wiederherstellungsprobe nicht. Der technische Paketlauf bestätigt auch nicht jede mögliche SELinux-/ACL-Konstellation. Die Entwicklungstests verändern keinen Produktivserver.