ZFS snapshots provide instantaneous, space-efficient point-in-time copies of your data. Unlike traditional backups, ZFS snapshots are created in milliseconds regardless of dataset size, consume only the space of changed blocks, and can be used for rollback, replication, and cloning. This guide covers using ZFS snapshots for continuous data protection on your VPS.
ZFS Snapshot Basics
# Create a snapshot (instantaneous, regardless of size)
zfs snapshot tank/data@2024-01-15-1200
# List snapshots
zfs list -t snapshot
# Check snapshot space usage
zfs list -t snapshot -o name,used,refer
# Rollback to a snapshot (destroys all changes since)
zfs rollback tank/data@2024-01-15-1200
# Access snapshot data without rollback
ls /tank/data/.zfs/snapshot/2024-01-15-1200/
Automated Snapshots with sanoid
# Install sanoid
sudo apt install sanoid
# /etc/sanoid/sanoid.conf
[tank/data]
use_template = production
recursive = yes
[tank/vms]
use_template = production
[template_production]
frequently = 4 # Keep 4 x 15-minute snapshots
hourly = 24 # Keep 24 hourly snapshots
daily = 30 # Keep 30 daily snapshots
weekly = 8 # Keep 8 weekly snapshots
monthly = 12 # Keep 12 monthly snapshots
yearly = 2 # Keep 2 yearly snapshots
autosnap = yes
autoprune = yes
# Enable sanoid timer
sudo systemctl enable --now sanoid.timer
# Verify snapshots are being created
zfs list -t snapshot -r tank/data | tail -10
Remote Replication with syncoid
# syncoid (part of sanoid) sends ZFS snapshots to remote servers
# First send (full): transfers entire dataset
# Subsequent sends (incremental): only changed blocks
# Replicate to backup server
syncoid tank/data backup-server:backup/data
# Automated replication via cron
# /etc/cron.d/zfs-replicate
*/30 * * * * root syncoid --no-sync-snap tank/data backup@remote:backup/data >> /var/log/syncoid.log 2>&1
# With SSH key authentication
syncoid --sshkey /root/.ssh/backup_key tank/data backup@192.168.1.100:backup/data
Database-Consistent Snapshots
#!/bin/bash
# Flush and snapshot for consistent database backup
# MySQL: flush tables and lock
mysql -e "FLUSH TABLES WITH READ LOCK;"
# Create snapshot
zfs snapshot tank/mysql@$(date +%Y%m%d-%H%M%S)
# Release lock
mysql -e "UNLOCK TABLES;"
# PostgreSQL: use pg_start_backup for consistency
sudo -u postgres psql -c "SELECT pg_start_backup('zfs_snap');"
zfs snapshot tank/postgres@$(date +%Y%m%d-%H%M%S)
sudo -u postgres psql -c "SELECT pg_stop_backup();"
Clone from Snapshots
# Create writable clone from snapshot (instant, no copy)
zfs clone tank/data@2024-01-15 tank/data-test
# Use for:
# - Testing migrations on production data copy
# - Development environments
# - Quick restore verification
# Destroy clone when done
zfs destroy tank/data-test
Monitoring
#!/bin/bash
# Check snapshot age and count
LATEST=$(zfs list -t snapshot -r tank/data -o creation -Hp | tail -1)
AGE=$(( ($(date +%s) - $LATEST) / 3600 ))
if [ "$AGE" -gt 2 ]; then
echo "WARNING: Latest snapshot is ${AGE}h old"
fi
# Check snapshot space usage
SNAP_USED=$(zfs list -t snapshot -r tank -o used -Hp | awk '{sum+=$1}END{print sum/1024/1024/1024}')
echo "Total snapshot space: ${SNAP_USED}GB"
Summary
ZFS snapshots provide the most efficient form of continuous data protection available. Instantaneous creation, block-level deduplication, and incremental replication make it possible to maintain minutes-granularity recovery points with minimal storage overhead. Combined with sanoid for automated management and syncoid for offsite replication, ZFS gives you a comprehensive backup strategy that traditional file-level backup tools cannot match in speed or efficiency.