TL;DR

zfs hold keep pool/dataset@snapzfs destroy fails with dataset is busy. Release with zfs release.

Retention scripts (zfs destroy -r …@auto-…) do not ask. A hold is a named lock on a snapshot: destroy fails until you drop the tag.

Use it on the snapshot you are zfs sending, or on the only known-good rollback after an upgrade.

Take the hold

1
zfs hold keep tank/home/cindys@snap1

Recursive on a whole tree (snapshot first, then hold):

1
2
zfs snapshot -r tank/home@now
zfs hold -r keep tank/home@now

The tag (keep) is yours, but unique per snapshot. You can have both keep and offsite on the same @now.

What destroy does

1
2
zfs destroy tank/home/cindys@snap1
# cannot destroy 'tank/home/cindys@snap1': dataset is busy

That is the hold, not a mounted filesystem. zfs destroy -d defers deletion until holds are gone; it does not override them.

List

1
2
zfs holds tank/home@now
zfs holds -r tank/home@now
1
2
3
NAME                  TAG   TIMESTAMP
tank/home@now         keep  Thu Jul 15 11:25:39 2010
tank/home/cindys@now  keep  Thu Jul 15 11:25:39 2010

Release, then destroy

1
2
zfs release -r keep tank/home@now
zfs destroy -r tank/home@now

If a long send is still running, wait: the hold exists so prune cannot eat your incremental cursor.

Oracle’s description (same mechanism on OpenZFS): Holding ZFS snapshots.

See also: send into an encrypted dataset.