Skip to content

Instantly share code, notes, and snippets.

@brycied00d
Last active August 25, 2023 03:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brycied00d/689f9abd08678b7463b36ad899aabaad to your computer and use it in GitHub Desktop.
Save brycied00d/689f9abd08678b7463b36ad899aabaad to your computer and use it in GitHub Desktop.
backup-restic.sh - My generap-purpose script to backup a system, balancing simplicity with configurability.
# This is an example file, with example values.
#export RESTIC_PASSWORD=apassword
#export RESTIC_REPOSITORY=b2:bucket:path
#export B2_ACCOUNT_ID=12345
#export B2_ACCOUNT_KEY=abcdefg
#restic_backup /etc
#restic_backup /root
#restic_backup /var
#restic_backup /usr/local
#restic_backup /home
#restic_backup --exclude /srv/Apt/acng/ /srv
#cd /tmp ; sudo -u postgres /usr/bin/pg_dumpall | restic_backup --stdin --stdin-filename postgres.sql
## Example of using LVM snapshots to backup point-in-time. This may be merged into backup-restic.sh
#snap_and_mount() {
# if [ -z "$1" ] ; then return 1 ; fi
# vg="$1"
# if [ -z "$2" ] ; then return 1 ; fi
# lv="$2"
# size=${3:-200G}
# /sbin/lvcreate --snapshot "$vg"/"$lv" --size $size --name backupsnapb2 --addtag temporary ; ret=$?
# #echo "lvcreate $ret"
# if [ $ret -eq 0 ] ; then
# mkdir -p /mnt/VG-"$vg"_LV-"$lv"
# mount -o ro /dev/"$vg"/backupsnapb2 /mnt/VG-"$vg"_LV-"$lv" ; ret=$?
# #echo "mount $ret"
# fi
# # Returns the last exit value, either lvcreate or mount, which should be 0 if success
# return $ret
#}
#umount_and_unsnap() {
# if [ -z "$1" ] ; then return 1 ; fi
# vg="$1"
# umount /dev/"$vg"/backupsnapb2 ; ret=$?
# #echo "Umount $ret"
# if [ $ret -eq 0 ] ; then
# /sbin/lvremove --force /dev/"$vg"/backupsnapb2 ; ret=$?
# #echo "lvremove $ret"
# fi
# # Returns the last exit value, either lvcreate or mount, which should be 0 if success
# return $ret
#}
## N=boot
#restic_backup /boot
#V=nextcloud-vg
#N=root
#if snap_and_mount $V $N 8G ; then
# restic_backup /mnt/VG-"$V"_LV-"$N"/etc
# restic_backup /mnt/VG-"$V"_LV-"$N"/home
# umount_and_unsnap $V
#fi
#!/usr/bin/env bash
# backup-restic
#
# Simple, basic script for performing backups using restic, and maintaining
# restic repositories.
# Configuration, including RESTIC_* and backend variables, and backup tasks go
# into /etc/backup-restic.conf which is a regular shell script that is sourced.
#
# Help: backup-restic.sh [configuration] [<help|install <version>|init|newkey|backup|$othercommands>]
# configuration -- Optional path to a configuration file. Defaults to /etc/backup-restic.conf
# install <version> -- install the specified version of restic to /usr/local/bin/restic
# init -- Initialise a new repository (RESTIC_REPOSITORY in the configuration)
# newkey -- Add a new key to the RESTIC_REPOSITORY
# backup -- Backup the given path. This is an alias to the restic_backup directive.
# $othercommands -- Any other restic commands, and their arguments, to be executed within the configured Restic parameters.
# No Arguments -- Perform the default backup operation.
#
# Installation: Install this script into /usr/local/bin/backup-restic.sh, then
# write the configuration file with commands and restic_backup.
# Cron: /usr/bin/ionice -c 2 -n 4 /usr/bin/nice /usr/local/bin/backup-restic.sh /etc/backup-restic_b2.conf | /usr/bin/logger -t backup-restic_b2
#
# PS: Yes, it does require bash (or ksh) for `[[`
#
# Author: Bryce Chidester <bryce@cobryce.com>
# Copyright: BSD 2-clause
#
# Changelog:
# 4: Work-around for https://github.com/restic/restic/issues/1807 by not
# passing --one-file-system if the backup command contains --stdin
# 3: Support for specifying/installing restic beta snapshots. Improved error
# handling and resiliency, will now exit if there are any syntax errors.
# Note: This may cause problems when restic reports an error (such as a file
# having changed on-disk during backup). This will be addressed in the future.
# Added support for PRUNE_OPTS?
# 2: Added some "help" functionality
# 1: Initial "release"
export SCRIPT_VERSION=4
UPSTREAM_VERSION=$(curl -sSL -A "$(basename $0)/$SCRIPT_VERSION ($(hostname))" https://brycesawesomeapp.com/backup-restic.sh | perl -n -e'/export SCRIPT_VERSION=(\d+)/ && print $1')
if [ "$UPSTREAM_VERSION" -gt "$SCRIPT_VERSION" ]; then
echo "Warning: There is an updated script available. Upgrade with:"
echo "curl --output $0 https://brycesawesomeapp.com/backup-restic.sh ; chmod +x $0"
sleep 5
# Don't exit, just warn.
fi
set -e # All errors (non-zero return) are fatal errors.
set -f # No globbing
set -C # No clobber
export CONFIG_FILE=/etc/backup-restic.conf
if [ -f "$1" ]; then
echo "Found configuration file: $1"
export CONFIG_FILE="$1"
shift
fi
if [ ! -f "$CONFIG_FILE" ]; then
echo "Unable to open the configuration file ($CONFIG_FILE). Exiting."
exit 1
fi
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export SCRIPT_NAME=$(basename $0)
export SCRIPT_BASENAME=$(basename $0 .sh)
export CONFIG_NAME=$(basename $CONFIG_FILE)
export CONFIG_BASENAME=$(basename $CONFIG_FILE .conf)
function restic_backup { # Alias
# Only runs once everything is ready and configured.
[ "x$CONFIGURED" = "x1" ] || return 0
ex=
if ! [[ $@ == *'--stdin'* ]]; then
ex=--one-file-system
fi
restic backup \
--tag "SCR:$SCRIPT_NAME" \
--tag "VER:$SCRIPT_VERSION" \
--tag "CFG:$CONFIG_NAME" \
$ex \
$@
}
# Read the configuration (embedded calls to restic_backup will fail until CONFIGURED=1)
export CONFIGURED=0
. $CONFIG_FILE
if [[ "$1" = *help* ]]; then
head -n22 $0
exit 0
elif [ "x$1" = "xinstall" -a -n "$2" ]; then
GOOS=$(uname -s)
if [ "$GOOS" = "Linux" ]; then GOOS=linux
elif [ "$GOOS" = "OpenBSD" ]; then GOOS=openbsd
fi
GOARCH=$(uname -m)
if [ "$GOARCH" = "x86_64" ]; then GOARCH=amd64
elif [[ "$GOARCH" = i*86 ]]; then GOARCH=386
fi
if [[ "$2" = *.*.*-* ]]; then
echo "Downloading and installing restic SNAPSHOT v$2 for $GOOS/$GOARCH"
if ! curl -sSL --output /usr/local/bin/restic https://beta.restic.net/v$2/restic_v${2}_${GOOS}_${GOARCH} ; then
echo "Error downloading restic."
exit 1
fi
else
echo "Downloading and installing restic v$2 for $GOOS/$GOARCH"
# Note: curl exits non-zero if bzcat fails because curl "Failed writing body"
if ! curl -sSL https://github.com/restic/restic/releases/download/v$2/restic_${2}_${GOOS}_${GOARCH}.bz2 | bzcat > /usr/local/bin/restic ; then
echo "Error downloading restic."
exit 1
fi
fi
chmod +x /usr/local/bin/restic
exit 0
elif [ "x$1" = "xinstall" ]; then
echo "A version to install must be specified."
exit 1
elif [ "x$1" = "xnewkey" ]; then
echo -n "Enter current password: "
read -s RESTIC_PASSWORD
echo
restic key add
echo Be sure to update RESTIC_PASSWORD
exit 0
elif [ "x$1" = "xinit" ]; then
restic init -o b2.connections=10
exit 0
elif [ -n "$1" ]; then
if [ "$1" = "backup" ]; then
shift
export CONFIGURED=1
restic_backup $@
else
restic $@
fi
exit 0
fi
# TODO: Change lock path on OpenBSD
if ! mkdir /var/lock/$SCRIPT_BASENAME ; then echo Lock file exists. Another backup process is already running. ; exit 1 ; fi
trap "rmdir /var/lock/$SCRIPT_BASENAME" EXIT INT TERM ERR
# No point scooping up anything temporary
which apt-get 2>/dev/null >/dev/null && apt-get clean
# This time, it's okay to run
export CONFIGURED=1
. $CONFIG_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment