Welcome to Zelta! This guide will get you syncing ZFS datasets in minutes. We'll cover installation, basic operations, and setting up automated policy-based replication.
Zelta has been battle-tested in production for over six years, managing tens of millions of snapshots. It runs on most UNIX and UNIX-like systems with zero package dependencies—just Bourne shell and AWK.
git clone https://github.com/bellhyve/zelta.git
cd zelta
sudo ./install.sh
The installer will guide you through setup. For non-root installation, see the installer output for user-mode configuration. The zelta binary be installed in the user's PATH, and for non-global installations, some environment variables must be set.
Zelta 1.0 (March 2024) is available in FreeBSD ports. For the latest features in v1.1, install from GitHub.
pkg install zelta
For detailed installation instructions and configuration options, see Installation & Configuration.
Zelta uses an SCP-like syntax to specify datasets and snapshots:
Format: [username@][hostname:]pool[/dataset][@snapshot]
Examples:
tank/dataserver10.biz:tank/filesbackup@server11.biz:pool/stufftwin@server12.biz:sink/log@todayreadonly=onzfs allow for delegationZelta does not need to be installed on backup sources or targets. Using SSH keys and agent forwarding, you can manage all replication from a secure bastion host.
Plus, there's no need to run Zelta as root. Using ZFS delegation and SSH keys, you can safely replicate datasets with limited access to keep your backup servers and orchestrators as hardened as possible.
On source systems, delegate send permissions:
# As root, grant minimal permissions to backup user
zfs allow -u backupuser bookmark,hold,send,snapshot sink/data
On target systems, delegate receive permissions:
# As root, grant receive permissions
zfs allow -u backupuser canmount,clone,compression,create,mount,readonly,receive,recordsize,rename tank/backups
The above provides a useful set of compatible permissions for a wide array of backup and recovery scenarios. Your specific zfs allow options should be tuned to take advantage of your particular replication scenario and the latest ZFS features available. See our detailed guides:
Before replicating, let's see what we're working with:
zelta match tank/data tank/backups/data
This shows you the relationship between two dataset trees—matching snapshots, discrepancies, or if oen side is missing. It's essential for validating replication and planning your backup strategy.
Replicate a dataset tree to a local backup:
zelta backup tank/data tank/backups/data
What just happened:
tank/data tree was replicated recursivelyreadonly=oninheritRun the same command again later to update incrementally. Zelta automatically detects the optimal zfs send method.
The syntax is identical for remote replication:
zelta backup tank/data backup@storage.example.com:pool/backups/data
Zelta uses SSH to stream the replication. Make sure you've set up SSH keys and zfs allow permissions on both systems.
Confirm everything matches:
zelta match tank/data backup@storage.example.com:pool/backups/data
You should see matching snapshots across the entire tree.
For managing multiple replication jobs, zelta policy automates the process using a configuration file. This is ideal for production environments where you're backing up dozens or hundreds of datasets.
The default policy file is located at /usr/local/etc/zelta/zelta.conf. Here's a simple example:
# /usr/local/etc/zelta/zelta.conf
# Global settings (optional)
SNAP_NAME: "$(date -u +auto-%Y-%m-%d_%H-%M)"
# Site names like "Production" are used for organization
# and setting backup job concurrency.
Production:
# Source hostname: app-server-01
app-server-01:
- tank/www: backups/app-server-01/www
- tank/database: backups/app-server-01/database
# Source hostname: app-server-02
app-server-02:
- tank/www: backups/app-server-02/www
- tank/cache: backups/app-server-02/cache
Breaking it down:
Production is the user-defined site nameapp-server-01 and app-server-02 are source hostnamestank/www, tank/database, etc. are source datasetsbackups/app-server-01/www, etc. are local target replicasAbout SNAP_NAME: This creates timestamped snapshots using your system's date command. You can customize this to match your preferred naming convention, or leave it blank to use the default of zelta_YYYY-MM-DD_H.M.S.
Execute all backup jobs defined in the policy:
zelta policy
Run a specific site:
zelta policy Production
Run only a specific host within a site:
zelta policy app-server-01
Run only a specific dataset:
zelta policy tank/www
Use cron or your system's scheduler to run zelta policy automatically:
# Example crontab entry: run every 6 hours
PATH=/usr/local/bin:/usr/bin:/bin
0 */6 * * * zelta policy
For production environments, consider:
zelta matchYou now have the basics of Zelta replication. Here are some directions to explore:
zelta revert: Roll back a dataset in place without losing current statezelta rotate: Handle divergent histories without destructive receiveszelta clone: Create temporary read-write copies for testingRun zelta usage for quick command reference, or zelta help for the full manual.
Here's a practical example showing a complete backup workflow from initial setup to automated replication:
# 1. Set up delegation on source (as root)
ssh root@app-server-01 "zfs allow -u backupuser send,snapshot,hold tank/www"
# 2. Set up delegation on target (as root)
zfs allow -u backupuser create,mount,canmount,readonly,receive tank/backups
# 3. Initial replication (as backupuser)
zelta backup backupuser@app-server-01:tank/www tank/backups/app-server-01/www
# 4. Verify the backup
zelta match backupuser@app-server-01:tank/www tank/backups/app-server-01/www
# 5. Add to policy configuration
cat >> /usr/local/etc/zelta/zelta.conf << 'EOF'
Production:
app-server-01:
- tank/www: backups/app-server-01/www
EOF
# 6. Run policy-based replication
zelta policy
# 7. Schedule automated backups
echo "0 */6 * * * /usr/local/bin/zelta policy" | crontab -
That's it. You now have automated, incremental, cryptographically verified backups running every 6 hours. No daemons, no configuration drift, no bull.