disaster-backup: Minecraft flush-not-stop, update descriptions

Minecraft instances are flushed to disk (save-all) and snapshotted while
the server keeps running — no player disruption. All other services stop
briefly for filesystem-consistent database snapshots, then restart.
Also update file header, install banner, schedule description, and README.

https://claude.ai/code/session_019XgsQ13XKm4Zj3cNsDNwHj
This commit is contained in:
Claude
2026-06-04 15:50:22 +00:00
parent 55d8d09e46
commit 9aa9b00b59
2 changed files with 58 additions and 32 deletions
+1 -1
View File
@@ -72,7 +72,7 @@ Update them any time with `sudo ./setup.sh configure`.
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
| `gaming` | `js99er`, `minecraft`, `wolf`, `wolf-pair` |
| `extras` | `silent-send`, `sync-cc` |
| `backup` | `backup` — Kopia (game saves/world data, no service downtime); `disaster-backup` — full `~/docker/<service>/` dirs (stop/snap/start, databases included) |
| `backup` | `backup` — Kopia (Minecraft world data + gaming saves, runs without stopping services); `disaster-backup` — complete recovery backup: entire `~/docker/<service>/` for every service (Minecraft: flush+snap, no downtime; all others: stop/snap/start for DB consistency) |
Run `./setup.sh --list` to see descriptions.
+57 -31
View File
@@ -1,17 +1,19 @@
#!/bin/bash
# services/disaster-backup.sh — Full Docker-service disaster-recovery backups.
# services/disaster-backup.sh — Complete disaster-recovery backup via Kopia.
# Part of the modular post-install system (sourced by setup.sh).
#
# Backs up entire ~/docker/<service>/ directories (stop → snapshot → start)
# with Kopia. Different services can go to different drives / Kopia repos.
# Backs up each entire ~/docker/<service>/ directory (compose file, config, data,
# databases — everything needed to restore from nothing). Per-service behaviour:
# Minecraft instances — flush world to disk (save-all), snapshot, no downtime
# All other services — stop, snapshot, restart (seconds of downtime each)
#
# Different services can be routed to different Kopia repos / drives.
# New services are auto-discovered on every run — no reconfiguration needed.
#
# Creates: ~/docker/disaster-backup/
# disaster-backup.conf settings + per-service destination map
# disaster-backup.sh worker (run directly or via systemd timer)
# restore/<dest>/ restore_kopia_backup.sh + backup.conf per destination
#
# Complementary to the 'backup' service (gaming saves). This one is the
# comprehensive "restore entire service from scratch" solution.
register_service disaster-backup backup "Disaster-recovery backup (full ~/docker service dirs, stop/start)"
@@ -31,13 +33,14 @@ install_disaster_backup() {
echo "║ Full ~/docker/<service>/ snapshots via Kopia ║"
echo "╚═══════════════════════════════════════════════════════╝"
echo ""
echo " Backs up each entire service directory — config, data, databases,"
echo " compose files, everything needed to restore a service from scratch."
echo " Each service is stopped briefly before snapshotting, then restarted,"
echo " ensuring consistent database state."
echo " Backs up each entire service directory — compose file, config, data,"
echo " databases, everything needed to restore a service from scratch."
echo ""
echo " Minecraft: world flushed to disk (save-all), snapshot, NO downtime."
echo " Everything else: stopped briefly, snapshotted, restarted."
echo ""
echo " Different services can go to different drives / Kopia repos."
echo " New services installed later are auto-detected on the next backup run."
echo " New services are auto-detected on every run — no reconfiguration needed."
echo ""
if [ "$DRY_RUN" = true ]; then
@@ -167,7 +170,8 @@ install_disaster_backup() {
echo " SCHEDULE & RETENTION"
echo "═══════════════════════════════════════════════════════"
echo ""
echo " Services are stopped briefly per snapshot — schedule for off-peak hours."
echo " Minecraft runs uninterrupted; other services stop briefly (seconds each)."
echo " Schedule for off-peak hours."
echo ""
echo " 1) Daily at 02:00 (recommended)"
echo " 2) Every 12 hours"
@@ -271,7 +275,9 @@ install_disaster_backup() {
cat > "$WORKER" << 'WORKEREOF'
#!/bin/bash
# Generated by the disaster-backup installer.
# Backs up full ~/docker/<service>/ directories via Kopia (stop → snap → start).
# Backs up full ~/docker/<service>/ directories via Kopia.
# Minecraft instances: flush to disk (save-all) then snapshot — no downtime.
# All other services: stop → snapshot → restart for consistency.
#
# sudo ./disaster-backup.sh run a full backup cycle
# sudo ./disaster-backup.sh snapshots list all snapshots (all repos)
@@ -305,6 +311,9 @@ dest_for_svc() {
echo "${!var:-${DEST_DEFAULT:-default}}"
}
# Detect itzg Minecraft instances by their Dockerfile signature.
is_minecraft() { [ -f "${1}Dockerfile" ] && grep -qs itzg "${1}Dockerfile"; }
case "${1:-run}" in
snapshots)
for dest in ${DEST_NAMES:-default}; do
@@ -332,26 +341,43 @@ for svc_dir in "$DOCKER_DIR"/*/; do
_repo_var="DEST_${dest}_REPO"
[ -n "${!_repo_var:-}" ] || { log "SKIP $svc — dest '$dest' not configured in conf"; continue; }
STOPPED=false
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$svc"; then
log "Stopping $svc..."
docker compose -f "${svc_dir}docker-compose.yml" down 2>/dev/null \
|| docker stop "$svc" 2>/dev/null \
|| log "WARNING: could not stop $svc — snapshotting live (consistency not guaranteed)"
STOPPED=true
fi
log "Snapshotting $svc (dest: $dest)..."
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
log "OK $svc"
if is_minecraft "$svc_dir"; then
# ── Minecraft: flush world to disk, snapshot without stopping ────────
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$svc"; then
log "Flushing Minecraft world '$svc' (save-all, no downtime)..."
docker exec "$svc" mc-send-to-console save-all flush 2>/dev/null \
|| docker exec "$svc" rcon-cli save-all 2>/dev/null || true
sleep 5
fi
log "Snapshotting $svc (dest: $dest)..."
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
log "OK $svc (Minecraft, no downtime)"
else
log "WARNING: snapshot failed for $svc"; rc=1
fi
else
log "WARNING: snapshot failed for $svc"; rc=1
fi
# ── All other services: stop → snapshot → restart ─────────────────
STOPPED=false
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$svc"; then
log "Stopping $svc..."
docker compose -f "${svc_dir}docker-compose.yml" down 2>/dev/null \
|| docker stop "$svc" 2>/dev/null \
|| log "WARNING: could not stop $svc — snapshotting live (consistency not guaranteed)"
STOPPED=true
fi
if [ "$STOPPED" = true ]; then
log "Starting $svc..."
docker compose -f "${svc_dir}docker-compose.yml" up -d 2>/dev/null \
|| log "WARNING: could not restart $svc — start it manually: docker compose -f ${svc_dir}docker-compose.yml up -d"
log "Snapshotting $svc (dest: $dest)..."
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
log "OK $svc"
else
log "WARNING: snapshot failed for $svc"; rc=1
fi
if [ "$STOPPED" = true ]; then
log "Starting $svc..."
docker compose -f "${svc_dir}docker-compose.yml" up -d 2>/dev/null \
|| log "WARNING: could not restart $svc — run: docker compose -f ${svc_dir}docker-compose.yml up -d"
fi
fi
done