| name | zfs |
| description | ZFS (OpenZFS) space efficiency and snapshotting for FreeBSD and Linux developers. Use when cloning datasets, creating build sandboxes, replicating storage, or writing Rust/C code that works with ZFS pools on macOS, FreeBSD, or Linux. Trigger phrases: "zfs snapshot", "zfs clone", "COW dataset", "zpool", "send/receive", "zfs dedup", "OpenZFS", "FreeBSD storage", "build rollback on ZFS". Covers: clone, snapshot, send/receive, dedup, Rust patterns, FreeBSD vs Linux differences.
|
ZFS — OpenZFS Reference
ZFS is the COW filesystem for FreeBSD (first-class, ships by default since FreeBSD 13)
and available on Linux via OpenZFS (Ubuntu, Debian, Arch). Commands are identical across
platforms — pool naming and device paths differ.
The core rule
On ZFS, the unit of COW cloning is a dataset (filesystem or volume), not a file.
To clone a directory tree: snapshot the dataset, then clone the snapshot.
zfs snapshot pool/dataset@tag
zfs clone pool/dataset@tag pool/dataset-clone
Clone is instant, zero space until divergence. This is the ZFS equivalent of cp -c on APFS.
Quick reference
| Operation | Command | Notes |
|---|
| Create snapshot | zfs snapshot pool/ds@name | Instant, space = changed blocks only |
| List snapshots | zfs list -t snapshot | |
| Clone snapshot | zfs clone pool/ds@snap pool/ds-clone | COW — zero space at creation |
| Rollback | zfs rollback pool/ds@snap | Destroys data after snapshot |
| Destroy clone/snap | zfs destroy pool/ds@snap | Clone must be destroyed first |
| Send (backup) | zfs send pool/ds@snap | zfs receive backup/ds | Stream to remote or file |
| Incremental send | zfs send -i pool/ds@prev pool/ds@now | zfs receive backup/ds | Delta only |
| Space usage | zfs list -o name,used,avail,refer | |
| Enable dedup | zfs set dedup=on pool/ds | Block-level; RAM-intensive |
| Enable compression | zfs set compression=lz4 pool/ds | Almost always worth enabling |
Snapshots for build isolation
zfs snapshot pool/src@pre-migration-2026-03-07
zfs rollback pool/src@pre-migration-2026-03-07
zfs destroy pool/src@pre-migration-2026-03-07
Snapshots consume space proportional to blocks changed after the snapshot, not the full dataset.
Dataset clones for test environments
zfs snapshot pool/prod@test-base
zfs clone pool/prod@test-base pool/test
zfs destroy pool/test
zfs destroy pool/prod@test-base
Send/receive — replication
zfs send pool/ds@snap | ssh user@remote zfs receive backup/ds
zfs send -i pool/ds@prev pool/ds@now | ssh user@remote zfs receive -F backup/ds
zfs send pool/ds@snap | gzip > ds-backup.gz
zfs send pool/ds@snap > ds-backup.zfs
zfs receive pool/ds < ds-backup.zfs
Compression — always enable
zfs set compression=lz4 pool/dataset
zfs set compression=zstd pool/dataset
LZ4 compression typically saves 30–50% on source code and logs with near-zero CPU cost.
Enable at dataset creation; only new writes are compressed.
Dedup — use carefully
zfs set dedup=on pool/dataset
Block-level deduplication. Requires ~5GB RAM per TB of data for the dedup table.
Only worth enabling when data has high block-level duplication (VM images, backups).
NOT recommended for source code or build artifacts — too much RAM, little gain.
Rust patterns
ZFS has no stable clonefile()-style syscall for userspace. Options:
1. Shell out (simplest, recommended for most cases):
use std::process::Command;
fn zfs_snapshot(dataset: &str, tag: &str) -> std::io::Result<()> {
let status = Command::new("zfs")
.args(["snapshot", &format!("{dataset}@{tag}")])
.status()?;
if status.success() { Ok(()) } else {
Err(std::io::Error::other("zfs snapshot failed"))
}
}
fn zfs_clone(origin: &str, tag: &str, clone: &str) -> std::io::Result<()> {
let snap = format!("{origin}@{tag}");
let status = Command::new("zfs")
.args(["clone", &snap, clone])
.status()?;
if status.success() { Ok(()) } else {
Err(std::io::Error::other("zfs clone failed"))
}
}
2. libzfs FFI (advanced, avoids process overhead):
libzfs is available on FreeBSD and Linux but has no stable API — version-specific.
Use the zfs-sys crate if it exists for your OpenZFS version, otherwise shell out.
FreeBSD vs Linux differences
| Aspect | FreeBSD | Linux (OpenZFS) |
|---|
| Pool device naming | /dev/ada0, /dev/da0, /dev/gpt/label | /dev/sda, /dev/nvme0n1 |
| Import on boot | Automatic via /etc/rc.conf: zfs_enable="YES" | zpool import -a in initrd or systemd unit |
| Default pool name | zroot (installer default) | rpool (Ubuntu default) |
| ARC (cache) tuning | vfs.zfs.arc_max in /boot/loader.conf | zfs_arc_max in /etc/modprobe.d/zfs.conf |
| Encrypted root | Native ZFS encryption or GELI (FreeBSD) | Native ZFS encryption or LUKS |
zpool status | Identical | Identical |
zfs commands | Identical | Identical |
Space audit commands
zpool list
zfs list -o name,used,avail,refer
zfs list -t snapshot
zpool iostat -v
zdb -b pool/dataset
Pools — bare minimum setup
zpool create mypool /dev/ada1
zpool create mypool mirror /dev/ada1 /dev/ada2
zpool status mypool
zpool destroy mypool
Always enable compression on the pool or root dataset after creation:
zfs set compression=lz4 mypool