#!/usr/bin/env bash # Run on the Bazzite box BEFORE wiping it. Archives your home folder to server-marvin as # tar.zst files, then reads every archive back to prove it's intact. # # scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup- # scripts/backup.sh /some/other/dir # any directory NOT on the disk you're about to wipe # # Re-running skips archives that already finished (FORCE=1 redoes them). # Close Steam, Firefox, Discord and Claude first — files that change mid-read get a warning. set -euo pipefail SHARE=/var/mnt/server-marvin-personal DEST="${1:-$SHARE/desktop-backup-$(date +%F)}" step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; } warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } # -- Make sure the backup lands somewhere that survives the reinstall ------------------ if [[ $DEST == "$SHARE"/* ]]; then ls "$SHARE" >/dev/null 2>&1 || true # poke the automount mountpoint -q "$SHARE" || die "$SHARE isn't mounted — is server-marvin up?" fi mkdir -p "$DEST" disk_of() { findmnt -n -o SOURCE -T "$1" | sed 's/\[.*//'; } [[ $(disk_of "$DEST") != "$(disk_of "$HOME")" ]] \ || die "$DEST is on the same disk as your home — that's the disk Ubuntu will wipe" echo "Backing up $HOME -> $DEST" df -h "$DEST" | tail -1 # -- Archive helper --------------------------------------------------------------------- archive() { # archive local name=$1 out="$DEST/$1.tar.zst"; shift step "$name" if [[ -s $out && -z ${FORCE:-} ]]; then echo "already done, skipping"; return; fi local rc=0 tar --create --file="$out.partial" --use-compress-program='zstd -T0 -3' \ --directory="$HOME" \ --checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \ "$@" || rc=$? echo # GNU tar exits 1 when a file changed while it was being read (an app was still open). # Everything else still made it in, so keep the archive; anything above 1 is fatal. (( rc <= 1 )) || die "tar failed on $name (exit $rc)" (( rc == 0 )) || warn "$name: some files changed while being read — close apps and FORCE=1 to redo if it matters" mv "$out.partial" "$out" ls -lh "$out" | awk '{print " " $5}' } # Big, self-contained folders get their own archive so they can be restored (or skipped) # separately. Everything else goes in home.tar.zst. archive home \ --anchored \ --exclude=./.cache \ --exclude=./.local/share/Trash \ --exclude=./.local/share/Steam \ --exclude=./.steam \ --exclude=./.local/share/containers \ --exclude=./.local/share/flatpak \ --exclude=./.local/share/baloo \ --exclude='./.var/app/*/cache' \ --exclude='./.claude.json.tmp.*' \ --exclude=./.xlcore \ --exclude=./ComfyUI \ --no-anchored \ --exclude=node_modules \ --exclude=__pycache__ \ . # FFXIV game install (~124 GB) — Square's patch servers make a redownload take hours. [[ -d $HOME/.xlcore ]] && archive xlcore ./.xlcore # ComfyUI minus its venv (rebuilt on Ubuntu); this is ~110 GB of models. [[ -d $HOME/ComfyUI ]] && archive comfyui \ --anchored --exclude=./ComfyUI/venv --exclude=./ComfyUI/.venv \ --no-anchored --exclude=__pycache__ \ ./ComfyUI # Steam: only saves and settings. Games get redownloaded; the ones on the Data drive stay put. archive steam-saves \ ./.local/share/Steam/userdata \ ./.local/share/Steam/steamapps/compatdata # -- Reference info for the rebuild ------------------------------------------------------- step "manifest" m="$DEST/manifest"; mkdir -p "$m" flatpak list --app --columns=application,origin,installation > "$m/flatpaks.txt" 2>/dev/null || true code --list-extensions > "$m/vscode-extensions.txt" 2>/dev/null || true brew leaves > "$m/brew-leaves.txt" 2>/dev/null || true rpm-ostree status > "$m/rpm-ostree-status.txt" 2>/dev/null || true cp /etc/fstab "$m/fstab" cp -r "$HOME/.local/share/flatpak/overrides" "$m/flatpak-overrides-user" 2>/dev/null || true cp -r /var/lib/flatpak/overrides "$m/flatpak-overrides-system" 2>/dev/null || true lsblk -f > "$m/disks.txt" ls "$HOME/.local/share/Steam/steamapps/common" > "$m/steam-games-home.txt" 2>/dev/null || true ls /run/media/"$USER"/Data/SteamLibrary/steamapps/common > "$m/steam-games-data-drive.txt" 2>/dev/null || true ls "$m" # -- Prove every archive reads back cleanly ---------------------------------------------- if [[ -z ${SKIP_VERIFY:-} ]]; then for f in "$DEST"/*.tar.zst; do step "verify $(basename "$f")" tar --use-compress-program=zstd --list --file="$f" > /dev/null || die "$f is corrupt — rerun with FORCE=1" echo "ok" done fi step "done" du -sh "$DEST"/*.tar.zst echo "Backup is in: $DEST" echo "Safe to wipe the 990 PRO once you've eyeballed that list."