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!----------------------------
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:
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
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 ...
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!
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.