I'm currently snapshotting my ZFS-based NAS nightly and weekly, a process that has saved my ass a few times. However, while the creation of the snapshot is automatic (from cron), the deletion of old snapshots is still a manual task. Obviously there's a risk that if I get hit by a bus, or the manual task isn't carried out, the NAS will run out of disk space.

Does anyone have any good ways / scripts they use to manage the number of snapshots stored on their ZFS systems? Ideally, I'd like a script that iterates through all the snapshots for a given ZFS filesystem and deletes all but the last n snapshots for that filesystem.

E.g. I've got two filesystems, one called tank and another called sastank. Snapshots are named with the date on which they were created: sastank@AutoD-2011-12-13 so a simple sort command should list them in order. I'm looking to keep the last 2 week's worth of daily snapshots on tank, but only the last two days worth of snapshots on sastank.

up vote 34 down vote accepted

You may find something like this a little simpler

zfs list -t snapshot -o name | grep ^tank@Auto | tac | tail -n +16 | xargs -n 1 zfs destroy -r
  • output the list of snapshot (names only) with zfs list -t snaphot -o name
  • filter to keep only the ones that match tank@Auto with grep ^tank@Auto
  • reverse the list (previously sorted from oldest to newest) with tac
  • limit output to the 16th oldest result and following with tail -n +16
  • then destroy with xargs -n 1 zfs destroy -vr

deleting snapshots in reverse order is supposedly more efficient.

or sort in reverse order of creation

zfs list -t snapshot -o name -S creation | grep ^tank@Auto | tail -n +16 | xargs -n 1 zfs destroy -vr

Test it with ...|xargs -n 1 echo

  • 1
    I think this needs a sort -r before the sed command. sed seems to output the bottom of the list beyond the first 15 lines, which in the default sort is the most recent. Flipping the list means I get the oldest snapshots at the bottom. – growse Dec 14 '11 at 14:14
  • He stated "deleting snapshots in reverse order is supposedly more efficient", thus the sort order. – tgunr Mar 17 '17 at 18:03

More general case of getting most recent snapshot based on creation date, not by name.

zfs list -H -t snapshot -o name -S creation | head -1

Scoped to a specific filesystem name TestOne

zfs list -H -t snapshot -o name -S creation -d1 TestOne | head -1

-H:No header so that first line is a snapshot name

-t snapshot: List snapshots (list can list other things like pools and volumes)

-o name: Display the snapshot name property.

-S creation: Capital S denotes descending sort, based on creation time. This puts most recent snapshot as the first line.

-d1 TestOne: Says include children, which seems confusing but its because as far as this command is concerned, snapshots of TestOne are children. This will NOT list snapshots of volumes within TestOne such as TestOne/SubVol@someSnapshot.

| head -1: Pipe to head and only return first line.

  • 1
    Thanks for the -d1. That was the key to the question "How do I get all snapshots for a given dataset?" – Jonathon Reinhart Dec 28 '16 at 19:12

growse's didn't work on OpenIndiana for me. It didn't understand -0 for xargs.

If using sort, be aware that it sorts alphabetically which may not be desired as you are probably wanting to find the most recent.

Here is code that will delete all but the last snapshots.

Remove the 'echo' to go live.


zfs list -t snapshot -o name | grep ^$FS@${SNAPNAME} |  sed -n -e :a -e '1,${RETENTION}!{P;N;D;};N;ba' | xargs -n 1 echo zfs destroy -r

Sources: http://sed.sourceforge.net/sed1line.txt

  • 2
    Upvote because anyone who can use sed like that deserves it. – growse May 25 '12 at 23:05
  • 2
    After a recent software update, that sed string stopped working for me and it started deleting all snapshots! bad sed! Luckily the production server was old and stayed safe. I now use sed -n -e :a -e '1,${RETENTION}!{P;N;D;};N;ba' – Dan Buhler Aug 17 '12 at 19:58
  • I've never seen anyone SED like that. – Jason Aug 8 '17 at 2:44

This totally doesn't answer the question itself, but don't forget you can delete ranges of snapshots.

zfs destroy zpool1/dataset@20160918%20161107

Would destroy all snapshots from "20160918" to "20161107" inclusive. Either end may be left blank, to mean "oldest" or "newest". So you could cook something up that figures out the "n" then destroy "...%n"..

Sorry to resurrect an old question.

I may have solved this with some bash-fu.

 zfs list -t snapshot -o name | grep ^tank@AutoD- | sort -r | wc -l | xargs -n 1 expr -$NUM_TO_KEEP + | tr -d '\n' | xargs -0 -i bash -c "zfs list -t snapshot -o name | grep ^tank@AutoD- | sort -r | tail -n{} | sort |xargs -t -n 1 zfs destroy -r"

Wow. It feels so wrong.

The path for head is needed on Solaris, but should work without the path on other distros.


zfs list -rt snap -H -o name ${dataset} | \
/usr/gnu/bin/head -n -${retention} | xargs -n 1 zfs destroy -r

Your Answer


By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.