Backup Guide

One overlooked aspect of data is availability; the ability to access said information, even under threatening scenarios like:

Using modern filesystems (like BTRFS or ZFS) along with ECC RAM can help detect and repair certain forms of corruption and write failures during unexpected power outages. Surge protectors and UPS are also essential protection from power irregularities. However, no protection system can guarantee absolute safety from a lightning strike, or a fire, or a burglar, or most likely yourself. Hence, a reasonable defense against all of the aforementioned threats requires a simple backup solution, one which is:

  1. Simple and employing the Unix philosophy; using small and common components together to accomplish a larger sum task.
  2. Fully owned by you (see: Better Software > Ownership).
  3. Stored on three independent copies (such as external USB hard drives), where:
  4. If important enough, encrypted (either per-file through gpgtar, or full-disk encryption using LUKS2 with dm-crypt through cryptsetup luksFormat).

A standard utility like tar meets all the criteria for an established, well-maintained tool for proper backups, and any script using it shouldn't be complex. Here's an example backup script (from my Dotfiles in ~/.local/bin/) which backs up things on my home directory (in addition to a list of all packages installed with pacman):

#!/bin/sh
# backup

list="$HOME/.local/share/backup-list.txt"

case "$1" in
  -*)
    echo "usage: backup [output-directory]"
    exit
    ;;
esac

dir="${1:-$PWD}"
[ -d "$dir" ] || { echo "backup: '$dir' is not a directory" >&2; exit 1; }
[ -f "$list" ] || { echo "backup: $list not found" >&2; exit 1; }

pkgs="/tmp/packages-list.txt"
pacman -Qqe > "$pkgs"

home="${HOME#/}"
name="backup-$(date +%F_%H-%M-%S)"
out="${dir%/}/${name}.tar.gz"

{ grep '^--exclude=' "$list"; grep -v '^--exclude=' "$list"; } |
  sed 's,\(^\|=\)~,\1'"$HOME"',' |
  tar -czf "$out" \
    --transform="s,^\($home\|tmp\)/,${name}/," \
    -T - "$pkgs"

rm -f "$pkgs"
backup /mnt/sdb1/

The script creates a compressed .tar.gz (tarball) archive with everything under a top-level directory, and dumps it to the output directory (in this case, the backup USB mounted in /mnt/sdb1/). If no output directory is specified, it defaults to the current working directory. The ~/.local/share/backup-list.txt file defines all directories and files to be backed up (and lines prefixed with --exclude= exclude a path instead). It looks something like this on my end (note that my dotfiles are inside of ~/Projects/ already, so it's kinda redundant to back them up again):

~/.config/aerc/accounts.conf
~/.config/gajim/
~/.config/isyncrc
~/.config/neocities-deploy/config.toml
~/.config/newsraft/feeds
~/.local/share/applications/
~/.local/share/backup-list.txt
~/.local/share/gajim/
~/.local/share/git/
~/.local/share/gnupg/
~/.local/share/pass/
~/.ssh/
~/Documents/Notes/
~/Pictures/
~/Projects/
~/Videos/
--exclude=~/.local/share/gajim/downloads/
--exclude=~/Pictures/Screenshots/
--exclude=~/Pictures/Wallpapers/
--exclude=~/Videos/Anime/
--exclude=~/Videos/Recordings/

Using this script, I take a backup to one of my drives every week (or everytime an important change happens like a password database update), rotate it back into storage, and push the oldest one off premises.

Individual USB disks should be replaced after about 5-7 years. Even though they don't see a whole lot of power on hours, they still receive tons of writes as their jobs as backup drives. Lastly, consider physically differentiating the drives in some way to simplify their rotation (like name labels with different colors), otherwise you'll always be checking the last backup date to confirm you're indeed updating the least recent of the bunch.