This script is provided “as is”, without warranty of any kind.
By using this script, you agree that I am not liable for any data loss, system damage, service interruption, or other issues that may occur as a result of running it.
This script performs destructive operations, including but not limited to:
- Creating ZFS snapshots
- Destroying ZFS snapshots
- Deleting log files (
.log,.err,.digest) - Executing system-level commands (
zfs,docker,mail)
SnapBeforeWatchTower is a Python 3 utility designed to safely manage ZFS snapshots around Docker environments.
It supports:
- Creating ZFS snapshots
- Capturing Docker image digests at snapshot time
- Enforcing snapshot retention (time + count)
- Cleaning up old snapshots and log groups
- Structured logging with automatic fallback
- Optional email notifications (errors and/or success)
The script must normally be run as root.
❗ ZFS is required
❗ Docker is required
❗ A dataset file is required
The script will always attempt to:
- Read datasets from a file passed via
--file - Run
docker images --digestswhen using--command create
If any mandatory requirement is missing or misconfigured, the script will fail safely.
- ✅ ZFS snapshot creation
- ✅ Safe snapshot deletion with strict name matching
- ✅ Guaranteed retention of newest snapshots
- ✅ Docker image digest capture (
.digestfile) - ✅ Dataset-driven operation
- ✅ Time-based + count-based retention
- ✅ Log rotation and cleanup
- ✅ Separate
.log,.err, and.digestfiles per run - ✅ Dry-run mode (no destructive changes)
- ✅ Optional email notifications
- ✅ Root enforcement with automatic
/tmpfallback logging
- Linux with ZFS
- Python 3.9+ recommended
The following must be available in $PATH:
zfsdockermail(only required if email notifications are used)
The dataset file must exist and contain one ZFS dataset per line.
Example datasets.txt:
tank/data
tank/docker
tank/vms
- Blank lines are ignored
- Invalid datasets will cause errors during execution
sudo ./SnapBeforeWatchTower.py \
--command create \
--file datasets.txt \
--older-than 7d \
--retain-count 10 \
--send-mail you@example.com \
--mail-on-successsudo ./SnapBeforeWatchTower.py \
--command delete \
--file datasets.txt \
--older-than 7d \
--retain-count 10 \
--send-mail you@example.comsudo ./SnapBeforeWatchTower.py \
--command create \
--file datasets.txt \
--older-than 7d \
--retain-count 10 \
--dry-run| Flag | Description |
|---|---|
-c, --command |
create or delete |
-f, --file |
Dataset file (required) |
-o, --older-than |
Retention cutoff (Nd, Nw, Nm) |
-r, --retain-count |
Always keep this many newest snapshots |
-s, --send-mail EMAIL |
Enable email notifications |
-mos, --mail-on-success |
Send mail only on success (requires -s) |
-d, --dry-run |
Show actions without making changes |
Only snapshots matching this exact format are managed:
SnapBeforeWatchTower-Date-YYYY-MM-DD_HH_MM_SS
Snapshots not matching this format are never deleted.
Retention is applied per dataset:
- Snapshots are sorted by timestamp (newest first)
- The newest
--retain-countsnapshots are always kept - Older snapshots are deleted only if:
- They are older than
--older-than - They are not in the retained set
- They are older than
This guarantees no accidental full snapshot deletion.
Each run produces a log group sharing the same timestamp:
.log→ main log (INFO + DEBUG).err→ errors only (removed if empty).digest→ Docker image digests (create mode only)
- Root execution →
./logs/ - Non-root execution →
/tmp/SnapBeforeWatchTower/
If not run as root:
- The script logs the error
- Optionally sends mail
- Exits without touching ZFS or Docker
- Sent only on failure
- Attaches:
- Latest
.log .erronly if non-empty
- Latest
- Sent only if run completed without errors
- Attaches:
- Latest
.log .erronly if non-empty
- Latest
No empty error files are ever attached.
- ❗ This script destroys ZFS snapshots
- ❗ Docker must be running for snapshot creation
- ❗ Dataset file must exist
- ❗ Must be run as root for full functionality
- ❗ Designed for automation (cron/systemd)
No license is implied unless explicitly added.
Use, modify, and run this script entirely at your own risk.