| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source

Snapshots in Practice — protect everything, recover anything

A ZFS snapshot is an instant, read-only copy of a dataset at a point in time. It costs nothing to create, uses no extra space until data changes, and can be rolled back in seconds. kldload ships ksnap to make the common operations fast, and sanoid to automate them in the background.

The mindset shift: On ext4 or xfs, snapshots require LVM or a volume manager and are cumbersome. On ZFS, snapshots are a first-class filesystem operation. They are so cheap that you should be taking them before every significant action — package installs, config changes, database migrations, anything you might want to undo.


ksnap command reference

Create a snapshot

# Snapshot all key datasets (root, home, var, data)
ksnap

# Snapshot a specific path
ksnap /home
ksnap /srv/myapp

# Snapshot with a label
ksnap /home before-migration

Snapshot naming: manual-YYYYMMDD-HHMMSS by default, or before-migration-YYYYMMDD-HHMMSS if you provide a label. The timestamp prevents name collisions if you run it multiple times in a day.

List snapshots

ksnap list
ksnap list /home

Expected output:

DATASET              SNAPSHOT                        USED    DATE
rpool/ROOT/default   manual-20260325-091200          0       2026-03-25 09:12
rpool/ROOT/default   manual-20260320-150000          84 MiB  2026-03-20 15:00
rpool/home           manual-20260325-091200          0       2026-03-25 09:12
rpool/home           before-migration-20260318       210 MiB 2026-03-18 11:30
rpool/data           sanoid-hourly-2026-03-25-08h    0       2026-03-25 08:00
rpool/data           sanoid-daily-2026-03-24         1.2 GiB 2026-03-24 00:00

The USED column shows how much data has changed since that snapshot was taken. A snapshot at 0 means nothing has changed in that dataset since the snapshot. Sanoid-managed snapshots are listed alongside manual ones.

Roll back a dataset

ksnap rollback /home
ksnap rollback /home before-migration-20260318
Warning: Rolling back a dataset destroys all data written after the snapshot was taken. This is irreversible. If you need to preserve the current state, take a new snapshot before rolling back: ksnap /home before-rollback.

If you do not specify a snapshot name, ksnap rollback rolls back to the most recent snapshot for that dataset.

Destroy a snapshot

ksnap destroy rpool/home@manual-20260101-120000

Snapshots do not expire automatically unless managed by sanoid. Destroy old snapshots periodically to reclaim the space they hold.

Snapshot all datasets

ksnap all

Takes a snapshot of every ZFS dataset on the system — root, home, var, data, and any custom datasets. Useful before major maintenance windows.


Real-world scenarios

Before installing packages

Snapshot root before installing or upgrading software. If a package pulls in a bad dependency or overwrites a config file, roll back in seconds.

ksnap / && kpkg install something-risky

Before a database migration

Snapshot the data dataset before running schema migrations. If the migration breaks, roll back the data and the schema together.

ksnap /srv/postgres before-migration-v2 && ./migrate.sh

Before a maintenance window

Snapshot everything before a scheduled maintenance window — kernel update, config change, certificate rotation. One command covers all datasets.

ksnap all

Recover a deleted file

Accidentally deleted a file? Mount the snapshot as a read-only directory and copy the file out. No rollback needed — the live dataset stays intact.

see below

Recovering a deleted file from a snapshot

ZFS snapshots are accessible under the .zfs/snapshot/ hidden directory of the dataset mountpoint. No mounting required — the directory is always there:

# List available snapshots for /home
ls /home/.zfs/snapshot/

# Find the file in a snapshot
ls /home/.zfs/snapshot/manual-20260325-091200/todd/

# Copy it back
cp /home/.zfs/snapshot/manual-20260325-091200/todd/important.conf \
   /home/todd/important.conf

The .zfs/ directory is invisible to standard tools like ls but is accessible if you reference it directly. No special commands, no mounting — just a path.


Comparing snapshots with zfs diff

zfs diff shows exactly what changed between two snapshots, or between a snapshot and the current state of a dataset. Useful for auditing changes or understanding what a script modified.

# What changed since the last snapshot?
zfs diff rpool/home@manual-20260325-091200

# What changed between two specific snapshots?
zfs diff rpool/data@before-migration-v1 rpool/data@before-migration-v2

# What changed in root since before the last upgrade?
zfs diff rpool/ROOT/default@pre-upgrade-20260320

Sample output:

M       /home/todd/.bashrc
+       /home/todd/projects/newfile.py
-       /home/todd/tmp/scratch.txt
R       /home/todd/notes.md -> /home/todd/docs/notes.md

Codes: M modified, + added, - removed, R renamed. This makes zfs diff a lightweight audit trail for any dataset.


Automatic snapshots with sanoid

Manual snapshots protect you from things you know are risky. sanoid protects you from things you did not know were going to happen. It runs as a systemd timer and takes snapshots on a configurable schedule, retaining them for a configurable period.

sanoid is installed and running by default on Desktop and Server profiles. Configuration lives at /etc/sanoid/sanoid.conf:

[rpool/home]
    use_template = production

[rpool/data]
    use_template = production

[template_production]
    hourly = 24      # keep 24 hourly snapshots
    daily = 30       # keep 30 daily snapshots
    monthly = 3      # keep 3 monthly snapshots
    autosnap = yes
    autoprune = yes

Check sanoid status:

systemctl status sanoid.timer
systemctl status sanoid.service

# See what sanoid has been doing
journalctl -u sanoid.service --since "24 hours ago"

# List sanoid-managed snapshots
ksnap list | grep sanoid

sanoid names its snapshots with prefixes like sanoid-hourly-2026-03-25-08h and sanoid-daily-2026-03-24. These are separate from manual ksnap snapshots and are pruned automatically according to the retention policy.


Snapshot space usage

A new snapshot uses zero space. It only grows as the underlying data changes. The USED column in ksnap list shows how much unique data each snapshot holds — data that would be freed if that snapshot were destroyed.

# Detailed space breakdown
zfs list -t snapshot -o name,used,referenced,written rpool/home

If snapshots are accumulating and consuming significant space:

# See total snapshot space per dataset
zfs list -o name,usedbysnapshots rpool

# Destroy all manual snapshots older than 30 days (example — adjust as needed)
zfs list -H -t snapshot -o name rpool/home | \
  awk -F@ '/manual-/ { print $2 }' | \
  sort | head -n -5 | \
  while read snap; do zfs destroy "rpool/home@$snap"; done
Rollback warning: Rolling back a dataset to a snapshot destroys all snapshots taken after that snapshot, in addition to all data written after it. There is no undo. If you want to preserve the current state, clone the dataset first: zfs clone rpool/home@current-state rpool/home-backup.

Sending snapshots offsite

zfs send and zfs recv turn snapshots into a replication stream. You can send the full state of a dataset to another pool — on the same machine, a remote host, or an external drive:

# Initial full send to a backup pool
zfs send rpool/home@sanoid-daily-2026-03-24 | \
  zfs recv backup/home

# Incremental send — only the changes since last time
zfs send -i rpool/home@sanoid-daily-2026-03-23 \
            rpool/home@sanoid-daily-2026-03-24 | \
  zfs recv backup/home

# Send to a remote host over SSH
zfs send rpool/data@sanoid-daily-2026-03-24 | \
  ssh backup-host zfs recv tank/data

syncoid (installed alongside sanoid) automates incremental sends and handles the bookmarking and error recovery:

# Replicate home to a remote backup server
syncoid rpool/home backup-host:tank/home

# Replicate all datasets
syncoid --recursive rpool backup-host:tank