wren-linux / extras / backup.sh
#!/bin/bash
# Encrypted backups of your home folder with restic (from Debian), to a
# USB drive or any folder you choose. Everything in the backup is
# encrypted with a password that restic asks for each time; it isn't
# stored anywhere. Backups only store what changed since the last one.
# This sets up the backup place and a "backup" command.
# For backups that run without you (a timer, a script), the config can hold
# a command that prints the password, e.g. one that asks a password manager;
# the password itself is never written down. At login it says when the last
# backup is 30 days old or more (backup remind): in sway a notification, on a
# text console a line.
# files: lib/sway.sh
# Usage: bash backup.sh [--remove | --manual]
# summary: Encrypted backups of your home folder (restic)
# security: most
# category: files
# status: apt:restic
. "$(dirname -- "${BASH_SOURCE[0]}")/../lib.sh"
if [ "${1:-}" = --manual ]; then print_manual; exit; fi
need_user
RC=$HOME/.bashrc
RC_BEGIN='# >>> Wren Linux backup reminder >>>'
RC_END='# <<< Wren Linux backup reminder <<<'
rc_take_out() { [ -f "$RC" ] && sed -i "\\|^$RC_BEGIN\$|,\\|^$RC_END\$|d" "$RC"; return 0; }
CONF_DIR="$HOME/.config/wren"
CONF="$CONF_DIR/backup.conf"
EXCLUDES="$CONF_DIR/backup-excludes"
if [ "${1:-}" = --remove ]; then
remove_bin backup
remove_snippet backup-reminder
rc_take_out
rm -f "$CONF" "$EXCLUDES"
apt_purge restic
note "Your backups themselves are left where they are."
exit
fi
say "Installing restic"
apt_install restic
# Where the backups go
repo=
if [ -f "$CONF" ]; then
repo=$(sed -n 's/^BACKUP_REPO=//p' "$CONF")
ask "Keep backing up to $repo?" y || repo=
fi
if [ -z "$repo" ]; then
default=
for d in /media/"$USER"/*/; do
[ -d "$d" ] && { default="${d%/}/wren-backup"; break; }
done
note "Where should backups go? A folder on a USB drive is best (plug it in first)."
[ -z "$default" ] || note "Press Enter for: $default"
tty_read repo " folder> "
repo=${repo:-$default}
[ -n "$repo" ] || die "No folder given."
repo=${repo/#\~/$HOME}
[[ $repo == /* ]] || die "Give the full path, starting with /."
fi
if [ -f "$repo/config" ]; then
note "Using the existing backup in $repo."
else
mkdir -p "$repo"
say "Creating an encrypted backup in $repo"
warn "Choose a strong password and keep it somewhere safe outside this laptop"
warn "(on paper, too): without it the backup can't be opened, and your"
warn "KeePassXC database is inside the backup."
restic -r "$repo" init </dev/tty || die "restic couldn't create the backup."
fi
mkdir -p "$CONF_DIR"
cmd=$(sed -n 's/^PASSWORD_COMMAND=//p' "$CONF" 2>/dev/null || true)
if [ -n "$cmd" ]; then
ask "Keep getting the backup password with: $cmd ?" y || cmd=
else
note "restic asks for the backup password each time. To run backups without typing it,"
note "give a command that prints it (for example one that reads a secret store you unlock at login)."
tty_read cmd " password command (Enter to be asked each time)> "
fi
printf '# Written by Wren Linux extras/backup.sh\nBACKUP_REPO=%s\n' "$repo" >"$CONF"
if [ -n "$cmd" ]; then printf 'PASSWORD_COMMAND=%s\n' "$cmd" >>"$CONF"; fi
# write_excludes FILE: what is left out of backups: caches, trash, and big
# things that can be downloaded or built again (VM disks: libvirt's, which
# Whonix uses, and ~/vpskvm, mockvps-kvm's). The file is yours to edit, one
# pattern per line; a later run only adds the lines Wren Linux's list gained.
write_excludes() {
local f=$1 line
if [ ! -f "$f" ]; then
printf '%s\n' "# Left out of backups (written by Wren Linux extras/backup.sh)" \
"$HOME/.cache" "$HOME/.local/share/Trash" "$HOME/.local/share/libvirt/images" >"$f"
fi
# Added to Wren Linux's list later (a line you deleted from the first three stays deleted)
for line in "$HOME/vpskvm" "$OFFLINE_DIR"; do
grep -qxF "$line" "$f" || echo "$line" >>"$f"
done
}
write_excludes "$EXCLUDES"
install_bin backup
# At login: a word when the last backup is 30 days old or more
put_snippet backup-reminder <<'EOF'
# Says so at login when the last backup is 30 days old or more (Wren Linux extras/backup.sh)
exec ~/.local/bin/backup remind
EOF
rc_take_out
# shellcheck disable=SC2016 # written into ~/.bashrc as it is, expanded there
printf '%s\n' "$RC_BEGIN" '[ -n "${WAYLAND_DISPLAY:-}" ] || [ ! -x ~/.local/bin/backup ] || ~/.local/bin/backup remind' "$RC_END" >>"$RC"
note "Backups go to: $repo"
note "At login it reminds you when the last backup is 30 days old. Unplug the drive after each one."
note "Left out (edit to change): $EXCLUDES"
cat <<'EOF'
How to use backup:
backup back up your home folder now
backup snapshots list your backups
backup ls latest list the files in the newest backup
backup restore latest --target ~/restored
get everything back, into ~/restored
backup restore latest --target ~/restored --include ~/.electrum
get back one folder
backup diff ID1 ID2 what changed between two backups
(IDs come from "backup snapshots")
backup check make sure the backup isn't damaged
backup forget --keep-last 10 --prune
keep only the 10 newest backups
Each command asks for the backup password. Restored files keep their
full path, e.g. ~/restored/home/you/.electrum: look there, then copy
back what you need.
EOF