My backup scripts and tools
Go to file
2024-07-01 14:24:28 -04:00
bin bin: rebuild borg.x86_64 with staticx 0.13.8 2022-09-17 11:55:44 -04:00
.gitignore misc: ignore .venv dir 2021-10-16 09:37:04 -04:00
backup.py backup: adjust email formatting 2021-10-26 21:37:53 -04:00
borg.sh all: remove concept of read-write key 2021-10-19 14:46:34 -04:00
config.yaml backup: replace simple max size with rule-based system 2021-10-18 17:43:33 -04:00
initial-setup.sh Support recovery by setting up an existing repo 2024-07-01 14:24:28 -04:00
Makefile borg: add ARM binary for Pi; update scripts to use it 2021-10-26 15:50:08 -04:00
notify.sh notify: fix notify.sh to work with server side; adjust text 2021-10-16 09:37:03 -04:00
Pipfile Implement filesystem scanning with configurable filters 2021-10-11 16:50:08 -04:00
Pipfile.lock Update Pipfile.lock for newer Cython compatibility 2023-11-03 13:52:32 -04:00
README.md Update README 2021-10-26 16:03:52 -04:00

Initial setup

Run on client:

sudo git clone https://git.jim.sh/jim/borg-setup.git /opt/borg
sudo /opt/borg/initial-setup.sh

Customize /opt/borg/config.yaml as desired.

Cheat sheet

After setup, the copy of this file on the client will have the variables in this section filled in automatically

Configuration

Hostname: ${HOSTNAME}
Base directory: ${BORG_DIR}
Destination: ${BACKUP_USER}@${BACKUP_HOST}
Repository: ${BACKUP_REPO}

Commands

See when next backup is scheduled:

systemctl list-timers borg-backup.timer

See status of most recent backup:

systemctl status --full --lines 999999 --no-pager --all borg-backup

Watch log:

journalctl --all --follow --unit borg-backup

Start backup now:

sudo systemctl start borg-backup

Interrupt backup in progress:

sudo systemctl stop borg-backup

Show backups and related info:

sudo ${BORG_DIR}/borg.sh info
sudo ${BORG_DIR}/borg.sh list

Run Borg using the read-write SSH key:

sudo ${BORG_DIR}/borg.sh --rw list

Mount and look at files:

mkdir mnt
sudo ${BORG_DIR}/borg.sh mount :: mnt
sudo -s # to explore as root
sudo umount mnt

Compaction and remote access

Old backups are "pruned" automatically, but because the SSH key is append-only, no space is actually recovered on the server, it's just marked for deletion. If you are sure that the client system was not compromised, then you can run compaction manually directly on the backup host by logging in via SSH (bitwarden ssh ${BACKUP_HOST} / ${BACKUP_USER}) and compacting there:

ssh ${BACKUP_USER}@${BACKUP_HOST} borg/borg compact --verbose --progress ${BACKUP_REPO}

This doesn't require the repo key. That key shouldn't be entered on the untrusted backup host, so for operations that need it, use a trusted host and run borg remotely instead, e.g.:

${BORG_BIN} --remote-path borg/borg info ${BACKUP_USER}@${BACKUP_HOST}:borg/${HOSTNAME}

The repo passphrase is in bitwarden borg ${HOSTNAME} / repo key.

Design

  • On server, we have a separate user account "jim-backups". Password for this account is in bitwarden in the "Backups" folder, under ssh backup.jim.sh.

  • Repository keys are repokeys, which get stored on the server, inside the repo. Passphrases are stored:

    • on clients (in /opt/borg/passphrase, for making backups)
    • in bitwarden (under borg <hostname>, user repo key)
  • Each client has two passwordless SSH keys for connecting to the server:

    • /opt/borg/ssh/id_ecdsa_appendonly
      • configured on server for append-only operation
      • used for making backups
    • /opt/borg/ssh/id_ecdsa_notify
      • configured on server for running borg/notify.sh only
      • used for sending email notifications on errors
  • Systemd timers start daily backups:

    /etc/systemd/system/borg-backup.service -> /opt/borg/borg-backup.service
    /etc/systemd/system/borg-backup.timer -> /opt/borg/borg-backup.timer
    
  • Backup script /opt/borg/backup.py uses configuration in /opt/borg/config.yaml to generate our own list of files, excluding anything that's too large by default. This requires borg 1.2 or newer.

Notes

Building Borg binary from git

sudo apt install python3.9 scons libacl1-dev libfuse-dev libpython3.9-dev patchelf
git clone https://github.com/borgbackup/borg.git
cd borg
virtualenv --python=python3.9 borg-env
source borg-env/bin/activate
pip install -r requirements.d/development.txt
pip install pyinstaller
pip install llfuse
pip install -e .[llfuse]
pyinstaller --clean --noconfirm scripts/borg.exe.spec
pip install staticx

# for x86
staticx -l /lib/x86_64-linux-gnu/libm.so.6 dist/borg.exe borg.x86_64

# for ARM; see https://github.com/JonathonReinhart/staticx/issues/209
staticx -l /lib/arm-linux-gnueabihf/libm.so.6 dist/borg.exe borg.armv7l

Then run borg.x86_64. Confirm the version with borg.armv7l --version.

Note: This uses the deprecated llfuse instead of the newer pyfuse3. pyfuse3 doesn't work because, at minimum, it pulls in trio which requires ssl which is explicitly excluded by scripts/borg.exe.spec.