$linuxjunkies
>

Set Up an Encrypted ZFS Pool

Configure native ZFS encryption with aes-256-gcm, manage keys and boot unlock, rotate keys safely, and replicate datasets encrypted with zfs send -w.

AdvancedUbuntuDebianFedoraArch10 min readUpdated June 1, 2026

Before you start

  • Root or sudo access on the target system
  • OpenZFS 2.1 or later installed (2.2+ recommended)
  • One or more spare block devices or partitions — all data on them will be destroyed
  • Basic familiarity with ZFS pool and dataset concepts

ZFS native encryption, introduced in OpenZFS 0.8, lets you encrypt datasets or entire pools without a separate LUKS layer. The encryption lives inside the ZFS stack, so you keep all the usual ZFS features — snapshots, send/receive, compression — while your data stays ciphertext on disk. This guide walks through creating an encrypted pool, managing keys, and doing encrypted send/receive between hosts.

Prerequisites and Terminology

You need OpenZFS 2.1 or later for stable encryption support. OpenZFS 2.2+ fixes several edge cases around raw send; use it when possible. A few terms to keep straight:

  • Encryption root: the dataset that owns a key. Child datasets inherit from it unless you set their own key.
  • Keylocation / keyformat: how ZFS finds and interprets the key (passphrase, raw file, hex string).
  • Raw send (zfs send -w): sends encrypted ciphertext, not plaintext — the receiving host never sees unencrypted data.

Install OpenZFS

Debian / Ubuntu

sudo apt update
sudo apt install zfsutils-linux

Fedora / RHEL 9 / Rocky Linux 9

Enable the ZFS repo from openzfs.github.io, then:

sudo dnf install https://zfsonlinux.org/fedora/zfs-release-2-5$(rpm --eval "%{dist}").noarch.rpm
sudo dnf install zfs

Arch Linux

sudo pacman -S zfs-dkms zfs-utils

Confirm your version before continuing:

zfs version

Create an Encrypted Pool

The examples below use two drives (/dev/sdb and /dev/sdc) in a mirror. Adjust the vdev topology — stripe, raidz, single disk — as needed. All data on those drives will be destroyed.

Option A: Passphrase encryption at pool creation

sudo zpool create \
  -O encryption=aes-256-gcm \
  -O keyformat=passphrase \
  -O keylocation=prompt \
  -O compression=zstd \
  -O atime=off \
  tank mirror /dev/sdb /dev/sdc

ZFS will prompt for a passphrase twice. The pool and its root dataset are now the encryption root. Child datasets inherit the key automatically.

Option B: File-based key (for automated unlock)

Generate a 32-byte random key and store it somewhere safe — ideally on a separate encrypted volume or a secrets manager mount:

sudo dd if=/dev/urandom bs=32 count=1 | xxd -p -c 256 > /etc/zfs/tank.key
sudo chmod 400 /etc/zfs/tank.key
sudo zpool create \
  -O encryption=aes-256-gcm \
  -O keyformat=hex \
  -O keylocation=file:///etc/zfs/tank.key \
  -O compression=zstd \
  -O atime=off \
  tank mirror /dev/sdb /dev/sdc

Create Encrypted Datasets

The pool root dataset is already encrypted. Create child datasets as normal — they inherit the parent's encryption root and key:

sudo zfs create tank/data
sudo zfs create tank/backups

To give a child dataset its own independent key (so you can unmount and lock it separately):

sudo zfs create \
  -o encryption=aes-256-gcm \
  -o keyformat=passphrase \
  -o keylocation=prompt \
  tank/secrets

Verify the encryption root of each dataset:

sudo zfs get encryptionroot,encryption,keystatus tank tank/data tank/secrets

Load Keys and Mount on Boot

After a reboot, encrypted datasets are present but locked. You must load their keys before mounting.

Manual unlock

sudo zfs load-key tank
sudo zfs mount -a

Automatic unlock via systemd

For file-based keys, the ZFS utilities ship a systemd target and service you can hook into:

sudo systemctl enable zfs-import-cache.target
sudo systemctl enable zfs-mount.service
sudo systemctl enable zfs-load-key@tank.service

The zfs-load-key@ template unit calls zfs load-key <pool> at boot. It only works cleanly with keylocation=file://… or keylocation=https://…. For passphrase pools on a server, consider storing the passphrase in a systemd credential or a TPM-backed secret instead of a plain file.

