zz is a lightweight Python utility designed to make ZFS off-site replication "Zeasy." It handles the heavy lifting of incremental sends, retention policies, and disaster recovery, ensuring your data is always backed up without the complexity of enterprise-grade storage orchestrators.
This project was started after years of frustration with other zfs replication tools. There are far more mature, feature rich, and scalable solutions out there. However, due to issues with setup, maintenance, breakage, recovery from breakage, and disaster recovery, and my own limitations, enough was enough. It started with two main tenants. Be simple and reliable. A person with minimal zfs knowledge (ability to create/modify/destroy pools and datasets) should be able to do any of the following in under a minute:
- Initialize and start replication with a replica server.
- Determine the status of the replication.
- Restore the primary from a replica and, once complete, resume replication with little or no fuss.
- Atomic Locking: Internal file locking prevents overlapping cron jobs from colliding.
- Sequential Catch-Up: Automatically detects and sends missing snapshot history if the network or server was down.
- Phase-Drift Correction: Built-in "slop" and timestamp flooring ensures syncs stay aligned with clock and minute boundaries.
- Dual Retention & Pruning: Maintain independent history windows (e.g., keep 1 hour of history locally but 30 days remotely).
- One-Command Recovery: Rebuild a lost local dataset from your remote target with a single
restorecommand. - Zero-Database: All configuration is stored directly in ZFS user properties on the dataset itself.
- Ensure Python 3.6+ is installed on your host (Tested on Rocky Linux 9).
- Download the
zzscript to/usr/local/bin/. - Make it executable:
chmod +x /usr/local/bin/zz
- Ensure SSH Key-Based Authentication is configured between the local and remote host.
To start backing up a dataset, use init. This performs the initial full transfer and sets the backup "contract."
zz init tank/data backup-server:pool/data --freq 5m --keep-local 1h --keep-remote 7dAdd zz sync to your crontab. It handles its own locking and timing checks.
* * * * * /usr/local/bin/zz sync >> /var/log/zz.log 2>&1View health, last sync time, and countdown for all managed datasets:
zz statusRecreate a lost dataset from the remote (includes all metadata and history):
zz restore backup-server:pool/data tank/dataRemove zz management but keep your data.
zz forget tank/dataUpdate a setting without re-initializing:
zz set tank/data freq 15mzz meta tank/datausage: zz [-h] <command> ...
Zeasy: Simplified ZFS Replication
options:
-h, --help show this help message and exit
Commands:
<command>
init Setup replication [dataset] [remote] [--freq] [--keep-local] [--keep-remote] [--keep-min]
sync Run incremental sync [dataset] [--force]
status Show health of all managed datasets
meta View configuration contract [dataset]
set Update properties [dataset] [prop] [value]
forget Stop tracking a dataset [dataset]
restore Recover from remote [remote] [local_dataset] [--latest]
Examples:
zz init tank/data backup:pool/data --freq 1h
zz restore backup:pool/data tank/data --latest
zz stores configuration in ZFS user properties. The settings move with the dataset.
| Property | Description | Default | Example |
|---|---|---|---|
| zz:target | Remote SSH target and path | - | 192.168.60.62:tank/test |
| zz:freq | How often to sync | 60m | 5m, 1h, 30d |
| zz:keep_local | Local retention window | 7d | 1h, 2h, 1d |
| zz:keep_remote | Remote retention window | 30d | 24h, 30d, 1y |
- Snapshots: zz only manages snapshots prefixed with @zz_auto_.
- Remote Integrity: zz uses incremental sends without the -F (Force) flag. Do not modify the remote dataset directly (keep it readonly=on) to avoid stream divergence.
- Lock Files: Stored in /tmp/zz_[dataset_name].lock to prevent overlapping runs.
- Phase-Drift: The script "floors" the last sync time to the nearest minute to prevent the schedule from slowly drifting forward.📄
License: MIT License
Keep it Zeasy.