ZFS - Tired of 0meg Snapshots Cluttering up? I made a script that only snaps if changes in dataset

simple-snapshot-zfs

This is a simple script to create ZFS Snapshots, designed to overcome snapshots with 0 changes cluttering your snapshot list.

To run, simply add script as a cron job.

If you use 3rd party programs to snapshot/restore, and use multiple snapshot policies, then don’t use this script. Some 3rd party programs rely on the 0 change snapshots for their date/time stamps when running restores/clones. In my case, I only have a few datasets that I self-manage. So it works great for me, as I want to avoid the visual clutter and don’t rely on 3rd party systems for snapshots or restores.

Key features

  • Runs a check to see if the dataset has been written to before snapshot

  • Prunes snapshots by keeping only the most recent 100 (adjustable)

  • Provides output to let you know how many snaps and how much space is taken up

Usage

Update script with your dataset names, including the pool. Don’t add a leading /, should look like: pool/dataset1

Run script manually or add it to a cron shedule

Sample output


Starting Snapshot 2023-03-23-23:21

_____________________________________________________________

No changes detected in pool/dataset1. No snapshot created.

Total snapshots for pool/admin: 3

Space used by snapshots for pool/dataset1: 239K

_____________________________________________________________

Snapshot created: pool/dataset1/code@2023-03-23-2321

Total snapshots for pool/admin/code: 20

Space used by snapshots for pool/dataset1/code: 18.0M

_____________________________________________________________

No changes detected in pool/dataset2. No snapshot created.

Total snapshots for pool/admin/certs: 3

Space used by snapshots for pool/dataset2: 947K

_____________________________________________________________

No changes detected in pool/documents. No snapshot created.

Total snapshots for pool/documents: 5

Space used by snapshots for pool/documents: 17.5G

_____________________________________________________________

No changes detected in pool/documents/nextcloud. No snapshot created.

Total snapshots for pool/documents/nextcloud: 4

Space used by snapshots for pool/documents/nextcloud: 20.0G

_____________________________________________________________

----------------------------Done!----------------------------

Changelog

v0.15

Address coding standards & issues

Define user configurable section, formatting

Updated Timestamp Formatting for readability

v0.10

Initial Commit

5 Likes

Nice!

With such an efficient system to create snapshots it is possible to create snapshots for all datasets. It would be nice not having to specify them all.

To snapshot all existing snapshots dynamically just change line 4 to the following:

DATASETS=( $(zfs list -o name -H) )

2 Likes

That’s great! I don’t run snapshots on my media dataset, hence I only list the ones that I want. But that is a good idea for those that want to snapshot everything. I’ll add that to the script tonight, with the option to comment that out, so user can choose what method they want to use.

This is a very useful script and your bash is very nice.

A couple of bash programming tips:
Since we are handling datasets in a script you probably want to set -e so the script exits if any of the commands fail.

Running it through shellcheck yelds a couple of warnings and shows a couple of very common bash pitfalls when using functions namely: local x=$(cmd ..) masks the return value of the command and it’s always 0, but that’s a minor thing :slight_smile:

The shellcheck output is below:

In script.sh line 12:
  local WRITTEN=$(zfs get -H -o value written "${DATASET}")
        ^-----^ SC2155 (warning): Declare and assign separately to avoid masking return values.


In script.sh line 15:
    local TIMESTAMP=$(date "+%Y%m%d-%H%M")
          ^-------^ SC2155 (warning): Declare and assign separately to avoid masking return values.


In script.sh line 28:
  local SNAPSHOTS=( $(zfs list -t snapshot -o name -s creation -r "${DATASET}" | grep "^${DATASET}@") )
                    ^-- SC2207 (warning): Prefer mapfile or read -a to split command output (or quote to avoid splitting).


In script.sh line 33:
  local SNAPSHOTS_SPACE=$(zfs get -H -o value usedbysnapshots "${DATASET}")
        ^-------------^ SC2155 (warning): Declare and assign separately to avoid masking return values.


In script.sh line 36:
  if [ ${SNAPSHOTS_COUNT} -gt ${KEEP} ]; then
       ^----------------^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean: 
  if [ "${SNAPSHOTS_COUNT}" -gt ${KEEP} ]; then

For more information:
  https://www.shellcheck.net/wiki/SC2155 -- Declare and assign separately to ...
  https://www.shellcheck.net/wiki/SC2207 -- Prefer mapfile or read -a to spli...
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
1 Like

Good advice. It was so simple/short for me that I didn’t bother with fail checks. But that would be very valuable for less experienced people using the script. I’ll spend some time this weekend looking at what you did there, sounds like a good v0.2!

Thanks!

Updated to version 0.15
fixed code to adhere to shellcheck best practices. (all but 1)
Created a user configurable section
cleaned up formatting
Updated snapshot Timestamp Formatting for readability

Next up is fixing that last shellcheck and adding logic to report user mis-configuration & properly report general errors.