Skip to content

Instantly share code, notes, and snippets.

@john-clark
Last active September 3, 2025 03:30
Show Gist options
  • Save john-clark/53823bf6101fc1d5554c9471f88d377f to your computer and use it in GitHub Desktop.
Save john-clark/53823bf6101fc1d5554c9471f88d377f to your computer and use it in GitHub Desktop.
Freebsd ZFS Scripts

Note this is not working yet.

Before trying to restore a snapshot type as root

  • ./zfs_backup_root.sh backup

Restore the ZFS snapshot

  1. Boot from the ISO/CD
  2. Choose 1 or hit enter when starting the CD
  3. At the first screen choose Live System
  4. Log in as root (with no password)
  5. Type ifconfig and look at the ethernet adapter name
  6. Type dhclient vtnet0
  7. Type fetch -o - http://192.168.1.2/zfs_restore_root.sh | sh
  8. The system will reboot and the snapshot will be gone

This will mount a rw temp folder, download the script, roll back the filesystem to the last backed up zfs snapshot

#!/bin/sh
# zfs_backup_root.sh
# Run with root to make a system snapshot
PREFIX="mysnap"
DATASET="zroot"
backup() {
snap="${DATASET}@${PREFIX}-$(date +%Y%m%d-%H%M%S)"
zfs snapshot -r "$snap" && echo "Snapshot created: $snap" || echo "Snapshot Failed."
}
restore() {
# Print instructions for restoring from a Live CD
echo "To restore a snapshot, boot from a FreeBSD Live CD/ISO."
echo "1. Select 'Live CD' at the boot menu."
echo "2. Log in as root (no password)."
echo "3. Configure networking:"
echo " - Run 'ifconfig' to find your network interface (e.g., em0)."
echo " - Run 'dhclient <interface>' to obtain an IP address."
echo "4. Run the restore script:"
echo " fetch -o - http://<your-server-ip>/zfs_restore_root.sh | sh"
echo "Replace <your-server-ip> with the IP or URL of the server hosting the script."
}
list() {
echo "Snapshots for $DATASET with prefix '$PREFIX':"
zfs list -t snapshot -H -o name -s creation | grep "^${DATASET}@${PREFIX}-" | awk '{print $1}'
}
purge() {
SNAPS=$(zfs list -H -t snapshot -o name | grep "^${DATASET}@${PREFIX}-")
[ -z "$SNAPS" ] && { echo "No snapshosts found."; return 1; }
echo "Permanently delete these snapshots:"
echo "$SNAPS" | awk '{ print " -", $0 }'
echo "Type yes to confirm"
read CONFIRM
[ "$CONFIRM" != "yes" ] && { echo "Purge cancelled."; return 1; }
echo "$SNAPS" | while read SNAP; do
echo "Deleting snapshot: $SNAP"
if zfs destroy -r "$SNAP"; then
echo "Deleted $SNAP"
else
echo "Failed to delete $SNAP"
fi
done
}
case "$1" in
backup) backup ;;
restore) restore ;;
list) list ;;
purge) purge ;;
*) echo "Usage: $0 {backup|restore|list|purge}" ;;
esac
#!/bin/sh
# restore_root.sh
# Host this script on a webserver so you can
# fetch -o - http://192.168.1.2/restore_root.sh | sh
# LiveCD-safe restore to latest snapshot made by backup script
# Import the ZFS pool without mounting datasets
echo "Importing pool '$POOL' without mounting datasets..."
if zpool list "$POOL" >/dev/null 2>&1; then
echo "Pool '$POOL' already imported. Attempting to export and re-import..."
zpool export -f "$POOL" 2>/dev/null || { echo "Error: Failed to export pool '$POOL'."; exit 1; }
fi
if ! zpool import -N "$POOL"; then
echo "Error: Failed to import pool '$POOL'. Retrying with force..."
zpool import -f -N "$POOL" || { echo "Error: Failed to import pool '$POOL' after retry."; exit 1; }
fi
# Find the latest snapshot with the specified prefix
SNAP_NAME=$(zfs list -t snapshot -H -o name -s creation | grep "^${POOL}@${PREFIX}-" | tail -n 1)
if [ -z "$SNAP_NAME" ]; then
echo "Error: No snapshots found on '$POOL' with prefix '$PREFIX'."
exit 1
fi
echo "Latest snapshot: $SNAP_NAME"
# Detect the active root dataset
ROOT_DS=$(zfs list -H -o name | grep "^${POOL}/ROOT/" | head -n 1)
if [ -z "$ROOT_DS" ]; then
echo "Error: No active root dataset found under '$POOL/ROOT'."
exit 1
fi
echo "Active root dataset: $ROOT_DS"
# Check for newer snapshots that might prevent rollback
NEWER_SNAPS=$(zfs list -t snapshot -H -o name -s creation | grep "^${ROOT_DS}@" | awk -v snap="$SNAP_NAME" '$0 > snap')
if [ -n "$NEWER_SNAPS" ]; then
echo "Error: Newer snapshots exist, which will be destroyed by rollback:"
echo "$NEWER_SNAPS" | awk '{ print " -", $0 }'
echo "Type 'yes' to confirm destructive rollback:"
read -r CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Rollback cancelled."
exit 1
fi
fi
# Perform the rollback
echo "Rolling back '$ROOT_DS' to snapshot '$SNAP_NAME'..."
if ! zfs rollback -r "$SNAP_NAME"; then
echo "Error: Rollback failed."
exit 1
fi
echo "Rollback complete. Pool will be exported automatically."
echo "Reboot the system to boot into the restored filesystem:"
echo " reboot"
echo "Remove the Live CD/ISO after reboot."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment