From 2c69041cf48b594d5de0521af9e0b5fa9502ef92 Mon Sep 17 00:00:00 2001 From: cg Date: Tue, 3 Dec 2024 13:47:27 +0000 Subject: [PATCH] initial version --- README.md | 25 +++++++++++++++ b0rged.service | 7 +++++ b0rged.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ b0rged.timer | 12 +++++++ env.example | 16 ++++++++++ patterns.system | 11 +++++++ 6 files changed, 154 insertions(+) create mode 100644 README.md create mode 100644 b0rged.service create mode 100755 b0rged.sh create mode 100644 b0rged.timer create mode 100644 env.example create mode 100644 patterns.system diff --git a/README.md b/README.md new file mode 100644 index 0000000..1597a7e --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# b0rged +simple wrapper for borg-backup, aimed at backing up to hetzner storage boxes + +# install +clone this repo somewhere to your machine - I use *~root/b0rged* + +set up your storage box, according to the comments in *b0rged-init.sh*. + +set up your .env file, copy *env.example* and adapt. + +run *b0rged-init.sh*. if you want to, you could delete the script afterwards. + +add your backup patterns. you could edit *patterns.system* or add as many *patterns.foo* files as you want. + +run *b0rged.sh* manually, once. check that everything works. + +copy the system and timer file to `~/.config/systemd/user`. you might need to adapt *ExecStart* in *b0rged.service*. + +enable and start the timer. you might need to use `sudo machinectl shell root@` instead of `sudo -i` to get a root shell that works with systemd. then run `systemctl --user enable --now b0rged.timer`. + +check that your timer is set up. it should look something like this: + + systemctl --user list-timers --all + NEXT LEFT LAST PASSED UNIT ACTIVATES + Tue 2024-12-03 19:01:57 UTC 5h 21min left n/a n/a b0rged.timer b0rged.service diff --git a/b0rged.service b/b0rged.service new file mode 100644 index 0000000..a71c0e6 --- /dev/null +++ b/b0rged.service @@ -0,0 +1,7 @@ +[Unit] +Description=b0rged backup +#OnFailure=status_email_user@%n.service +# error reporting - see https://wiki.archlinux.org/title/Systemd/Timers#MAILTO to set up. + +[Service] +ExecStart=/root/b0rged/b0rged.sh diff --git a/b0rged.sh b/b0rged.sh new file mode 100755 index 0000000..a3ab891 --- /dev/null +++ b/b0rged.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +set -o allexport +eval $(cat ${DIR}'/.env' | sed -e '/^#/d;/^\s*$/d' -e 's/\(\w*\)[ \t]*=[ \t]*\(.*\)/\1=\2/' -e "s/=['\"]\(.*\)['\"]/=\1/g" -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g") +set +o allexport + +info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } +trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + +if [ -z ${BORG_REPO+x} ]; then info "BORG_REPO must be set, check your .env"; exit 1; fi +if [ -z ${BORG_PASSPHRASE+x} ]; then info "BORG_PASSPHRASE must be set, check your .env"; exit 1; fi +node_metrics_enabled=${NODE_METRICS_ENABLED:-1} +node_metrics_path=${NODE_METRICS_PATH:-/usr/local/lib/prom} + +start=$(date +%s) + +exec {lock_fd}>/var/lock/borg-backup.lock || exit 1 +flock -n "$lock_fd" || { echo "ERROR: flock() failed." >&2; exit 1; } + +patternfile=$(mktemp) +cat patterns.* > $patternfile + +if [ ${DOCKER_VOLUMES_ENABLED} -eq 1 ]; then + info "Collecting docker volumes..." + : ${DOCKER_COMMAND:=/usr/bin/docker} + volumes=$(${DOCKER_COMMAND} volume ls -f label=org.zknt.backup -f driver=local --format "{{.Name}}") + dockerpatterns=$(mktemp) + for volume in ${volumes}; do + mountpoint=$(${DOCKER_COMMAND} volume inspect ${volume} --format "{{.Mountpoint}}") + echo "R $(dirname ${mountpoint})" >> $dockerpatterns + done + cat $dockerpatterns >> $patternfile + rm $dockerpatterns +fi + +info "Starting borg..." +borg create \ + --warning \ + --filter AME \ + --list \ + --stats \ + --show-rc \ + --compression lz4 \ + --exclude-caches \ + --exclude '/home/*/.cache/*' \ + --exclude '/var/tmp/*' \ + ::'{hostname}-{now}' \ + --patterns-from $patternfile + +backup_exit=$? + +rm $patternfile + +borg prune \ + --list \ + --prefix '{hostname}-' \ + --show-rc \ + --keep-daily ${BORG_PRUNE_KEEP_DAILY:-14} \ + --keep-weekly ${BORG_PRUNE_KEEP_WEEKLY:-4} \ + --keep-monthly ${BORG_PRUNE_KEEP_MONTHLY:-6} + +prune_exit=$? + +global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) + +if [ ${global_exit} -eq 0 ]; then + info "Backup, Prune, and Compact finished successfully" +elif [ ${global_exit} -eq 1 ]; then + info "Backup, Prune, and/or Compact finished with warnings" +else + info "Backup, Prune, and/or Compact finished with errors" +fi + +end=$(date +%s) + +if [ ${node_metrics_enabled} -eq 1 ]; then + echo "borg_last_run ${end}" > ${node_metrics_path}/borg_backup.prom.$$ + echo "borg_last_duration $(($end - $start))" >> ${node_metrics_path}/borg_backup.prom.$$ + mv ${node_metrics_path}/borg_backup.prom{.$$,} +fi + +flock -u "$lock_fd" diff --git a/b0rged.timer b/b0rged.timer new file mode 100644 index 0000000..81379b0 --- /dev/null +++ b/b0rged.timer @@ -0,0 +1,12 @@ +[Unit] +Description=b0rged timer + +[Timer] +OnCalendar=*-*-* 1,7,13,19:00:00 +RandomizedDelaySec=30m +AccuracySec=1s +Persistent=true +Unit=b0rged.service + +[Install] +WantedBy=timers.target diff --git a/env.example b/env.example new file mode 100644 index 0000000..d902693 --- /dev/null +++ b/env.example @@ -0,0 +1,16 @@ +BORG_REPO=ssh://u335394-sub5@u335394.your-storagebox.de:23/./borg +BORG_PASSPHRASE=abcd1234 + +BORG_PRUNE_KEEP_DAILY=14 +BORG_PRUNE_KEEP_WEEKLY=4 +BORG_PRUNE_KEEP_MONTHLY=6 + +# Wether to write metrics to a textfile for node-exporter to pick up. +NODE_METRICS_ENABLED=0 +# Path to write metrics to. See --collector.textfile.directory in node-exporter +NODE_METRICS_PATH=/usr/local/lib/prom + +# collect docker volumes +# checks for given label on volumes +DOCKER_VOLUMES_ENABLED=0 +DOCKER_VOLUMES_LABEL=borg.backup diff --git a/patterns.system b/patterns.system new file mode 100644 index 0000000..7c97313 --- /dev/null +++ b/patterns.system @@ -0,0 +1,11 @@ +R /etc +R /root +- **/log/ +- **/logs/ +- **/cache/ +- **/.cache/ +- **/node_modules/ +- **/vendor/ +- **/typo3temp/ +- **/composer-cache/ +- **/.git/