#!/bin/bash # # Purpose : Back up your home folder with restic to your Nextcloud files, in # .backup-. Run daily by coredesktop-backup.timer; # `configure backup` sets it up. # # Keeps 7 daily, 4 weekly and 6 monthly backups. To leave out more, list # paths or patterns in ~/.config/coredesktop/backup-exclude, one per line. # Wrapped in { ...; exit; } so bash reads the whole script up front; update # replaces this file (git pull) while a backup may be running. { COREDESKTOP="$HOME/.local/share/coredesktop" USER_EXCLUDE="$HOME/.config/coredesktop/backup-exclude" LOG_HINT="See: journalctl --user -u coredesktop-backup" # Tell the desktop when a backup fails, as no one watches the timer fail() { echo "$1" >&2 notify-send --app-name=CoreDesktop --icon=dialog-error "Backup failed" "$1" 2> /dev/null exit 1 } source "$COREDESKTOP/system/backup-env.sh" || exit 1 [ -n "$RESTIC_REPOSITORY" ] || fail "Backups aren't set up. Run: configure backup" [ -n "$RESTIC_PASSWORD_COMMAND" ] || fail "The backup's passphrase isn't in your keyring. Run: configure backup" # Run at login, perhaps before the network is up; give it a few minutes for try in {1..10}; do curl -sf --max-time 10 -o /dev/null "${BACKUP_WEBDAV%%/remote.php/*}/status.php" && break sleep 30 done # Rebuilt by restic, or by reinstalling, so not worth the space excludes=( "$HOME/.cache" "$HOME/.davfs2/cache" "$HOME/.local/share/Trash" "$HOME/.local/share/containers" "$HOME/.local/share/flatpak" "$HOME/.local/share/siblings" "$HOME/.local/share/coredesktop" "$HOME/.var/app/*/cache" "$HOME/.nv" # Package caches and tool installs "$HOME/.npm" "$HOME/.bun/install/cache" "$HOME/.cargo/registry" "$HOME/.cargo/git" "$HOME/go/pkg" "$HOME/.local/share/mise/installs" "$HOME/.local/share/mise/downloads" "$HOME/.local/share/uv" "$HOME/.local/share/pipx" "$HOME/.local/share/claude" "$HOME/.local/share/nvim/lazy" "$HOME/.local/share/nvim/mason" # The agents' own installs and downloads; their settings, memories and # sessions are kept "$HOME/.hermes/hermes-agent" "$HOME/.hermes/tools" "$HOME/.hermes/installs" "$HOME/.hermes/bin" "$HOME/.hermes/cache" "$HOME/.hermes/audio_cache" "$HOME/.hermes/image_cache" # LM Studio's models and engines can be downloaded again "$HOME/.lmstudio/models" "$HOME/.lmstudio/extensions" "$HOME/.lmstudio/bin" # Electron and Chromium apps (Element, browsers, editors) keep caches here "$HOME/.config/*/Cache" "$HOME/.config/**/Code Cache" "$HOME/.config/**/GPUCache" "$HOME/.config/**/GrShaderCache" "$HOME/.config/**/GPUPersistentCache" "$HOME/.config/**/DawnGraphiteCache" "$HOME/.config/**/DawnWebGPUCache" "$HOME/.config/**/Service Worker/CacheStorage" # Element's search index for encrypted messages, which it rebuilds "$HOME/.config/Element/EventStore" # WinBoat's Windows VM: its disk is large, and changes whenever Windows runs "$HOME/winboat" ) exclude_args=(--exclude-caches) for path in "${excludes[@]}"; do exclude_args+=(--exclude "$path") done [ -f "$USER_EXCLUDE" ] && exclude_args+=(--exclude-file "$USER_EXCLUDE") echo "Backing up $HOME to $BACKUP_DIR in your Nextcloud files..." # --one-file-system leaves out other mounts and btrfs subvolumes inside home restic backup --one-file-system --host "$HOSTNAME" "${exclude_args[@]}" "$HOME" status=$? # 3 means some files couldn't be read; the rest were backed up (( status == 0 || status == 3 )) || fail "restic couldn't back up $HOME (exit $status). $LOG_HINT" echo "Removing old backups..." restic forget --host "$HOSTNAME" --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune \ || fail "restic couldn't remove old backups. $LOG_HINT" echo "Backup finished." exit 0 }