Skip to content

Instantly share code, notes, and snippets.

@cnrd
Last active October 8, 2024 10:59
Show Gist options
  • 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