Skip to content

Datasets and Properties

A pool is one bucket. Datasets are how you organise what's in the bucket — one dataset per job, each with its own settings, its own snapshots, and its own space rules. This chapter is mostly properties: the small knobs that turn a dataset from "a folder" into "a folder that fits its job exactly".

Making datasets

sudo zfs create tank/photos
sudo zfs create tank/media
sudo zfs create tank/backups
sudo zfs create tank/photos/2026

Datasets nest like folders, mount automatically under the pool's mountpoint (/tank/photos, and so on), and can be created and destroyed without touching the pool or each other:

$ zfs list
NAME              USED  AVAIL  REFER  MOUNTPOINT
tank              212G   700G    96K  /tank
tank/backups      120G   700G   120G  /tank/backups
tank/media         80G   700G    80G  /tank/media
tank/photos        12G   700G    12G  /tank/photos
tank/photos/2026    2G   700G     2G  /tank/photos/2026

Notice AVAIL is the same everywhere: without limits, every dataset draws on the whole pool's free space. That is the point of a pool — and the reason for the quotas below, when one dataset's appetite must not starve the others.

Properties: set once, inherited below

Read any property with zfs get, set it with zfs set, and undo a local setting with zfs inherit:

zfs get compression tank/photos
sudo zfs set compression=lz4 tank
zfs get -r compression tank   # -r: show the whole tree

The output tells you the source of the value — local (set here), inherited from tank (set by a parent), or default. This is the property system's one rule: a setting on a parent applies to everything under it, unless a child overrides it. Set your opinions at the pool root once, and make exceptions dataset by dataset.

Two cautions that save real confusion. Most property changes apply only to new writes — setting compression on a dataset full of old files does not recompress them (copy the files afresh if you want that). And a handful of properties are fixed at dataset creation (the big one, recordsize, can be changed, but again only reshapes new writes).

The properties worth knowing

Property Plain meaning Home guidance
compression Compress blocks as they are written lz4 everywhere. It is fast, it spots incompressible data and gives up cheaply, and smaller data is also faster data
atime Record when files are read off (set at pool creation) — almost nobody needs read-times
recordsize The block size ZFS thinks in for a dataset Leave at the 128K default, except: 1M suits datasets of huge files (media, disk images), and small values suit databases. Match the workload, not the rumour
quota A hard ceiling on a dataset's size Use on the dataset that might eat the pool — a backups target, a child's media folder
reservation Space guaranteed to a dataset The opposite promise: this much is held for this dataset no matter what the others do
copies Keep 2 (or 3) copies of this dataset's blocks A way to buy extra safety for one precious dataset on an otherwise single-disk pool. Costs the space, obviously
readonly Refuse writes Handy for a finished archive nobody should "tidy"
mountpoint Where the dataset mounts none for parents you use purely as containers; legacy if another tool (fstab, a container runtime) should mount it
canmount Whether this dataset itself mounts noauto or off for container parents, so the parent doesn't shadow its children
sync How strictly writes wait for the disk Leave standard. disabled is faster and can lose the last seconds of writes in a crash — a fine trade for a scratch dataset, a terrible one by accident
dedup Store identical blocks once Off. It needs large amounts of RAM relative to pool size and saves most people almost nothing. The one property this guide flatly advises against
sharenfs / sharesmb Offer the dataset over the network A convenience that asks ZFS to register the share for you. Chapter 11 uses it in one recipe

A worked shape

A typical home server's tree, and why:

tank
├── media        compression=lz4, recordsize=1M
├── photos       compression=lz4, copies default
├── documents    the small, precious stuff
├── backups      quota set, receives other machines
├── vm           recordsize left small-ish, sync as the guest needs
└── scratch      sync=disabled, nobody cries if it vanishes

The pattern to copy is not the names — it is that each dataset exists because its job differs, and its properties say how. When every dataset is identical, you didn't need datasets; when each is tuned to its contents, snapshots and quotas (the next two chapters) become per-job tools instead of blunt ones.

Check: what did I just change?

zfs get all tank/photos | head -30
zfs get -r -o name,property,value,source all tank

The second command is the audit view: every non-default value in the tree, and where it came from. If you cannot explain a line in it, something was set by accident — zfs inherit <property> <dataset> puts it back to following its parent.