Featured image of post TrueNAS SCALE: ZFS snapshots and replication that actually recover

TrueNAS SCALE: ZFS snapshots and replication that actually recover

ZFS snapshots are cheap, instant and atomic, which makes them easy to accumulate into something useless: thousands of snapshots nobody prunes, on a pool with no second copy. TrueNAS SCALE gives you the tools — periodic snapshot tasks, replication tasks, and dataset-level control — but the layout decision in front of them is what determines whether a restore takes minutes or days.

1. Lay out datasets for recovery, not for tidiness

One dataset per data class, and replication at the dataset boundary:

/mnt/tank
├── tank/vm            # zvols for VMs — replicate every 15 min
├── tank/containers    # docker appdata — replicate hourly
├── tank/media         # photos, video — replicate daily
└── tank/documents     # irreplaceable — replicate hourly + cloud copy

A single tank/data dataset means a replication task for photos carries the write amplification of the whole pool, and a rollback of one file class rolls back everything. Split them.

Recordsize matters for the same reason: recordsize=16K for VM zvols (with volblocksize=16K at creation), 1M for media, default 128K for mixed. You cannot change a zvol's blocksize after creation — recreate the zvol and copy the data before it holds anything important.

2. Snapshot tasks

Via the UI: Data Protection → Periodic Snapshot Tasks. Via CLI, so it is reviewable and repeatable:

midclt call pool.snapshottask.create '{
  "dataset": "tank/containers",
  "recursive": true,
  "naming_schema": "auto-%Y-%m-%d_%H-%M",
  "schedule": {"minute": "0", "hour": "*"},
  "lifetime": "14",
  "enabled": true
}'

recursive: true is the important flag: it snapshots child datasets atomically, which keeps a database directory and its config in the same instant.

lifetime is in days and is the only thing stopping snapshot sprawl. Twelve snapshots of a churning docker appdata can be hundreds of GB of space that ZFS cannot reclaim until they expire.

3. Replication to the second copy

A snapshot on the same pool is not a backup. Replicate to a second pool — internal, USB-attached for cold copies, or a second NAS:

midclt call replication.create '{
  "name": "containers-to-backup",
  "direction": "PUSH",
  "transport": "SSH",
  "source_datasets": ["tank/containers"],
  "target_dataset": "backup/containers",
  "recursive": true,
  "auto": true,
  "retention_policy": "SOURCE",
  "ssh_credentials": {"host": "backup.lan", "private_key": 1},
  "schedule": {"minute": "30", "hour": "*"}
}'

retention_policy: SOURCE means the destination keeps the same snapshots as the source — you prune in one place. CUSTOM lets the destination keep more, which is what you want for the off-site copy where bandwidth limits how often you can ship: keep dailies locally for a week, keep monthlies remotely for a year.

4. Test a rollback, not just a send

# what exists
zfs list -t snapshot -o name,used,refer tank/containers | tail -5

# non-destructive check: mount a snapshot and compare
zfs clone tank/containers@auto-2026-10-20_09-00 tank/restore-test
ls /mnt/tank/restore-test
zfs destroy tank/restore-test

# the real thing: roll back a dataset
zfs rollback -r tank/containers@auto-2026-10-20_09-00

zfs rollback -r destroys every snapshot newer than the target. On a replicated dataset that divergence is then pushed to the destination on the next run — which is correct, and also why you test on a clone first.

Pitfalls

  • Snapshots are not free on write-heavy datasets. They pin blocks. A 12-snapshot rotation on a churning zvol can consume more space than the zvol itself.
  • zfs rollback is local only. The remote keeps its older chain until the next replication sync; check zfs list -t snapshot on both sides, not just the source.
  • Replication before the snapshot task runs silently sends nothing. Order the schedules or use auto: true, which replicates the newest common snapshot.
  • Scrubs are not optional. Schedule a monthly scrub on both pools; that is how ZFS detects bit rot before your restore discovers it.
  • Encryption keys. If a dataset is encrypted, the replication target needs the key to mount. Test that the key exists in a password manager, not only in the boot pool.

A snapshot without an off-pool copy is a rollback convenience, not a backup. Both, or neither counts.

Built with Hugo
Theme Stack designed by Jimmy