ZFS and Backups Revision as of Sunday, 26 March 2023 at 23:27 UTC

Backup all datasets and snapshots in a Zpool to an external USB drive. This person has basically the same approach except that they’re doing it via manual cron job.

Run all this stuff as root! Get weird errors with zfs not being able to unmount before receiving snapshots :/

#!/usr/local/bin/bash

export SOURCE_POOL="source"
export DESTINATION_POOL="backup"
export SNAPSHOT_LABEL="manual_monthly_backup"
export SNAPSHOT_LABEL_PREV="${SNAPSHOT_LABEL}_previous"

# --- Initial Backup ---

# First, take a snapshot of the pool to back up
# I have underlying underlying datasets, so make this recursive
zfs snapshot -r "$SOURCE_POOL@$SNAPSHOT_LABEL"

# Initial transfer. `pv` shows progress nicely. We
# are backing up all datasets and their snapshots.
zfs send -R "$SOURCE_POOL@$SNAPSHOT_LABEL" | pv | zfs receive -vF $DESTINATION_POOL

# --- All subsequent backups ---

# Incremental backups! First, rename the old
zfs rename -r $SOURCE_POOL@$SNAPSHOT_LABEL $SOURCE_POOL@$SNAPSHOT_LABEL_PREV

# Take a fresh new snapshot
zfs snapshot -r $SOURCE_POOL@$SNAPSHOT_LABEL

# Send incrementals to the destination. The `-i` flag
# will send the difference between the two arguments to
# the destination.
#
# If all intermediary snapshots are required, use '-I'
zfs send -R -i $SOURCE_POOL@$SNAPSHOT_LABEL_PREV $SOURCE_POOL@$SNAPSHOT_LABEL | pv | zfs receive -vF $DESTINATION_POOL

# Don't need the previous snapshots anymore... clean up
zfs destroy -r $SOURCE_POOL@$SNAPSHOT_LABEL_PREV
zfs destroy -r $DESTINATION_POOL@$SNAPSHOT_LABEL_PREV

cron Job

From that forum post up top:

(
    zfs rename -r mainpool@offsite-backup-new mainpool@offsite-backup-old;
    zfs snapshot -r mainpool@offsite-backup-new;
    zfs send -Ri mainpool@offsite-backup-old mainpool@offsite-backup-new | zfs recv -vFdu usbdrivepool;
    zfs destroy -r mainpool@offsite-backup-old;
    zfs destroy -r usbdrivepool@offsite-backup-old;
) | mail -s "FreeNAS Replication to USB Drive" "myemail@example.com"

Other Notes

I ended up just making a small script that uses rsync and snapshots. Sample:

TIMESTAMP=$(date "+%Y-%m-%dT%H.%M.%S")

zfs snapshot "backup/miscellaneous@$TIMESTAMP"
rsync -avWHh --progress --stats /mnt/orangepool/miscellaneous/ /mnt/backup/miscellaneous/

zfs snapshot "backup/media@$TIMESTAMP"
rsync -avWHh --progress --stats /mnt/orangepool/media/ /mnt/backup/media/

Other useful commands:

# View snapshots for a particular pool
zfs list -t snapshot -r mypool

# View snapshot names only for a given pool (silences the header too)
zfs list -r -t snapshot -o name -H backup

# Rename a snapshot
zfs rename mypool/dataset_foo mypool/dataset_bar