Cron based ZFS snapshot
ZFS snapshots are a simple and secure way to store a read-only state of a file system that can be restored in the event of accidental data loss, transferred, and replicated to another machine for backup.
I wanted the NAS to take a snapshot every day and keep the last 7 snapshots. Here’s how I did it:
# cat zfs_snapshot_create.sh
#!/usr/bin/env zsh
# Daily ZFS Snapshot with Pushover Notification
# CONFIGURATION
DATE=$(date +%Y-%m-%d)
SNAPSHOT_PREFIX="autobackup_${DATE}"
PUSHOVER_USER_KEY="${PUSHOVER_USER_KEY:-}" # set via env
PUSHOVER_API_TOKEN="${PUSHOVER_API_TOKEN:-}" # set via env
PUSHOVER_API_URL="https://api.pushover.net:443/1/messages.json"
LOGFILE="/var/log/zfs_snapshot_create.log"
HOSTNAME="$(hostname)"
# Function: Send pushover notification
send_pushover() {
local title="$1"
local message="$2"
[[ -n "$PUSHOVER_USER_KEY" && -n "$PUSHOVER_API_TOKEN" ]] || return
curl -s \
-F "token=${PUSHOVER_API_TOKEN}" \
-F "user=${PUSHOVER_USER_KEY}" \
-F "title=${title}" \
-F "message=${message}" \
${PUSHOVER_API_URL} >/dev/null
}
# Start Logging
{
echo "[$(date)] Starting ZFS snapshot creation..."
for pool in "tank" "rpool"; do
SNAPSHOT_NAME="${pool}@${SNAPSHOT_PREFIX}"
echo "Creating snapshot: ${SNAPSHOT_NAME}"
if /usr/sbin/zfs snapshot -r "${SNAPSHOT_NAME}"; then
echo "Snapshot ${SNAPSHOT_NAME} created successfully."
send_pushover "ZFS Snapshot Success (${HOSTNAME})" \
"Snapshot created: ${SNAPSHOT_NAME}"
else
echo "Error: Failed to create snapshot ${SNAPSHOT_NAME}"
send_pushover "ZFS Snapshot FAILED (${HOSTNAME})" \
"Snapshot failed: ${SNAPSHOT_NAME}"
fi
done
echo "[$(date)] Snapshot creation finished."
} >> "$LOGFILE" 2>&1
This script creates autobackup_$currentdate
snapshots recursively for the tank
and rpool
pools.
# cat zfs_snapshot_delete.sh
#!/usr/bin/zsh
# Configuration
PUSHOVER_USER_KEY="${PUSHOVER_USER_KEY:-}" # set via env
PUSHOVER_API_TOKEN="${PUSHOVER_API_TOKEN:-}" # set via env
PUSHOVER_API_URL="https://api.pushover.net:443/1/messages.json"
LOG_FILE="/var/log/zfs_snapshot_delete.log"
RETENTION=7 # Number of snapshots to retain
# Function to send Pushover notification
send_pushover_notification() {
local message=$1
local title=$2
curl -s \
-F "token=${PUSHOVER_API_TOKEN}" \
-F "user=${PUSHOVER_USER_KEY}" \
-F "message=${message}" \
-F "title=${title}" \
${PUSHOVER_API_URL} > /dev/null 2>&1
}
# Function to delete old snapshots for a given ZFS pool
delete_old_snapshots() {
local pool_name=$1
local pool_label=$2
echo "Checking snapshots for ${pool_name}..." | tee -a ${LOG_FILE}
# Get the snapshots and delete all except the last RETENTION number of snapshots
local snapshots
snapshots=$(/usr/sbin/zfs list -d 1 -o name -t snapshot | grep "${pool_name}@autobackup" | tail -n +$(($RETENTION + 1)))
if [ -n "$snapshots" ]; then
echo "Deleting the following snapshots:" | tee -a ${LOG_FILE}
echo "$snapshots" | tee -a ${LOG_FILE}
echo "$snapshots" | xargs /usr/sbin/zfs destroy -r
if [ $? -eq 0 ]; then
local message="Successfully deleted old snapshots for ${pool_label}. Retained ${RETENTION} most recent snapshots."
echo "$message" | tee -a ${LOG_FILE}
send_pushover_notification "$message" "ZFS Snapshot Cleanup"
else
local message="Failed to delete snapshots for ${pool_label}. Please check the logs."
echo "$message" | tee -a ${LOG_FILE}
send_pushover_notification "$message" "ZFS Snapshot Cleanup"
fi
else
# No snapshots to delete
local message="No snapshots to delete for ${pool_label}. All snapshots are within the retention limit."
echo "$message" | tee -a ${LOG_FILE}
send_pushover_notification "$message" "ZFS Snapshot Cleanup"
fi
}
# Main process
clear
echo "Starting ZFS snapshot cleanup process..." | tee -a ${LOG_FILE}
# send_pushover_notification "ZFS Snapshot Cleanup started." "ZFS Snapshot Cleanup"
# Delete snapshots for each pool
delete_old_snapshots "tank" "Tank Pool"
delete_old_snapshots "rpool" "Root Pool"
# Completion message
echo "ZFS snapshot cleanup completed." | tee -a ${LOG_FILE}
# send_pushover_notification "ZFS Snapshot Cleanup completed successfully." "ZFS Snapshot Cleanup"
To automatically run it, add the following to your root crontab:
# Create daily ZFS anapshots
10 6 * * * PUSHOVER_USER_KEY=xxxxx PUSHOVER_API_TOKEN=yyyyyyy /root/zfs_snapshot_create.sh
# Keep the last 7 snapshots and delete the rest:
20 6 * * * PUSHOVER_USER_KEY=xxxxx PUSHOVER_API_TOKEN=yyyyyyy /root/zfs_snapshot_delete.sh
This will create the snapshots every day at 6:10 AM and delete them at 6:20AM.