Files
ubuntu-post-install/services/gaming-backup.sh
T
Claude d4e06914e7 Add remote Caddy support — generate snippet files when Caddy is on another host
New site config key: CADDY_REMOTE_HOST (set via 'sudo ./setup.sh configure').
When set, configure_caddy_for_service operates in "remote" mode instead of
writing to a local Caddyfile:
- Upstream uses CADDY_REMOTE_HOST:PORT (host IP, not container name)
- Snippet saved to ~/docker/caddy-snippets/<subdomain>.caddy
- User is shown scp/rsync commands to copy it to the Caddy machine

Three modes in configure_caddy_for_service (lib/common.sh and inline stubs):
  local:  ~/docker/caddy/ exists → write Caddyfile + reload (existing behavior)
  remote: CADDY_REMOTE_HOST set → save snippet, print copy instructions
  none:   neither configured → silent return (unchanged)

All 31 service standalone bootstrap stubs updated with the new logic.
CADDY_REMOTE_HOST global added to all 42 standalone bootstrap sections.
setup.sh configure now prompts for CADDY_REMOTE_HOST with a clear explanation.
wolf.sh: add missing stubs (configure_caddy_for_service, write_readme,
  prompt_yn, ensure_docker_dir_ownership) and the Authelia/Caddy/start calls
  that were missing from the install function.

https://claude.ai/code/session_014CCYqVwW6d6f5dw1qRokYt
2026-06-08 21:10:37 +00:00

491 lines
26 KiB
Bash