Key Rotation and Changes

Rotate the key for an encryption root without re-encrypting the underlying data (ZFS re-wraps the data encryption key, not every block):

Change to a new passphrase

sudo zfs change-key -o keyformat=passphrase -o keylocation=prompt tank

Change to a new key file

sudo dd if=/dev/urandom bs=32 count=1 | xxd -p -c 256 > /etc/zfs/tank-new.key
sudo chmod 400 /etc/zfs/tank-new.key
sudo zfs change-key -o keyformat=hex -o keylocation=file:///etc/zfs/tank-new.key tank

Encrypted Send / Receive

Raw send (-w) transmits ciphertext. The receiving system never decrypts the data and does not need the key to store the stream. This is the correct way to replicate encrypted datasets to an untrusted backup host.

One-time full send

# On the source host:
sudo zfs snapshot tank/data@$(date +%Y%m%d)
sudo zfs send -w tank/data@20240601 | ssh backup-host sudo zfs receive -F backup/data

Incremental encrypted send

sudo zfs send -w -i tank/data@20240601 tank/data@20240602 \
  | ssh backup-host sudo zfs receive -F backup/data

On the receiving host, the dataset will be present but locked — keystatus shows unavailable. That is expected and correct for an untrusted replica. To decrypt it there, you would run zfs load-key with the same key that was used on the source.

Important: You cannot mix raw and non-raw send streams for the same dataset. If you ever send without -w, the receiving dataset becomes a plaintext copy and loses its encrypted status. Always use -w for encrypted replication.

Verification

Check that encryption is active and the key is loaded:

sudo zfs get encryption,keylocation,keyformat,keystatus,encryptionroot tank

Expected keystatus is available when the key is loaded, unavailable when locked. Confirm data is actually ciphertext on disk by reading raw blocks from an unmounted, locked dataset — you should see no recognizable plaintext. Lock the dataset first:

sudo zfs unmount tank/data
sudo zfs unload-key tank/data
sudo dd if=/dev/sdb bs=512 count=8 skip=2048 2>/dev/null | strings | head

If the pool is encrypted, strings will return nothing meaningful.

Troubleshooting

  • Key not found at boot: Verify the key file path is correct and readable by root before the ZFS mount service runs. If the file lives on another ZFS dataset, ordering matters — systemd unit dependencies get complex fast. Consider keylocation=https:// against a local secrets server instead.
  • Cannot receive: stream is not raw but target is encrypted: You sent without -w. Destroy the receiving dataset, recreate it, and always use zfs send -w.
  • zfs: encryption not supported: Your kernel module is older than 0.8, or you built without encryption support. Run modinfo zfs | grep version and compare against the OpenZFS release notes.
  • Pool imports fine but datasets not mounted: Keys are not loaded. Run zfs load-key -a to load all keys that have a reachable keylocation, then zfs mount -a.
  • Passphrase-based pool on a headless server won't auto-mount: This is by design. Use a file-based key stored securely, a TPM, or a network-bound disk encryption (NBDE) approach with Clevis/Tang.
tested on:Ubuntu 24.04Debian 12Fedora 40Arch rolling

Frequently asked questions

Does ZFS native encryption protect against a stolen drive?
Yes. With encryption=aes-256-gcm, all data blocks on disk are ciphertext. Without the key loaded, an attacker with physical access to the drive cannot read your data.
Can I add encryption to an existing unencrypted pool?
No. ZFS encryption must be set at dataset creation time. You must create a new encrypted dataset and copy or send data into it; there is no in-place encryption upgrade.
Does raw send preserve deduplication tables?
No. Raw send (-w) does not transmit dedup tables. The receiving pool deduplicates independently if dedup is enabled there, but the two tables are unrelated.
What happens if I lose the key file?
Your data is permanently unrecoverable. ZFS has no key escrow mechanism. Back up your key file to at least two separate secure locations, such as a password manager and offline encrypted storage.
Can I use ZFS encryption alongside LUKS?
Yes, and some high-security deployments layer both. LUKS protects at the block device level while ZFS encryption operates above it. The added complexity is only warranted in specific threat models; for most use cases ZFS native encryption alone is sufficient.

Related guides