Skip to content

Example: Create Borg backups on a USB stick

Daniel Rudolf edited this page Nov 5, 2017 · 9 revisions
#!/bin/bash
set -e

# external drive label
DEVICE_LABEL="My_Backup_Stick"

# sleep time when external drive isn't plugged in yet
# sleep for 5 seconds for up to 60 times (= 5 minutes total)
DEVICE_SLEEP=5
DEVICE_SLEEP_COUNT=60

# repository path (relative to drive mountpoint)
REPOSITORY="Borg"

# be verbose, otherwise output nothing (except errors)
BORG_CREATE_PARAMS="-v --stats --show-rc"
BORG_DELETE_PARAMS="-v --stats --show-rc"
BORG_PRUNE_PARAMS="-v --list --stats --show-rc"
if tty --quiet; then
    BORG_CREATE_PARAMS+=" --progress"
    BORG_DELETE_PARAMS+=" --progress"
fi
if [ "$1" == "-q" ] || [ "$1" == "--quiet" ]; then
    BORG_CREATE_PARAMS=""
    BORG_DELETE_PARAMS=""
    BORG_PRUNE_PARAMS=""
fi

# check for external drive
if [ ! -b "/dev/disk/by-label/$DEVICE_LABEL" ]; then
    SUMMARY="Borg Backup"
    BODY="To start your Borg Backup \"/home/\", you must plug in your \"$DEVICE_LABEL\" USB stick!"
    notify-send --app-name="borg-notify" --icon="borg" --urgency="normal" --expire-time="-1" "$SUMMARY" "$BODY"

    for (( i=1; i <= $DEVICE_SLEEP_COUNT; i++)); do
        sleep $DEVICE_SLEEP

        if [ -b "/dev/disk/by-label/$DEVICE_LABEL" ]; then
            sleep $DEVICE_SLEEP
            break
        fi
    done

    [ -b "/dev/disk/by-label/$DEVICE_LABEL" ] || exit 75
fi

MOUNTPOINT="$(findmnt --first-only --noheadings --output=target --source "/dev/disk/by-label/$DEVICE_LABEL")"
if [ ! -d "$MOUNTPOINT" ]; then
    echo "External drive is not mounted: $DEVICE_LABEL" >&2
    exit 1
fi

REPOSITORY="$MOUNTPOINT/$REPOSITORY"
trap "sync -f \"$REPOSITORY\"; sync -f \"$REPOSITORY\"" INT TERM EXIT

# prepare repository
if [ ! -e "$REPOSITORY" ]; then
    mkdir "$REPOSITORY"
    borg init --encryption none "$REPOSITORY"
fi
if [ ! -d "$REPOSITORY" ]; then
    echo "Invalid repository: $REPOSITORY" >&2
    exit 1
fi

# create backup (ignore warnings)
BORG_CREATE_STATUS=0
borg create $BORG_CREATE_PARAMS \
    --compression lzma \
    --exclude-caches --one-file-system \
    "$REPOSITORY"::'{hostname}-{now:%Y-%m-%dT%H:%M:%S}' \
    "$HOME" \
    --exclude '$HOME/.cache' \
    --exclude '$HOME/.local/share/Trash' \
    --exclude '$HOME/.thumbnails' \
    || { BORG_CREATE_STATUS=$?; true; }

[ $BORG_CREATE_STATUS -lt 2 ] || exit $BORG_CREATE_STATUS

# remove checkpoints (ignore warnings)
BORG_DELETE_STATUS=0
if [ $BORG_CREATE_STATUS -eq 0 ]; then
    borg list "$REPOSITORY" \
        | grep '\.checkpoint ' \
        | cut -s -d ' ' -f 1 \
        | xargs --max-args 1 --no-run-if-empty -I "%ARCHIVE%" -- \
        borg delete $BORG_DELETE_PARAMS "$REPOSITORY"::"%ARCHIVE%" \
        || { BORG_DELETE_STATUS=$?; true; }

    [ $BORG_DELETE_STATUS -lt 2 ] || exit $BORG_DELETE_STATUS
fi

# prune old backups (ignore warnings)
# keep everything within two days + 12 daily backups (= 2 weeks),
# 26 weekly backups (= 6 months), 24 monthly backups (= 2 years),
# 10 yearly backups
BORG_PRUNE_STATUS=0
borg prune $BORG_PRUNE_PARAMS \
    "$REPOSITORY" --prefix '{hostname}-' \
    --keep-within=2d --keep-daily=12 \
    --keep-weekly=26 --keep-monthly=24 \
    --keep-yearly=10 \
    || { BORG_PRUNE_STATUS=$?; true; }

[ $BORG_PRUNE_STATUS -lt 2 ] || exit $BORG_PRUNE_STATUS

if [ $BORG_CREATE_STATUS -eq 1 ] || [ $BORG_DELETE_STATUS -eq 1 ] || [ $BORG_PRUNE_STATUS -eq 1 ]; then
    exit 254
fi

Clone this wiki locally