#!/bin/bash
# services/gaming-backup.sh — Frequent gaming-saves backup (no service downtime).
# Part of the modular post-install system (sourced by setup.sh).
#
# Can also be run standalone on any machine:
# sudo bash gaming-backup.sh
# (Docker must already be installed when run standalone)
#
# Backs up the things you can't re-download — progress, saved games, user data:
# • Minecraft worlds / player data (every <id>/data instance under $DOCKER_DIR)
# • Emulator saves & save states ($GAME_STORAGE_DIR/saves) [gaming box]
# • ES-DE scraped artwork ($GAME_STORAGE_DIR/media) [gaming box]
# • Steam user data & game saves ($GAME_STORAGE_DIR/steam) [gaming box]
# • Wolf state (/etc/wolf — config + profile_data) [gaming box]
#
# Unlike the 'backup' service, nothing is stopped — Minecraft worlds are flushed
# to disk (save-all) and snapshotted live. Run this hourly or every few hours for
# frequent save protection; use 'backup' nightly for full service recovery.
#
# It does NOT back up ROMs or Steam game installs — those are re-downloadable.
#
# Safe to re-run: it reconnects to an existing repository and refreshes the
# config, policies, worker script and timer.
# ── Standalone bootstrap ──────────────────────────────────────────────────────
# Detected when the script is executed directly rather than sourced by setup.sh.
# Sets up helpers and globals, then defers execution until after the function
# definition at the bottom of this file.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
[[ "$(id -u)" == "0" ]] || { echo "Run with sudo: sudo bash $0"; exit 1; }
_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_COMMON="$_SELF_DIR/../lib/common.sh"
if [[ -f "$_COMMON" ]]; then
# Full repo present — use the real helpers (picks up ~/docker/.config too)
# shellcheck source=../lib/common.sh
source "$_COMMON"
else
# One-off copy — inline minimal stubs so the script works without the repo
log_info() { echo -e "\033[0;34m[INFO]\033[0m $*"; }
log_success() { echo -e "\033[0;32m[OK]\033[0m $*"; }
log_warning() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
log_error() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; }
ensure_docker_dir_ownership() {
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$@" 2>/dev/null || true
}
# Match common.sh's eval-based pattern so local vars in install_* are set correctly
prompt_text() {
local _q="$1" _def="$2" _var="$3" _r
[[ "${UNATTENDED:-false}" == "true" ]] && { eval "$_var='$_def'"; return; }
read -r -p " $_q " _r
eval "$_var='${_r:-$_def}'"
}
prompt_yn() {
local _q="$1" _def="$2" _var="$3" _r
[[ "${UNATTENDED:-false}" == "true" ]] && { eval "$_var='$_def'"; return; }
read -r -p " $_q " _r
eval "$_var='${_r:-$_def}'"
}
generate_password() {
local _len="${1:-32}"
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "$_len"
}
fi
# Globals — ACTUAL_USER/ACTUAL_HOME must come before DOCKER_DIR
# ($HOME under sudo is /root, not the real user's home)
ACTUAL_USER="${ACTUAL_USER:-${SUDO_USER:-$USER}}"
ACTUAL_HOME="$(getent passwd "$ACTUAL_USER" 2>/dev/null | cut -d: -f6 || echo "${HOME:-/root}")"
DOCKER_DIR="${DOCKER_DIR:-$ACTUAL_HOME/docker}"
DRY_RUN="${DRY_RUN:-false}"
UNATTENDED="${UNATTENDED:-false}"
SITE_TZ="${SITE_TZ:-$(cat /etc/timezone 2>/dev/null || echo UTC)}"
SITE_DOMAIN="${SITE_DOMAIN:-example.com}"
SITE_CADDY_NET="${SITE_CADDY_NET:-caddy_net}"
CADDY_REMOTE_HOST="${CADDY_REMOTE_HOST:-}"
register_service() { :; } # no-op — no wizard to register into
_RUN_STANDALONE=1
fi
# ─────────────────────────────────────────────────────────────────────────────
register_service gaming-backup backup "Gaming saves backup (Minecraft worlds, emulator saves, Steam)"
install_gaming_backup() {
log_info "Setting up gaming saves backup (Kopia)..."
# ── Repo-conventional paths ──────────────────────────────────────────────
local BACKUP_DIR="$DOCKER_DIR/gaming-backup"
local CONF_FILE="$BACKUP_DIR/backup.conf" # editable settings
local WORKER="$BACKUP_DIR/backup_gaming.sh" # worker script
local KOPIA_CONFIG="/etc/gaming-backup/repository.config"
local CACHE_DIR="/var/cache/gaming-backup"
local SVC_NAME="post-install-gaming-backup"
local DEFAULT_REPO="$ACTUAL_HOME/backups/gaming-kopia"
echo ""
echo "╔═══════════════════════════════════════════════════════╗"
echo "║ Gaming Backup Setup · Kopia ║"
echo "║ Minecraft worlds · game saves · user data ║"
echo "╚═══════════════════════════════════════════════════════╝"
echo ""
echo " Backs up progress / saves / user data — NOT ROMs or game installs."
echo " Nothing is stopped: Minecraft worlds are flushed to disk then snapshotted."
echo " Run frequently (hourly or every few hours) without disrupting gameplay."
echo " Use the 'backup' service for full service recovery (nightly)."
echo ""
# ── DRY-RUN ──────────────────────────────────────────────────────────────
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would install Kopia from the official apt repository"
echo "[DRY-RUN] Would create $BACKUP_DIR (owned by $ACTUAL_USER)"
echo "[DRY-RUN] Would create/connect a Kopia repository at $DEFAULT_REPO"
echo "[DRY-RUN] Would write config $CONF_FILE and worker $WORKER"
echo "[DRY-RUN] Would install systemd service+timer $SVC_NAME (cron fallback)"
echo "[DRY-RUN] Would optionally run the first backup"
return 0
fi
# ── 1. Ensure Kopia is installed ─────────────────────────────────────────
if ! command -v kopia >/dev/null 2>&1; then
log_info "Kopia not found — installing from the official apt repository..."
if command -v apt-get >/dev/null 2>&1; then
install -d -m 0755 /etc/apt/keyrings
if curl -fsSL https://kopia.io/signing-key \
| gpg --dearmor --yes -o /etc/apt/keyrings/kopia-keyring.gpg; then
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" \
> /etc/apt/sources.list.d/kopia.list
apt-get update -y && apt-get install -y kopia
fi
fi
fi
if ! command -v kopia >/dev/null 2>&1; then
log_error "Kopia is still not installed."
echo " Install it manually, then re-run this service:"
echo " https://kopia.io/docs/installation/"
return 1
fi
local KOPIA_BIN; KOPIA_BIN="$(command -v kopia)"
log_success "Kopia: $("$KOPIA_BIN" --version 2>/dev/null | head -1)"
mkdir -p "$BACKUP_DIR" || return 1
ensure_docker_dir_ownership "$BACKUP_DIR"
# ── 2. What to back up ───────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " WHAT TO BACK UP"
echo "═══════════════════════════════════════════════════════"
echo ""
local DEFAULT_MCBASE="$DOCKER_DIR"
echo " Each Minecraft instance's world is backed up from its <id>/data folder;"
echo " all instances under this folder are detected automatically."
echo " (type 'none' to exclude Minecraft)"
local MC_BASE_DIR=""
prompt_text " Folder containing Minecraft instance(s) [${DEFAULT_MCBASE}]:" "$DEFAULT_MCBASE" MC_BASE_DIR
if [ "$MC_BASE_DIR" = none ]; then
MC_BASE_DIR=""
else
MC_BASE_DIR="${MC_BASE_DIR/#\~/$ACTUAL_HOME}"; MC_BASE_DIR="${MC_BASE_DIR%/}"
local _found=() d
for d in "$MC_BASE_DIR"/*/; do
[ -f "${d}Dockerfile" ] && grep -qs itzg "${d}Dockerfile" && [ -d "${d}data" ] \
&& _found+=("$(basename "$d")")
done
if [ "${#_found[@]}" -gt 0 ]; then
log_success " Detected Minecraft instance(s): ${_found[*]}"
else
log_warning " No instances detected yet under $MC_BASE_DIR — picked up once created."
fi
fi
# ── Optional gaming-box sources ──────────────────────────────────────────
echo ""
echo " The following are only relevant on a gaming box (Wolf + emulators +"
echo " Steam). On a plain homelab server you can leave them disabled."
local DEFAULT_STORAGE="$ACTUAL_HOME/drives/games"
local GAME_STORAGE_DIR=""
prompt_text " Game storage dir (ROMs/Steam/saves live here) [${DEFAULT_STORAGE}]:" "$DEFAULT_STORAGE" GAME_STORAGE_DIR
GAME_STORAGE_DIR="${GAME_STORAGE_DIR/#\~/$ACTUAL_HOME}"
GAME_STORAGE_DIR="${GAME_STORAGE_DIR%/}"
echo ""
local _a=""
prompt_yn " Back up emulator saves ($GAME_STORAGE_DIR/saves)? (y/N):" "n" _a
local BACKUP_SAVES; BACKUP_SAVES=$([[ "$_a" =~ ^[Yy]$ ]] && echo yes || echo no)
prompt_yn " Back up Steam user data/saves (game installs excluded)? (y/N):" "n" _a
local BACKUP_STEAM; BACKUP_STEAM=$([[ "$_a" =~ ^[Yy]$ ]] && echo yes || echo no)
prompt_yn " Back up ES-DE scraped artwork ($GAME_STORAGE_DIR/media)? (y/N):" "n" _a
local BACKUP_MEDIA; BACKUP_MEDIA=$([[ "$_a" =~ ^[Yy]$ ]] && echo yes || echo no)
echo ""
echo " /etc/wolf includes pairing/config AND profile_data — where Wolf stores"
echo " every app's home dir (ES-DE settings, controller mappings, RetroArch"
echo " saves & save states, standalone-emulator saves)."
prompt_yn " Back up Wolf state (/etc/wolf)? (y/N):" "n" _a
local BACKUP_WOLF; BACKUP_WOLF=$([[ "$_a" =~ ^[Yy]$ ]] && echo yes || echo no)
local WOLF_STATE_DIR="/etc/wolf"
# ── 3. Repository location ────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " BACKUP REPOSITORY (local)"
echo "═══════════════════════════════════════════════════════"
echo ""
echo " Backups are stored in a local Kopia repository. You can mirror it to"
echo " another computer or the cloud later (see backup.conf, REMOTE_* lines)."
echo " Put it on a DIFFERENT drive from your data if you can."
echo ""
local REPO_DIR=""
prompt_text " Repository path [${DEFAULT_REPO}]:" "$DEFAULT_REPO" REPO_DIR
REPO_DIR="${REPO_DIR/#\~/$ACTUAL_HOME}"
REPO_DIR="${REPO_DIR%/}"
echo ""
echo " The repository is encrypted. A strong password is generated and stored"
echo " in backup.conf (root-only). KEEP A COPY — without it backups cannot be"
echo " restored, even by you."
echo ""
local KOPIA_PASSWORD=""
if [ "$UNATTENDED" = true ]; then
echo " [auto] Generating a random repository password."
else
read -rsp " Repository password [Enter = auto-generate]: " KOPIA_PASSWORD; echo
fi
if [ -z "$KOPIA_PASSWORD" ]; then
KOPIA_PASSWORD="$(generate_password 32)"
log_info " Generated a random repository password (saved in backup.conf)."
fi
# ── 4. Retention + schedule ───────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " SCHEDULE & RETENTION"
echo "═══════════════════════════════════════════════════════"
echo ""
echo " 1) Daily at 03:00"
echo " 2) Every 6 hours"
echo " 3) Hourly (recommended for active gaming)"
echo " 4) Custom (systemd OnCalendar)"
echo ""
local _sch=""
prompt_text " How often? [3]:" "3" _sch
local ONCALENDAR SCHED_LABEL
case "${_sch:-3}" in
1) ONCALENDAR="*-*-* 03:00:00"; SCHED_LABEL="daily at 03:00" ;;
2) ONCALENDAR="*-*-* 00,06,12,18:00:00"; SCHED_LABEL="every 6 hours" ;;
4) prompt_text " OnCalendar expression:" "hourly" ONCALENDAR; SCHED_LABEL="$ONCALENDAR" ;;
*) ONCALENDAR="hourly"; SCHED_LABEL="hourly" ;;
esac
echo ""
local KEEP_LATEST=""
prompt_text " How many recent snapshots to keep (latest)? [24]:" "24" KEEP_LATEST
local KEEP_DAILY=7 KEEP_WEEKLY=4 KEEP_MONTHLY=6
# ── Notifications (ntfy) ─────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " NOTIFICATIONS (optional)"
echo "═══════════════════════════════════════════════════════"
echo ""
echo " Receive a push notification after every backup (and on failures)."
echo " Uses ntfy — free and self-hostable. Create a topic at https://ntfy.sh"
echo ""
local NTFY_URL="" NTFY_TOKEN=""
prompt_text " ntfy topic URL (blank to skip):" "" NTFY_URL
if [ -n "$NTFY_URL" ]; then
prompt_text " ntfy access token (blank if public/no auth):" "" NTFY_TOKEN
fi
# ── 5. Write backup.conf ──────────────────────────────────────────────────
log_info "Writing $CONF_FILE ..."
tee "$CONF_FILE" >/dev/null << CONFEOF
# ── gaming-backup config (read by gaming-backup.sh) ──────────────────────────
# Generated on $(date '+%F %T'). Safe to hand-edit.
KOPIA="$KOPIA_BIN"
KOPIA_CONFIG="$KOPIA_CONFIG"
KOPIA_CACHE_DIR="$CACHE_DIR"
# Repository encryption password — KEEP A COPY somewhere safe.
KOPIA_PASSWORD='$KOPIA_PASSWORD'
# ── Sources (progress / saves / user data only) ──────────────────────────────
# MC_BASE_DIR holds Minecraft instances; every <id>/data with an itzg Dockerfile
# is snapshotted automatically (covers multi-server setups).
MC_BASE_DIR="$MC_BASE_DIR"
GAME_STORAGE_DIR="$GAME_STORAGE_DIR"
WOLF_STATE_DIR="$WOLF_STATE_DIR"
BACKUP_SAVES="$BACKUP_SAVES" # \$GAME_STORAGE_DIR/saves
BACKUP_STEAM="$BACKUP_STEAM" # \$GAME_STORAGE_DIR/steam (game installs excluded by policy)
BACKUP_MEDIA="$BACKUP_MEDIA" # \$GAME_STORAGE_DIR/media (ES-DE scraped artwork)
BACKUP_WOLF="$BACKUP_WOLF" # /etc/wolf — config + profile_data
# ── Optional offsite mirror ──────────────────────────────────────────────────
# Mirror the WHOLE repository to another computer or the cloud after each run.
# Leave REMOTE_TYPE=none to stay local-only.
#
# SFTP: REMOTE_TYPE="sftp" REMOTE_ARGS="--host H --username U --path /srv/..."
# B2: REMOTE_TYPE="b2" REMOTE_ARGS="--bucket B --key-id K --key A"
# S3: REMOTE_TYPE="s3" REMOTE_ARGS="--bucket B --endpoint E --access-key K --secret-access-key S"
# rclone: REMOTE_TYPE="rclone" REMOTE_ARGS="--remote-path myremote:gaming-kopia"
#
REMOTE_TYPE="none"
REMOTE_ARGS=""
# ── Notifications (ntfy) ─────────────────────────────────────────────────────
# Set NTFY_URL to receive backup success/failure alerts.
# Leave blank to disable. NTFY_TOKEN is optional (for private topics).
NTFY_URL="$NTFY_URL"
NTFY_TOKEN="$NTFY_TOKEN"
CONFEOF
chown root:root "$CONF_FILE" 2>/dev/null || true
chmod 600 "$CONF_FILE"
log_success "backup.conf written (chmod 600 — contains the repo password)"
# ── 6. Create / connect the repository, set policies ─────────────────────
log_info "Preparing repository at $REPO_DIR ..."
mkdir -p "$REPO_DIR" "$CACHE_DIR" "$(dirname "$KOPIA_CONFIG")"
kp() { env KOPIA_PASSWORD="$KOPIA_PASSWORD" "$KOPIA_BIN" --config-file="$KOPIA_CONFIG" "$@"; }
if kp repository status >/dev/null 2>&1; then
log_success "Already connected to a repository."
elif test -e "$REPO_DIR/kopia.repository.f"; then
log_info "Existing repository found — connecting..."
kp repository connect filesystem --path="$REPO_DIR" --cache-directory="$CACHE_DIR" \
|| { log_error "Failed to connect to existing repository."; return 1; }
log_success "Connected to existing repository."
else
log_info "Creating new repository..."
kp repository create filesystem --path="$REPO_DIR" --cache-directory="$CACHE_DIR" \
|| { log_error "Failed to create repository."; return 1; }
log_success "Repository created."
fi
log_info "Applying global policy (zstd compression + retention)..."
kp policy set --global --compression=zstd >/dev/null
kp policy set --global \
--keep-latest="$KEEP_LATEST" \
--keep-daily="$KEEP_DAILY" \
--keep-weekly="$KEEP_WEEKLY" \
--keep-monthly="$KEEP_MONTHLY" \
--keep-annual=0 --keep-hourly=0 >/dev/null
log_success "Retention: keep latest $KEEP_LATEST, $KEEP_DAILY daily, $KEEP_WEEKLY weekly, $KEEP_MONTHLY monthly"
if [ "$BACKUP_STEAM" = yes ]; then
log_info "Setting Steam ignore rules (excluding game installs, keeping saves)..."
kp policy set "$GAME_STORAGE_DIR/steam" \
--add-ignore='**/steamapps/common' \
--add-ignore='**/steamapps/downloading' \
--add-ignore='**/steamapps/shadercache' \
--add-ignore='**/steamapps/temp' \
--add-ignore='**/steamapps/workshop' \
--add-ignore='**/depotcache' >/dev/null 2>&1 \
|| log_warning "Could not pre-set Steam ignore policy (applies on first snapshot)."
fi
# ── 7. Install the worker script ─────────────────────────────────────────
log_info "Installing worker $WORKER ..."
cp "${HERE:-}/extras/backup_gaming.sh" "$WORKER"
chmod +x "$WORKER"
chown root:root "$WORKER" 2>/dev/null || true
log_success "backup_gaming.sh installed"
# ── Copy the interactive restore script ───────────────────────────────────
local RESTORE_SRC="${HERE:-}/extras/restore_kopia.sh"
local RESTORE_DEST="$BACKUP_DIR/restore_gaming.sh"
if [ -f "$RESTORE_SRC" ]; then
cp "$RESTORE_SRC" "$RESTORE_DEST"
chmod +x "$RESTORE_DEST"
chown root:root "$RESTORE_DEST" 2>/dev/null || true
log_success "restore_kopia.sh installed"
else
log_warning "extras/restore_kopia.sh not found — restore script not installed"
fi
# ── Install test script ───────────────────────────────────────────────────
local TEST_SCRIPT="$BACKUP_DIR/test_backup.sh"
local TEST_SRC="${HERE:-}/extras/test_backup.sh"
if [ -f "$TEST_SRC" ]; then
cp "$TEST_SRC" "$TEST_SCRIPT"
chmod +x "$TEST_SCRIPT"
chown root:root "$TEST_SCRIPT" 2>/dev/null || true
log_success "test_backup.sh installed"
else
log_warning "extras/test_backup.sh not found — test script not installed"
fi
# ── 8. Install systemd timer (fallback: cron) ────────────────────────────
local AUTORUN=""
if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
log_info "Installing systemd service + timer ($SCHED_LABEL)..."
tee "/etc/systemd/system/${SVC_NAME}.service" >/dev/null << UNITEOF
[Unit]
Description=Post-install gaming backup (Minecraft world + game saves via Kopia)
After=docker.service network-online.target
Wants=docker.service
[Service]
Type=oneshot
ExecStart=/bin/bash $WORKER run
UNITEOF
tee "/etc/systemd/system/${SVC_NAME}.timer" >/dev/null << UNITEOF
[Unit]
Description=Schedule gaming backup ($SCHED_LABEL)
[Timer]
OnCalendar=$ONCALENDAR
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
UNITEOF
systemctl daemon-reload
systemctl enable --now "${SVC_NAME}.timer"
log_success "Timer enabled: $SCHED_LABEL"
AUTORUN="systemctl list-timers ${SVC_NAME}.timer"
else
log_warning "systemd not detected — installing a cron job instead."
local CRON
case "${_sch:-3}" in
1) CRON="0 3 * * *" ;;
2) CRON="0 0,6,12,18 * * *" ;;
*) CRON="0 * * * *" ;;
esac
echo "$CRON root /bin/bash $WORKER run >> /var/log/${SVC_NAME}.log 2>&1" \
> "/etc/cron.d/${SVC_NAME}"
log_success "Cron job installed: $CRON"
AUTORUN="cat /etc/cron.d/${SVC_NAME}"
fi
# ── 9. First backup now? ─────────────────────────────────────────────────
echo ""
local _now=""
prompt_yn " Run the first gaming backup now? (Y/n):" "y" _now
if [[ ! "$_now" =~ ^[Nn]$ ]]; then
/bin/bash "$WORKER" run || log_warning "First backup reported warnings — check the output above."
fi
# ── Summary ──────────────────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " GAMING BACKUP CONFIGURED"
echo "═══════════════════════════════════════════════════════"
echo ""
echo " Repository : $REPO_DIR (encrypted, dedup + zstd)"
echo " Schedule : $SCHED_LABEL"
echo " Config : $CONF_FILE"
echo " Worker : $WORKER"
echo " Backing up :"
[ -n "$MC_BASE_DIR" ] && echo " • Minecraft worlds $MC_BASE_DIR/*/data (all instances)"
[ "$BACKUP_SAVES" = yes ] && echo " • Emulator saves $GAME_STORAGE_DIR/saves"
[ "$BACKUP_STEAM" = yes ] && echo " • Steam user data $GAME_STORAGE_DIR/steam (game installs excluded)"
[ "$BACKUP_MEDIA" = yes ] && echo " • ES-DE scraped art $GAME_STORAGE_DIR/media"
[ "$BACKUP_WOLF" = yes ] && echo " • Wolf state $WOLF_STATE_DIR (config + profile_data)"
echo " NOT backed up: ROMs, Steam game installs (re-downloadable)."
echo ""
echo " Commands:"
echo " sudo $WORKER back up now"
echo " sudo $WORKER snapshots list snapshots"
echo " sudo $RESTORE_DEST interactive restore"
echo " sudo $RESTORE_DEST --list list all snapshot sources"
echo ""
echo " Backup test (restore latest, compare, restore-back):"
echo " sudo $TEST_SCRIPT test most recent backup"
echo " sudo $TEST_SCRIPT --list list testable sources"
echo " sudo $TEST_SCRIPT <source> test a specific source"
[ -n "${NTFY_URL:-}" ] && echo "" && echo " Notifications: $NTFY_URL"
echo ""
echo " $AUTORUN"
echo ""
echo " Tip: also install the 'backup' service for nightly full-service recovery."
echo ""
log_warning "Save your repository password (in backup.conf) somewhere safe —"
log_warning "without it the encrypted backups cannot be restored."
echo ""
log_success "Gaming backup configured."
}
# Run immediately when executed directly (deferred until after function definition)
[[ "${_RUN_STANDALONE:-0}" == 1 ]] && install_gaming_backup