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:
@@ -72,7 +72,7 @@ Update them any time with `sudo ./setup.sh configure`.
|
|||||||
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
|
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
|
||||||
| `gaming` | `js99er`, `minecraft`, `wolf`, `wolf-pair` |
|
| `gaming` | `js99er`, `minecraft`, `wolf`, `wolf-pair` |
|
||||||
| `extras` | `silent-send`, `sync-cc` |
|
| `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.
|
Run `./setup.sh --list` to see descriptions.
|
||||||
|
|
||||||
|
|||||||
+57
-31
@@ -1,17 +1,19 @@
|
|||||||
#!/bin/bash
|
#!/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).
|
# Part of the modular post-install system (sourced by setup.sh).
|
||||||
#
|
#
|
||||||
# Backs up entire ~/docker/<service>/ directories (stop → snapshot → start)
|
# Backs up each entire ~/docker/<service>/ directory (compose file, config, data,
|
||||||
# with Kopia. Different services can go to different drives / Kopia repos.
|
# 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/
|
# Creates: ~/docker/disaster-backup/
|
||||||
# disaster-backup.conf settings + per-service destination map
|
# disaster-backup.conf settings + per-service destination map
|
||||||
# disaster-backup.sh worker (run directly or via systemd timer)
|
# disaster-backup.sh worker (run directly or via systemd timer)
|
||||||
# restore/<dest>/ restore_kopia_backup.sh + backup.conf per destination
|
# 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)"
|
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 "║ Full ~/docker/<service>/ snapshots via Kopia ║"
|
||||||
echo "╚═══════════════════════════════════════════════════════╝"
|
echo "╚═══════════════════════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Backs up each entire service directory — config, data, databases,"
|
echo " Backs up each entire service directory — compose file, config, data,"
|
||||||
echo " compose files, everything needed to restore a service from scratch."
|
echo " databases, everything needed to restore a service from scratch."
|
||||||
echo " Each service is stopped briefly before snapshotting, then restarted,"
|
echo ""
|
||||||
echo " ensuring consistent database state."
|
echo " Minecraft: world flushed to disk (save-all), snapshot, NO downtime."
|
||||||
|
echo " Everything else: stopped briefly, snapshotted, restarted."
|
||||||
echo ""
|
echo ""
|
||||||
echo " Different services can go to different drives / Kopia repos."
|
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 ""
|
echo ""
|
||||||
|
|
||||||
if [ "$DRY_RUN" = true ]; then
|
if [ "$DRY_RUN" = true ]; then
|
||||||
@@ -167,7 +170,8 @@ install_disaster_backup() {
|
|||||||
echo " SCHEDULE & RETENTION"
|
echo " SCHEDULE & RETENTION"
|
||||||
echo "═══════════════════════════════════════════════════════"
|
echo "═══════════════════════════════════════════════════════"
|
||||||
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 ""
|
||||||
echo " 1) Daily at 02:00 (recommended)"
|
echo " 1) Daily at 02:00 (recommended)"
|
||||||
echo " 2) Every 12 hours"
|
echo " 2) Every 12 hours"
|
||||||
@@ -271,7 +275,9 @@ install_disaster_backup() {
|
|||||||
cat > "$WORKER" << 'WORKEREOF'
|
cat > "$WORKER" << 'WORKEREOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Generated by the disaster-backup installer.
|
# 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 run a full backup cycle
|
||||||
# sudo ./disaster-backup.sh snapshots list all snapshots (all repos)
|
# sudo ./disaster-backup.sh snapshots list all snapshots (all repos)
|
||||||
@@ -305,6 +311,9 @@ dest_for_svc() {
|
|||||||
echo "${!var:-${DEST_DEFAULT:-default}}"
|
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
|
case "${1:-run}" in
|
||||||
snapshots)
|
snapshots)
|
||||||
for dest in ${DEST_NAMES:-default}; do
|
for dest in ${DEST_NAMES:-default}; do
|
||||||
@@ -332,26 +341,43 @@ for svc_dir in "$DOCKER_DIR"/*/; do
|
|||||||
_repo_var="DEST_${dest}_REPO"
|
_repo_var="DEST_${dest}_REPO"
|
||||||
[ -n "${!_repo_var:-}" ] || { log "SKIP $svc — dest '$dest' not configured in conf"; continue; }
|
[ -n "${!_repo_var:-}" ] || { log "SKIP $svc — dest '$dest' not configured in conf"; continue; }
|
||||||
|
|
||||||
STOPPED=false
|
if is_minecraft "$svc_dir"; then
|
||||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$svc"; then
|
# ── Minecraft: flush world to disk, snapshot without stopping ────────
|
||||||
log "Stopping $svc..."
|
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$svc"; then
|
||||||
docker compose -f "${svc_dir}docker-compose.yml" down 2>/dev/null \
|
log "Flushing Minecraft world '$svc' (save-all, no downtime)..."
|
||||||
|| docker stop "$svc" 2>/dev/null \
|
docker exec "$svc" mc-send-to-console save-all flush 2>/dev/null \
|
||||||
|| log "WARNING: could not stop $svc — snapshotting live (consistency not guaranteed)"
|
|| docker exec "$svc" rcon-cli save-all 2>/dev/null || true
|
||||||
STOPPED=true
|
sleep 5
|
||||||
fi
|
fi
|
||||||
|
log "Snapshotting $svc (dest: $dest)..."
|
||||||
log "Snapshotting $svc (dest: $dest)..."
|
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
|
||||||
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
|
log "OK $svc (Minecraft, no downtime)"
|
||||||
log "OK $svc"
|
else
|
||||||
|
log "WARNING: snapshot failed for $svc"; rc=1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log "WARNING: snapshot failed for $svc"; rc=1
|
# ── All other services: stop → snapshot → restart ─────────────────
|
||||||
fi
|
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 "Snapshotting $svc (dest: $dest)..."
|
||||||
log "Starting $svc..."
|
if kp_for "$dest" snapshot create --description="disaster: $svc" "$svc_dir"; then
|
||||||
docker compose -f "${svc_dir}docker-compose.yml" up -d 2>/dev/null \
|
log "OK $svc"
|
||||||
|| log "WARNING: could not restart $svc — start it manually: docker compose -f ${svc_dir}docker-compose.yml up -d"
|
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
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user