diff --git a/README.md b/README.md index fef3e3c..784f248 100644 --- a/README.md +++ b/README.md @@ -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//` 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//` for every service (Minecraft: flush+snap, no downtime; all others: stop/snap/start for DB consistency) | Run `./setup.sh --list` to see descriptions. diff --git a/services/disaster-backup.sh b/services/disaster-backup.sh index 03d35f7..2ad2fcf 100644 --- a/services/disaster-backup.sh +++ b/services/disaster-backup.sh @@ -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// directories (stop → snapshot → start) -# with Kopia. Different services can go to different drives / Kopia repos. +# Backs up each entire ~/docker// 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// 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// 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// directories via Kopia (stop → snap → start). +# Backs up full ~/docker// 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