Skip to content

Instantly share code, notes, and snippets.

@cnrd
Last active January 14, 2024 03:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save cnrd/9b8d50a17a3f7860298337bbbb084d12 to your computer and use it in GitHub Desktop.
Save cnrd/9b8d50a17a3f7860298337bbbb084d12 to your computer and use it in GitHub Desktop.
Backup script that uses rclone and ZFS snapshots to create incremental backups
#!/usr/bin/env bash
## Configs ##
ZFSSNAPSHOTNAME="rclone"
RCLONECONFIGPATH="/root/.config/rclone/rclone.conf"
BWLIMIT="10M"
TRANSFERS=10
mountSnapshots () {
mkdir -p "/mnt/$RUNNAME"
mount -t zfs "$ZFSSNAPSHOTBASE@$ZFSSNAPSHOTNAME" "/mnt/$RUNNAME"
while read -r line
do
MOUNTPATH=$(echo "$line" | sed "s/$ESCAPEDZFSSNAPSHOTBASE\///" | sed "s/@$ZFSSNAPSHOTNAME//")
if [ -d "/mnt/$RUNNAME/$MOUNTPATH" ]; then
mount -t zfs "$line" "/mnt/$RUNNAME/$MOUNTPATH"
fi
done < <(zfs list -t snapshot | grep "$ZFSSNAPSHOTNAME" | grep "$ZFSSNAPSHOTBASE" | grep -v "$ZFSSNAPSHOTBASE/\." | grep -o "$ZFSSNAPSHOTBASE/[0-9,a-z,A-Z,/,[:space:]]*@$ZFSSNAPSHOTNAME" | sed 's/[[:space:]]*$//')
}
unmountSnapshots () {
while read -r line
do
MOUNTPATH=$(echo "$line" | sed "s/$ESCAPEDZFSSNAPSHOTBASE\///" | sed "s/@$ZFSSNAPSHOTNAME//")
if [ -d "/mnt/$RUNNAME/$MOUNTPATH" ]; then
umount "/mnt/$RUNNAME/$MOUNTPATH"
fi
done < <(zfs list -t snapshot | grep "$ZFSSNAPSHOTNAME" | grep "$ZFSSNAPSHOTBASE" | grep -v "$ZFSSNAPSHOTBASE/\." | grep -o "$ZFSSNAPSHOTBASE/[0-9,a-z,A-Z,/,[:space:]]*@$ZFSSNAPSHOTNAME" | sed 's/[[:space:]]*$//' | tac)
umount "/mnt/$RUNNAME"
rmdir "/mnt/$RUNNAME"
}
cleanup () {
cd "/"
unmountSnapshots
zfs destroy -r "$ZFSSNAPSHOTBASE@$ZFSSNAPSHOTNAME"
if [ -f "/var/log/$RUNNAME.log" ]; then
bzip2 "/var/log/$RUNNAME.log"
fi
rm $PIDFILE
}
trapCleanup () {
trap SIGINT
cleanup
exit
}
createPIDFile () {
if [ -f $PIDFILE ]
then
PID=$(cat $PIDFILE)
ps -p $PID > /dev/null 2>&1
if [ $? -eq 0 ]
then
echo "Process already running"
exit 1
else
## Process not found assume not running
echo $$ > $PIDFILE
if [ $? -ne 0 ]
then
echo "Could not create PID file"
exit 1
fi
fi
else
echo $$ > $PIDFILE
if [ $? -ne 0 ]
then
echo "Could not create PID file"
exit 1
fi
fi
}
if [ $# -eq 2 ]
then
BASENAME=$(basename $1)
RUNNAME="$ZFSSNAPSHOTNAME-$BASENAME"
ZFSSNAPSHOTBASE=$(zfs list | grep "$BASENAME" | grep -v "$BASENAME/" | awk '{print $1}')
ESCAPEDZFSSNAPSHOTBASE=$(echo "$ZFSSNAPSHOTBASE" | sed 's/\//\\\//')
PIDFILE=/var/lock/$RUNNAME.pid
ISOTIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
YEAR=$(date -u +"%Y")
createPIDFile
zfs snapshot -r "$ZFSSNAPSHOTBASE@$ZFSSNAPSHOTNAME"
trap "trapCleanup" INT
if [ -f "/var/log/$RUNNAME.log.bz2" ]; then
bunzip2 "/var/log/$RUNNAME.log.bz2"
fi
mountSnapshots
# Do tha backup dance!
if [[ $BWLIMIT ]]; then
rclone --config "$RCLONECONFIGPATH" sync "/mnt/$RUNNAME" "$2:$BASENAME/current" --backup-dir "$2:$BASENAME/$YEAR/$ISOTIME" --transfers $TRANSFERS --log-file /var/log/$RUNNAME.log --log-level INFO --bwlimit $BWLIMIT
else
rclone --config "$RCLONECONFIGPATH" sync "/mnt/$RUNNAME" "$2:$BASENAME/current" --backup-dir "$2:$BASENAME/$YEAR/$ISOTIME" --transfers $TRANSFERS --log-file /var/log/$RUNNAME.log --log-level INFO
fi
# Create filelist
# | sed 's/\[/\\\[/g' | sed 's/\]/\\\]/g' | sed 's/\?/\\\?/g' | sed 's/\*/\\\*/g' | sed 's/{/\\{/g' | sed 's/}/\\}/g'
cd "/mnt/$RUNNAME"
find . | sed 's/^.\/*\(.*\)/\1/' | sed '/^[[:space:]]*$/d' | bzip2 > "/tmp/filelist-$RUNNAME.bz2"
rclone --config "$RCLONECONFIGPATH" copyto "/tmp/filelist-$RUNNAME.bz2" "$2:$BASENAME/$YEAR/$ISOTIME/filelist.bz2"
rm "/tmp/filelist-$RUNNAME.bz2"
cleanup
else
echo "Argument should be: path remote"
fi
@johnpwhite
Copy link

Hi, I'm a noob at this, could you add some destructions, or elaborate on the comments in the script to talk one through it please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment