#!/bin/bash # # This is a simple backup script using borg. It's supposed to serve as a # starting point and to be adjusted to your system. # # Important steps: # - define a host "backup" in root's .ssh/config # - read the script and adjust to your needs # - As root run `borg init -v --encryption=keyfile backup:borg-$HOSTNAME` # (note that zsh uses $HOST instead of $HOSTNAME) # - If you want, increase the max_segment_size in # ssh://backup/borg-$HOSTNAME/config from the default 5MiB set -e main() { if [[ $UID != 0 ]]; then exec sudo "$0" "$@" fi TMPDIR="$(mktemp -d "/tmp/${0##*/}.XXXXXX")" trap "rm -rf '${TMPDIR}'" EXIT TERM # these mountpoints will be excluded. mountpoints not listed in either this # or the includeMountpoints variable below will throw an error excludeMountpoints=( /tmp /sys /dev /proc /run /mnt/levant/nfs /media ) # these mountpoints will be included includeMountpoints=( / /boot /home /mnt/data ) # list of patterns that should be excluded. This supports shell globbing as # well as regex pattern. Refer to man borg for details. IFS='' read -r -d '' excludeList_borg < "$TMPDIR/exclude-list-borg" # save some data that's useful for restores local backupDataDir=/root/backup-data/ mkdir -p "$backupDataDir" fdisk -l > "$backupDataDir/fdisk" vgdisplay > "$backupDataDir/vgdisplay" pvdisplay > "$backupDataDir/pvdisplay" lvdisplay > "$backupDataDir/lvdisplay" df -a > "$backupDataDir/df" findmnt -l > "$backupDataDir/findmnt" # TODO: create snapshots here if possible. Sadly that's really difficult so # it will probably not be implemented local backupdir="borg-$HOSTNAME" backup_borg / "backup:$backupdir" ssh backup "touch $backupdir/last-backup-timestamp" } backup_borg() { local src=$1 local dst=$2 local -a options=( --verbose --numeric-owner --compression lz4 --stats --exclude-from "$TMPDIR/exclude-list-borg" ) if tty -s; then options+=(--progress) fi borg create "${options[@]}" "$dst::backup-$(date "+%Y%m%d-%H%M%S")" "$src" borg prune --keep-within 7d --keep-daily 7 --keep-weekly 12 --keep-monthly 12 --keep-yearly 3 -v "$dst" } ### support functions below ### ## # usage : in_array( $needle, $haystack ) # return : 0 - found # 1 - not found ## in_array() { local needle=$1; shift local item for item in "$@"; do [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } # same as in_array except 0 is returned if any item in haystack starts with needle in_array_startswith() { local needle=$1; shift local item for item in "$@"; do [[ "$needle" == "$item"* ]] && return 0 # Found done return 1 # Not Found } exclude_mountpoints() { local error=0 for fs in "${excludeMountpoints[@]}"; do excludeList_borg+="sh:$fs/*"$'\n' done while read line; do local mountpoint=$(echo "$line" | cut -d\ -f2 | sed 's#\040# #g;') if ! in_array "$mountpoint" "${includeMountpoints[@]}"; then if ! in_array_startswith "$mountpoint/" "${excludeMountpoints[@]/%//}"; then error=1 echo "Warning: mountpoint not excluded or included: $mountpoint" >&2 fi fi done