Here is the process that I use to create local zfs backups.
Manual Backup Procedure
- Create Snapshots
- zfs snapshot -r storage/containers@backup-20240115
- -r: for recursive
- Create a RaidZ1 Pool as our backup target
- zpool create backup-pool raidz1 [DISK1] [DISK2] [DISK3] [DISK4]
- Send the initial snapshot
- zfs send -R --raw storage/containers@backup-20240115 | zfs receive -o readonly=on backup-pool/containers
- -R: for recursive
- --raw: so that encrypted data is not decrypted (not encrypted data will remain not encrypted)
- -o readonly=on: so that the backups are not editable
- Send incremental snapshots
- zfs send -R --raw -I storage/containers@backup-20240115 storage/containers@backup-20240210 | zfs receive backup-pool/containers
- -I [prev-snapshot]: incremental data since specified snapshot
Automating Backup Procedure
- Created the below scripts to help automate in /root/backup_scripts/
- snapshot.sh
- snapshot-all.sh
- backup.sh
- backup-all.sh
- Set the snapshot-all.sh script on crontab to run monthly
- crontab -e
- 05 8 1 * * sh /root/backup_scripts/snapshot-all.sh
- remember that the cron time is UTC
- Manually push the backups
- nohup sh backup-all.sh >>all.log 2>&1 &
Scripts
snapshot.sh
#!/bin/sh
DATASET=$1
zfs snapshot -r ${DATASET}@backup-$(date "+%Y%m%d")
snapshot-all.sh
#!/bin/sh
sh /root/backup_scripts/snapshot.sh rpool
sh /root/backup_scripts/snapshot.sh storage
backup.sh
#!/bin/sh
if [ $# -ne 2 ] ; then
echo "Requires 2 args source and destination"
exit 1
fi
src=$1
dest=$2
snaplist=$(zfs list -t snapshot $src)
if [ $? -ne 0 ] ; then
echo "No snapshots found for $src"
exit 1
fi
bkpsnap=$(zfs list -t snapshot $dest 2>&1)
incremental=$?
if [ $incremental -eq 0 ] ; then
incsnap=$(echo "$bkpsnap" | tail -n 1 | cut -f1 -d" " | cut -f2 -d"@")
snap=$(echo "$snaplist" | grep "$incsnap" -A 1 | tail -n 1 | cut -f1 -d" " | cut -f2 -d"@")
if [ "$incsnap" = "$snap" ] ; then
echo "Backup is already up to date"
exit 1
fi
echo "Sending incremental snap $src@$snap on top of $dest@$incsnap"
echo "zfs send -R --raw -I $src@$incsnap $src@$snap | zfs receive $dest"
zfs send -R --raw -I $src@$incsnap $src@$snap | zfs receive $dest
else
snap=$(echo "$snaplist" | tail -n +2 | head -n 1 | cut -f1 -d" " | cut -f2 -d"@")
echo "Sending initial snap $src@$snap to $dest"
echo "zfs send -R --raw $src@$snap | zfs receive -o readonly=on $dest"
zfs send -R --raw $src@$snap | zfs receive -o readonly=on $dest
fi
echo "Complete\n"
backup-all.sh
#!/bin/sh
echo "========================================"
echo "Backup on $(date +"%Y-%m-%d %H:%M:%S")"
echo ""
zpool import bpool
sh /root/backup_scripts/backup.sh rpool/data backup-pool/data
sh /root/backup_scripts/backup.sh storage/containers backup-pool/containers
sh /root/backup_scripts/backup.sh storage/encrypted backup-pool/encrypted
sh /root/backup_scripts/backup.sh storage/lists backup-pool/lists
echo "Complete at $(date +"%Y-%m-%d %h:%M:%S")"
echo "Performing scrub"
zpool scrub -w backup-pool
echo "Complete at $(date +"%Y-%m-%d %h:%M:%S")"
zpool status backup-pool
zpool export backup-pool
echo "Spinning down hard drives"
hdparm -y /dev/disk/by-id/ata-WDC_WD60EFPX-68C5ZN0_WD-XXXXXXX
hdparm -y /dev/disk/by-id/ata-WDC_WD60EFPX-68C5ZN0_WD-XXXXXXX
hdparm -y /dev/disk/by-id/ata-WDC_WD60EFZX-68B3FN0_WD-XXXXXXX
hdparm -y /dev/disk/by-id/ata-WDC_WD60EFZX-68B3FN0_WD-XXXXXXX
echo "Hard drives spun down"
echo "========================================"
Appendix
Sources