diff --git a/README.md b/README.md index 3550294..cc6b4fa 100644 --- a/README.md +++ b/README.md @@ -67,14 +67,16 @@ Cloud-Firewall, ohne die Zertifikatsprüfung zu brechen. ## Backup & Restore -Warm-Backup (laufender Betrieb, nächtlich per Cron des Users `rantanplan`): - -```bash -docker exec -u git -w /tmp thread-net-git-gitea-1 \ - gitea dump -c /data/gitea/conf/app.ini -f /tmp/gitea-dump.zip -docker cp thread-net-git-gitea-1:/tmp/gitea-dump.zip /opt/backup/gitea-dump-$(date +%F).zip -docker exec thread-net-git-gitea-1 rm /tmp/gitea-dump.zip -``` +Warm-Backup: nächtlich um 03:17 per Cron des Users `rantanplan` +(`backup/gitea-backup.sh`, Log: `/opt/backup/backup.log`). +**Derzeit pausiert** (Cron auskommentiert, seit 2026-07-30): die Backups +sollen off-host in ein Borg-Repo auf einer Storage Box, bis dahin bleibt +der letzte Dump vom 2026-07-30 liegen — siehe CFGMON-09 im Repo +`sorb/Backlogs`. Das Skript +stoppt kurz den Runner (SQLite-Journal-Race bei `gitea dump`), streamt +den Dump ohne Zwischenkopie (`gitea dump --type tar -f - | gzip`) und +behält **nur den neuesten Stand** auf dem Host — die 38-G-Platte trägt +nicht mehr. Manuell: einfach das Skript ausführen. Restore auf frischem Host: Volume `thread-net-git_gitea-data` anlegen, Dump-Zip entpacken (`gitea-repo.zip` → `/data/git/repositories`, @@ -84,7 +86,7 @@ https://docs.gitea.com/administration/backup-and-restore **Off-Host-Kopie nicht vergessen** — ein Backup auf demselben Host schützt nicht vor Plattenverlust: -`scp -P 2248 rantanplan@188.245.193.243:/opt/backup/gitea-dump-.zip .` +`scp -P 2248 rantanplan@188.245.193.243:/opt/backup/gitea-dump-.tar.gz .` ## Bezüge diff --git a/backup/gitea-backup.sh b/backup/gitea-backup.sh index 2abb717..6c6c8f0 100755 --- a/backup/gitea-backup.sh +++ b/backup/gitea-backup.sh @@ -7,14 +7,29 @@ set -eu BACKUP_DIR=/opt/backup CONTAINER=thread-net-git-gitea-1 -TARGET="$BACKUP_DIR/gitea-dump-$(date +%F).zip" +TARGET="$BACKUP_DIR/gitea-dump-$(date +%F).tar.gz" -docker exec "$CONTAINER" rm -f /tmp/gitea-dump.zip -docker exec -u git -w /tmp "$CONTAINER" gitea dump -c /data/gitea/conf/app.ini -f /tmp/gitea-dump.zip -docker cp -q "$CONTAINER:/tmp/gitea-dump.zip" "$TARGET.tmp" -docker exec "$CONTAINER" rm -f /tmp/gitea-dump.zip +# Runner pausieren: sein DB-Polling erzeugt sekuendlich SQLite-Journal- +# Dateien, an deren Verschwinden "gitea dump" beim Packen scheitert +# (stat gitea.db-journal: no such file). Start am Ende auch im Fehlerfall. +docker stop gitea-runner >/dev/null +trap 'docker start gitea-runner >/dev/null' EXIT + +# Dump als Stream (-f -): keine Zwischendatei im Container -- die Platte +# traegt nur EINE Dump-Kopie plus den alten Stand waehrend der Rotation. +ok="" +for attempt in 1 2 3; do + if docker exec -u git -w /tmp "$CONTAINER" gitea dump -c /data/gitea/conf/app.ini --type tar -f - 2>>"$BACKUP_DIR/backup.log" | gzip > "$TARGET.tmp"; then + ok=1 + break + fi + rm -f "$TARGET.tmp" + echo "$(date -Is) dump-Versuch $attempt fehlgeschlagen, retry..." + sleep 5 +done +[ -n "$ok" ] || { echo "$(date -Is) backup FEHLGESCHLAGEN nach 3 Versuchen"; exit 1; } mv "$TARGET.tmp" "$TARGET" -find "$BACKUP_DIR" -maxdepth 1 -name 'gitea-dump-*.zip' ! -name "$(basename "$TARGET")" -delete +find "$BACKUP_DIR" -maxdepth 1 -name 'gitea-dump-*' ! -name "$(basename "$TARGET")" -delete echo "$(date -Is) backup ok: $TARGET ($(du -h "$TARGET" | cut -f1))"