borg-backup.sh (3659B)
1 #!/bin/bash 2 3 export BORG_REPO={{ borg_repo | quote }} 4 export BORG_PASSPHRASE={{ borg_passphrase | quote }} 5 export BORG_RSH='ssh -F /root/.ssh/borg-config' 6 7 umask 077 8 info() { printf "\n%s %s\n\n" "$(date)" "$*" >&2; } 9 10 # Acquire the lock before changing any dump files or installing cleanup traps. 11 # A competing cron/manual/reboot run must not touch the owner's snapshot. 12 exec 9>/run/taler-borg-backup.lock || exit 2 13 if ! flock --exclusive --nonblock 9; then 14 info "Another backup is running; this run made no changes" 15 exit 75 16 fi 17 cd /root || exit 2 18 cleanup() { 19 rm -f -- postgres-backup.sql postgres-backup.sql.gz 20 } 21 trap cleanup EXIT 22 trap 'exit 2' HUP INT TERM 23 24 info "Dumping database" 25 26 # The root-owned output is intentionally opened by this shell. 27 # shellcheck disable=SC2024 28 sudo -u postgres pg_dumpall > postgres-backup.sql 29 db_exit=$? 30 31 if [[ $db_exit -ne 0 ]]; then 32 info "DB export failed, exit status $db_exit" 33 exit 1 34 fi 35 36 # Note: I actually benchmarked (!) this on *our* SQL data. 37 # zstd was fastest, but gzip was smallest 38 # (tested: gzip, bzip, zstd, lzip, xz) 39 # -f so that a snapshot left behind by an interrupted run does not 40 # block every future backup. 41 gzip -f postgres-backup.sql || exit 1 42 43 echo "Database snapshot created:" 44 ls -al postgres-backup.sql.gz 45 46 info "Starting backup" 47 48 # Backup the most important directories into an archive named after 49 # the machine this script is currently running on: 50 51 borg create \ 52 --verbose \ 53 --filter AME \ 54 --list \ 55 --stats \ 56 --show-rc \ 57 --compression lz4 \ 58 --exclude-caches \ 59 --exclude 'home/*/.cache/*' \ 60 --exclude 'var/tmp/*' \ 61 --exclude 'var/lib/taler-exchange/secmod-*/*' \ 62 \ 63 ::'{hostname}-{now}' \ 64 /etc \ 65 /root \ 66 /var/lib/libeufin-nexus \ 67 /var/lib/taler-exchange 68 69 backup_exit=$? 70 71 info "Removing database dump" 72 73 rm -f -- postgres-backup.sql.gz || exit 2 74 75 if [[ $backup_exit -ne 0 ]]; then 76 info "Backup did not complete cleanly, exit status $backup_exit; not pruning" 77 exit $backup_exit 78 fi 79 80 info "Pruning repository" 81 82 # Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly 83 # archives of THIS machine. The '{hostname}-*' matching is very important to 84 # limit prune's operation to this machine's archives and not apply to 85 # other machines' archives also: 86 87 borg prune \ 88 --list \ 89 --glob-archives '{hostname}-*' \ 90 --show-rc \ 91 --keep-daily 7 \ 92 --keep-weekly 4 \ 93 --keep-monthly 6 94 95 prune_exit=$? 96 if [[ $prune_exit -ne 0 ]]; then 97 info "Prune did not complete cleanly, exit status $prune_exit; not compacting" 98 exit "$prune_exit" 99 fi 100 101 # actually free repo disk space by compacting segments 102 103 info "Compacting repository" 104 105 borg compact 106 107 compact_exit=$? 108 109 # use highest exit code as global exit code 110 global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) 111 global_exit=$(( compact_exit > global_exit ? compact_exit : global_exit )) 112 global_exit=$(( db_exit > global_exit ? db_exit : global_exit )) 113 114 if [ ${global_exit} -eq 0 ]; then 115 info "Backup, Prune, and Compact finished successfully" 116 elif [ ${global_exit} -eq 1 ]; then 117 info "Backup, Prune, and/or Compact finished with warnings" 118 else 119 info "Backup, Prune, and/or Compact finished with errors" 120 fi 121 122 exit ${global_exit}