ZFS Encryption
ZFS Encryption Overview
Section titled “ZFS Encryption Overview”Definition. ZFS native encryption is a dataset-level encryption mechanism integrated into the ZFS storage stack, introduced in OpenZFS 0.8 (ZoL 0.8.0, FreeBSD 12.0). It encrypts data and Metadata at the block level before it is written to the pool, transparently decrypting on read. Unlike dm-crypt/LUKS, which operates at the block device layer below ZFS, native ZFS encryption is Aware of ZFS data structures and operates within the DMU (Data Management Unit).
Why Native ZFS Encryption Over dm-crypt/LUKS
Section titled “Why Native ZFS Encryption Over dm-crypt/LUKS”| Feature | ZFS Native Encryption | dm-crypt / LUKS |
|---|---|---|
| Encryption granularity | Per-dataset | Per-block device |
| Key management scope | Dataset hierarchy aware | Volume-level only |
| Send/receive integration | Raw mode preserves crypto | Not integrated |
| Snapshot encryption | Inherited automatically | Full volume encrypted |
| Deduplication support | Per-dataset keys | Single key per volume |
| Changing encryption | Per-dataset rekey | Requires full re-encrypt |
| TrueNAS integration | Full GUI support | Manual setup |
| Boot from encrypted pool | Supported (with key load) | Supported |
| Multiple encryption keys | Different keys per child | Single key per volume |
Native ZFS encryption provides the critical advantage of per-dataset key granularity. You can Encrypt one dataset with one passphrase and a sibling dataset with a different passphrase, all Within the same pool. With dm-crypt, the entire block device is encrypted with a single key, and There is no concept of per-directory or per-dataset keys.
Encryption in the ZFS Write Path
Section titled “Encryption in the ZFS Write Path”When encryption is enabled on a dataset, the encryption step is inserted between the DMU and the SPA In the ZFS write path:
graph TD
A[Application Write] --> B[ZPL]
B --> C[DMU]
C --> D[Compress]
D --> E[Encrypt]
E --> F[Checksum]
F --> G[Write to Pool]The order matters. Compression is applied before encryption because encrypted data is effectively Random and cannot be compressed. The checksum is computed on the encrypted data (not the plaintext), Which means scrubbing verifies the integrity of the encrypted ciphertext.
Encryption at Rest vs. Encryption in Transit
Section titled “Encryption at Rest vs. Encryption in Transit”ZFS native encryption is encryption at rest. It protects data on physical media — if a drive is Removed from the pool, the data is unreadable without the key. It does not protect data in transit. For network protection, use SMB3 encryption, NFS with Kerberos (krb5p), or a VPN.
Encryption Properties
Section titled “Encryption Properties”Core Encryption Properties
Section titled “Core Encryption Properties”ZFS exposes encryption configuration through dataset properties. These properties control how Encryption operates, what keys are used, and where keys are stored.
| Property | Values | Default | Set At | Description |
|---|---|---|---|---|
encryption | on, off, aes-256-gcm, aes-128-gcm, aes-256-ccm, chacha20-poly1305, aes-256-xts | off | Dataset creation only | Encryption algorithm or on/off toggle |
keyformat | none, passphrase, hex, raw | none | Dataset creation only | Format of the encryption key |
keylocation | prompt, file:///path, https://server/path | prompt | Dataset creation only | Where to read the encryption key from |
pbkdf2iters | Integer (iterations) | 350000 | Dataset creation only | PBKDF2 iterations for passphrase stretching |
encryptionroot | Read-only (dataset path) | - | Inherited | The root dataset that holds the master key |
keystatus | Read-only (available, unavailable, none) | - | Read-only | Whether the encryption key is loaded |
Setting Encryption Properties
Section titled “Setting Encryption Properties”Encryption properties can only be set at dataset creation time. They cannot be changed on an Existing dataset (with one exception: keylocation can be changed after creation). To change the Encryption algorithm or key format on an existing dataset, you must create a new dataset and copy The data.
# Create an encrypted dataset with all properties setzfs create -o encryption=on \ -o keyformat=passphrase \ -o keylocation=prompt \ -o pbkdf2iters=350000 \ tank/secret
# Check encryption propertieszfs get encryption,keyformat,keylocation,pbkdf2iters,encryptionroot,keystatus tank/secretOutput:
NAME PROPERTY VALUE SOURCEtank/secret encryption on localtank/secret keyformat passphrase localtank/secret keylocation prompt localtank/secret pbkdf2iters 350000 localtank/secret encryptionroot tank/secret -tank/secret keystatus available -Inherited Encryption
Section titled “Inherited Encryption”When you create a child dataset inside an encrypted parent, the child inherits the parent”s Encryption settings by default. The encryptionroot of the child points to the topmost encrypted Ancestor.
# Parent is encryptedzfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt tank/secret
# Child inherits encryption automaticallyzfs create tank/secret/docs
# Check child encryption propertieszfs get encryption,encryptionroot,keyformat,keystatus tank/secret/docsOutput:
NAME PROPERTY VALUE SOURCEtank/secret/docs encryption on inherited from tank/secrettank/secret/docs encryptionroot tank/secret -tank/secret/docs keyformat passphrase inherited from tank/secrettank/secret/docs keystatus available -The child dataset tank/secret/docs is encrypted with the same key as its parent. Loading the key On the parent (tank/secret) automatically makes the child accessible.
Overriding Inherited Encryption
Section titled “Overriding Inherited Encryption”A child dataset can override the parent’s encryption by specifying its own encryption properties at Creation time. This creates a new encryption root:
# Create child with its own encryption keyzfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ tank/secret/separate-vault
# This child has its own encryptionrootzfs get encryptionroot tank/secret/separate-vault# NAME PROPERTY VALUE# tank/secret/separate-vault encryptionroot tank/secret/separate-vaultNow tank/secret/separate-vault requires its own key, independent of tank/secret.
Encryption Algorithm Selection
Section titled “Encryption Algorithm Selection”| Algorithm | Key Size | Mode | Performance (AES-NI) | Use Case |
|---|---|---|---|---|
| aes-256-gcm | 256 bit | GCM (AEAD) | Fastest | Default, general purpose |
| aes-128-gcm | 128 bit | GCM (AEAD) | Fast | Slightly faster than 256, adequate |
| aes-256-ccm | 256 bit | CCM (AEAD) | Moderate | Legacy hardware without GCM support |
| chacha20-poly1305 | 256 bit | Stream | Moderate | CPUs without AES-NI instructions |
| aes-256-xts | 256 bit | XTS | Fast | Block device encryption (zvols) |
Definition. AEAD (Authenticated Encryption with Associated Data) modes such as GCM and CCM Combine encryption and authentication in a single operation. This means every encrypted block has an Integrity check built in — tampering with ciphertext is detected during decryption. This is in Addition to ZFS’s own checksum verification.
:::info aes-256-gcm is the default when encryption=on is specified. It provides the best Performance on modern CPUs with AES-NI support and is the recommended choice for all workloads. chacha20-poly1305 is the fallback for CPUs without AES-NI (e.g., some ARM SoCs). :::
pbkdf2iters Property
Section titled “pbkdf2iters Property”The pbkdf2iters property controls the number of PBKDF2 (Password-Based Key Derivation Function 2) Iterations used to stretch a user passphrase into a cryptographic key. Higher values make Brute-force attacks on weak passphrases more expensive.
| Iterations | Key Derivation Time (approx.) | Recommendation |
|---|---|---|
| 100000 | ~50 ms | Legacy default, too low |
| 350000 | ~150 ms | Current default, minimum |
| 500000 | ~200 ms | Good for sensitive data |
| 1000000 | ~400 ms | High-security environments |
:::caution Higher pbkdf2iters values increase the time to load the encryption key at boot. If you Set pbkdf2iters=1000000Every boot (or key load) will take an additional ~400 ms per dataset. This property only applies to keyformat=passphrase. It has no effect on hex or raw key Formats, which use the raw key material directly. :::
Key Management
Section titled “Key Management”Key Formats
Section titled “Key Formats”ZFS supports four key formats, each with different security and usability characteristics:
| Key Format | Source | Key Length | Security | Convenience | Use Case |
|---|---|---|---|---|---|
| passphrase | User-entered at prompt | Variable | High (if strong) | Low | Interactive use, laptops |
| hex | Hexadecimal string | 64 chars (256b) | Medium | High | Automated key loading |
| raw | Raw binary key file | 32 bytes (256b) | Medium | High | Automated, key files |
| none | No encryption | N/A | N/A | N/A | Unencrypted datasets |
passphrase Format
Section titled “passphrase Format”A passphrase is a human-memorable string that is stretched into a 256-bit key using PBKDF2-SHA512. The passphrase is prompted for interactively when the key needs to be loaded.
# Create dataset with passphrasezfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt tank/docs# Enter passphrase: ********# Re-enter passphrase: ********
# Load key (prompts for passphrase)zfs load-key tank/docs# Enter passphrase: ********Passphrase strengths:
| Type | Example | Entropy (approx.) | Security |
|---|---|---|---|
| Weak | password123 | ~10 bits | Broken |
| Moderate | Tr0ub4dour&3 | ~30 bits | Low |
| Strong | correct horse battery staple | ~60 bits | Moderate |
| Very strong | 7 random words (Diceware) | ~90 bits | Good |
| Excellent | 16+ random ASCII characters | ~105+ bits | Excellent |
:::info Use a Diceware passphrase (6-8 random words from a word list) or a randomly generated string Of 20+ characters. Store the passphrase in a password manager and write it down on paper stored in a Physically secure location (safe deposit box, fireproof safe). :::
hex Format
Section titled “hex Format”A hex key is a 64-character hexadecimal string representing a 256-bit key. It is stored in a file or Provided directly. No key stretching is applied — the hex string is used directly as the encryption Key.
# Generate a random 256-bit hex keyopenssl rand -hex 32# Output: a1b2c3d4e5f6... (64 hex characters)
# Create dataset with hex key from fileecho "a1b2c3d4e5f6...64chars" > /root/keys/tank_docs.keychmod 400 /root/keys/tank_docs.key
zfs create -o encryption=on -o keyformat=hex \ -o keylocation=file:///root/keys/tank_docs.key \ tank/docs
# Load key from filezfs load-key -L file:///root/keys/tank_docs.key tank/docsraw Format
Section titled “raw Format”A raw key is a 32-byte binary file containing the raw 256-bit key. Like hex, no key stretching is Performed.
# Generate a raw 32-byte keyopenssl rand -out /root/keys/tank_docs.raw 32chmod 400 /root/keys/tank_docs.raw
# Create dataset with raw keyzfs create -o encryption=on -o keyformat=raw \ -o keylocation=file:///root/keys/tank_docs.raw \ tank/docs
# Load keyzfs load-key -L file:///root/keys/tank_docs.raw tank/docskeylocation Options
Section titled “keylocation Options”The keylocation property specifies where ZFS finds the encryption key:
| keylocation | Behavior | Use Case |
|---|---|---|
prompt | Prompts on stdin at key load time | Passphrase, interactive use |
file:///absolute/path | Reads key from a local file | Automated key loading |
file:///path/to/directory | For encryptionroot: reads child key files | Multi-dataset key management |
https://server/path | Fetches key from an HTTPS URL | Remote key server |
Key Location Inheritance
Section titled “Key Location Inheritance”When a child dataset inherits encryption from a parent, it can use the parent’s key location scheme. For the file:// key format, you can set the parent’s keylocation to a directory path. Each child Dataset’s key file is expected to be named after the dataset within that directory:
# Parent points to a key directoryzfs create -o encryption=on -o keyformat=hex \ -o keylocation=file:///root/keys/tank \ tank/secret
# Key files for children go in /root/keys/tank/echo "hexkey1..." > /root/keys/tank/docsecho "hexkey2..." > /root/keys/tank/media
zfs create tank/secret/docs # Key read from /root/keys/tank/docszfs create tank/secret/media # Key read from /root/keys/tank/mediaLoading and Unloading Keys
Section titled “Loading and Unloading Keys”# Load a single dataset's key (prompts for passphrase)zfs load-key tank/secret
# Load key from a specific filezfs load-key -L file:///root/keys/tank_secret.key tank/secret
# Load keys recursively for all datasets under a rootzfs load-key -r tank/secret
# Load key for a specific datasetzfs load-key tank/secret/docs
# Unload a key (dataset becomes inaccessible)zfs unload-key tank/secret/docs
# Unload keys recursivelyzfs unload-key -r tank/secret
# Check which keys are loadedzfs get keystatus -r tankWhen a key is unloaded, the dataset and all its children are unmounted and become inaccessible. The Data remains on disk, encrypted, but cannot be read or written until the key is loaded again.
Changing Keys with zfs change-key
Section titled “Changing Keys with zfs change-key”The zfs change-key command allows you to change the encryption key for a dataset without Re-encrypting the data. The master key wrapping key is re-encrypted with the new key material, but The actual data encryption keys are preserved.
# Change passphrase for an existing encrypted datasetzfs change-key -o keyformat=passphrase -o keylocation=prompt tank/secret# Enter new passphrase: ********# Re-enter new passphrase: ********
# Change from passphrase to hex keyzfs change-key -o keyformat=hex -o keylocation=file:///root/keys/tank_secret.key tank/secret
# Change pbkdf2iters (requires re-entering the passphrase)zfs change-key -o pbkdf2iters=500000 tank/secret
# Change the encryption algorithm (requires full re-encryption)zfs change-key -o encryption=chacha20-poly1305 tank/secret:::caution Changing the encryption algorithm with zfs change-key triggers a full re-encryption of All data in the dataset. This is a long-running operation that consumes significant I/O bandwidth And CPU. Plan this for off-peak hours. Changing the passphrase or key format does not require Re-encryption. :::
Auto-Mount at Boot
Section titled “Auto-Mount at Boot”ZFS can automatically load encryption keys at boot for datasets that use key files (not Passphrases). Configure this by setting keylocation to a file path:
# Auto-load key from file at bootzfs create -o encryption=on -o keyformat=raw \ -o keylocation=file:///root/keys/tank_secret.raw \ tank/secret
# The key will be loaded automatically when the pool is imported at boot# Ensure the key file exists and is readable at boot time:::caution Storing the key file on the same pool that it decrypts defeats the purpose of encryption. If the pool is stolen, the key file is stolen with it. Store key files on a separate, secure Location — a USB drive, a separate small pool, or a remote key server. :::
Creating Encrypted Datasets
Section titled “Creating Encrypted Datasets”Step-by-Step: Creating an Encrypted Pool Root
Section titled “Step-by-Step: Creating an Encrypted Pool Root”While encryption is set at the dataset level (not pool level), you can create an encrypted root Dataset that all child datasets inherit from:
# 1. Create the pool (unencrypted at the pool level)zpool create -o ashift=12 -O compression=lz4 -O atime=off \ tank mirror /dev/sda /dev/sdb \ mirror /dev/sdc /dev/sdd
# 2. Create an encrypted root datasetzfs create -o encryption=on \ -o keyformat=passphrase \ -o keylocation=prompt \ -o pbkdf2iters=350000 \ tank/encrypted
# 3. Create child datasets (inherit encryption)zfs create -o recordsize=128K tank/encrypted/docszfs create -o recordsize=64K tank/encrypted/vmszfs create -o compression=off tank/encrypted/media
# 4. Verify inheritancezfs get encryption,encryptionroot,keystatus -r tank/encryptedCreating a Standalone Encrypted Dataset
Section titled “Creating a Standalone Encrypted Dataset”You do not need to encrypt the entire pool. You can encrypt individual datasets within an Unencrypted pool:
# Pool has mixed encrypted and unencrypted datasetszpool create -o ashift=12 tank mirror /dev/sda /dev/sdb
# Unencrypted dataset for public datazfs create -o compression=lz4 tank/public
# Encrypted dataset for sensitive datazfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ -o compression=zstd tank/secret
# Another encrypted dataset with different keyzfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ -o compression=lz4 tank/confidentialMixed Encryption Hierarchy
Section titled “Mixed Encryption Hierarchy”A pool can contain a mix of encrypted and unencrypted datasets at any level:
# Unencrypted rootzfs create tank/data
# Encrypted childzfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ tank/data/private
# Unencrypted siblingzfs create tank/data/public
# Encrypted grandchild (inherits from private)zfs create tank/data/private/documents
# Verify the hierarchyzfs get encryption,encryptionroot -r tank/dataCreating Encrypted zvols (Block Devices)
Section titled “Creating Encrypted zvols (Block Devices)”Zvols (ZFS volumes) can also be encrypted. This is useful for iSCSI LUNs or raw VM disk images:
# Create an encrypted zvol for iSCSIzfs create -V 100G \ -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ -o volblocksize=64K -o compression=lz4 \ tank/iscsi/encrypted-lun
# The zvol key must be loaded before the block device is usablezfs load-key tank/iscsi/encrypted-lun
# Verifyzfs get encryption,volsize,volblocksize,keystatus tank/iscsi/encrypted-lunEncryption and the Boot Pool
Section titled “Encryption and the Boot Pool”On TrueNAS, the boot pool ( boot-pool) is separate from the data pool. Encrypting the Boot pool is generally not recommended because:
- The bootloader must be able to load the kernel and initramfs.
- Key management for the boot pool adds complexity without significant security benefit (the boot pool contains the OS, not user data).
Encrypt user data pools instead. The TrueNAS boot pool should remain unencrypted.
Managing Encrypted Pools
Section titled “Managing Encrypted Pools”Importing Encrypted Pools
Section titled “Importing Encrypted Pools”When you import an encrypted pool, the datasets within it are not automatically accessible. You must Load the keys before mounting:
# 1. Import the poolzpool import tank
# 2. Check key status (all unavailable)zfs get keystatus -r tank/encrypted# NAME PROPERTY VALUE# tank/encrypted keystatus unavailable# tank/encrypted/docs keystatus unavailable
# 3. Load the encryption keyzfs load-key -r tank/encrypted# Enter passphrase for 'tank/encrypted': ********
# 4. Verify keys are loadedzfs get keystatus -r tank/encrypted# NAME PROPERTY VALUE# tank/encrypted keystatus available# tank/encrypted/docs keystatus available
# 5. Mount the datasetszfs mount -aImporting with Key Auto-Load
Section titled “Importing with Key Auto-Load”The -l flag on zpool import automatically attempts to load all encryption keys during import:
# Import and attempt to auto-load keyszpool import -l tank
# For keyformat=passphrase, this will prompt for each encrypted dataset# For keyformat=raw or hex with keylocation=file://, it will attempt to read the key files:::info On TrueNAS SCALE, the -l flag is used by default when importing pools at boot. If your Encrypted datasets use passphrase keys, TrueNAS will prompt you for the passphrase during boot. If They use key files, TrueNAS will attempt to load them from the specified file locations Automatically. :::
Exporting Encrypted Pools
Section titled “Exporting Encrypted Pools”Exporting an encrypted pool unloads all keys and unmounts all datasets:
# Export the pool (keys are unloaded automatically)zpool export tank
# After export, the data on disk is encrypted and inaccessible# To access it again, you must import and reload keysRecovery Scenarios
Section titled “Recovery Scenarios”Scenario: Forgot Passphrase
Section titled “Scenario: Forgot Passphrase”If you forget the passphrase for an encrypted dataset, the data is permanently irrecoverable. There is no backdoor, no recovery mechanism, no workaround. The encryption is designed to be Computationally infeasible to break.
:::caution There is no “forgot password” mechanism for ZFS encryption. If you lose the passphrase, The data is gone forever. Store passphrases in multiple secure locations: a password manager, a Physical safe deposit box, and a trusted family member’s possession. :::
Scenario: Key File Deleted
Section titled “Scenario: Key File Deleted”If the key file is deleted but you remember the passphrase (or have a backup of the key material), You can regenerate the key:
# If you have the passphrase:zfs change-key -o keyformat=passphrase -o keylocation=prompt tank/secret
# If you have a backup of the raw key:# Restore the key file from backup, then load itcp /backup/keys/tank_secret.raw /root/keys/tank_secret.rawzfs load-key -L file:///root/keys/tank_secret.raw tank/secretScenario: Pool on Removed Disks
Section titled “Scenario: Pool on Removed Disks”If drives are removed from the system and reconnected later:
# Import the poolzpool import tank
# Load keyszfs load-key -r tank/encrypted
# Verify data integrityzpool scrub tankScenario: System Failure with Key Files on Separate Media
Section titled “Scenario: System Failure with Key Files on Separate Media”If the system fails and key files were stored on a separate USB drive:
- Install TrueNAS on new hardware.
- Connect the pool drives and the USB drive with key files.
- Import the pool:
zpool import tank - Load keys from the USB drive:
zfs load-key -r -L file:///mnt/usb/keys tank/encrypted - Verify data integrity:
zpool scrub tank
Rekeying
Section titled “Rekeying”Rekeying is the process of changing the encryption key or algorithm. There are two levels:
Shallow rekey (fast): Changes the wrapping key (passphrase, key file) but keeps the same data Encryption keys. No data re-encryption required.
Deep rekey (slow): Changes the encryption algorithm, which requires re-encrypting all data.
# Shallow rekey: change passphrase (fast, no data re-encryption)zfs change-key -o keyformat=passphrase -o keylocation=prompt tank/secret
# Deep rekey: change algorithm (slow, full re-encryption)zfs change-key -o encryption=aes-256-xts tank/secretPerformance Impact
Section titled “Performance Impact”Encryption Overhead
Section titled “Encryption Overhead”The performance impact of ZFS encryption depends primarily on whether the CPU supports AES-NI (AES New Instructions) hardware acceleration:
| CPU Feature | Encryption Overhead (AES-256-GCM) | Notes |
|---|---|---|
| AES-NI | 1-5% | Negligible on modern CPUs |
| No AES-NI | 20-40% | Significant; use ChaCha20 |
| AES-NI + AVX | 1-3% | Best case, modern Intel/AMD |
| ARM crypto | 3-10% | ARMv8 AES instructions |
Most Intel CPUs since Westmere (2010) and AMD CPUs since Bulldozer (2011) support AES-NI. Check With:
# Check if CPU supports AES-NIgrep -o aes /proc/cpuinfo | wc -l# If > 0, AES-NI is supported
# Or check CPU flagslscpu | grep aes# Flags: ... aes ...Benchmarking Encryption Performance
Section titled “Benchmarking Encryption Performance”# Write test to encrypted datasetdd if=/dev/zero of=/mnt/tank/encrypted/testfile bs=1M count=10000 oflag=direct
# Write test to unencrypted dataset (for comparison)dd if=/dev/zero of=/mnt/tank/unencrypted/testfile bs=1M count=10000 oflag=direct
# Read testdd if=/mnt/tank/encrypted/testfile of=/dev/null bs=1M iflag=direct
# Compare with fio for more realistic workloadsfio --name=enc-write --ioengine=libaio --iodepth=32 --rw=write \ --bs=128K --direct=1 --size=10G --directory=/mnt/tank/encrypted \ --runtime=60 --group_reporting
fio --name=unenc-write --ioengine=libaio --iodepth=32 --rw=write \ --bs=128K --direct=1 --size=10G --directory=/mnt/tank/unencrypted \ --runtime=60 --group_reportingEncryption and Compression Interaction
Section titled “Encryption and Compression Interaction”Compression is always applied before encryption in the ZFS pipeline. This is critical because:
- Encrypted data is incompressible — any compression algorithm sees ciphertext as random noise.
- Compressing first reduces the amount of data that needs to be encrypted.
- Compression and encryption together reduce both storage usage and the encryption computational cost (fewer bytes to encrypt).
# This is the correct order (ZFS handles this automatically):# Plaintext -> Compress -> Encrypt -> Checksum -> Write to disk
# Always enable compression on encrypted datasets (unless the data is already incompressible)zfs set compression=zstd tank/encrypted/docs| Data Type | Compression | Encryption | Combined Effect |
|---|---|---|---|
| Text, code, logs | 3-5x | 1-5% | Less data encrypted, net benefit |
| Databases | 1.2-1.5x | 1-5% | Marginal compression savings |
| Already-encrypted data | 1.0x | 1-5% | Disable compression, encrypt only |
| Media (JPEG, MP4, MKV) | 1.0x | 1-5% | Disable compression, encrypt only |
| Virtual machine images | 1.3-2.0x | 1-5% | Moderate compression before encrypt |
Encryption and Deduplication
Section titled “Encryption and Deduplication”Deduplication operates on block hashes. When encryption is enabled:
- Deduplication happens after encryption in the write path.
- Each encrypted block has a unique nonce, meaning identical plaintext blocks produce different ciphertext blocks.
- Deduplication is effectively useless on encrypted datasets because no two encrypted blocks will ever have the same hash.
# Do NOT enable dedup on encrypted datasetszfs set dedup=off tank/encrypted
# Verify dedup is offzfs get dedup tank/encryptedEncryption and ZFS Features
Section titled “Encryption and ZFS Features”Snapshots and Encryption
Section titled “Snapshots and Encryption”Snapshots of encrypted datasets inherit the parent dataset’s encryption. The snapshot is encrypted With the same key as the live dataset. No separate key management is needed for snapshots.
# Create a snapshot of an encrypted datasetzfs snapshot tank/encrypted/docs@daily-2026-04-07
# The snapshot is encrypted with the same key# If the key is unloaded, the snapshot is also inaccessiblezfs get encryption,encryptionroot tank/encrypted/docs@daily-2026-04-07:::info Snapshots do not require separate key management. They use the same encryption key as their Parent dataset. If you load the key for the parent, all snapshots become accessible. If you unload The key, all snapshots become inaccessible. :::
Clones and Encryption
Section titled “Clones and Encryption”Clones of encrypted snapshots inherit the encryption of the source snapshot. The clone uses the same Encryption key as the source:
# Clone an encrypted snapshotzfs clone tank/encrypted/docs@daily-2026-04-07 tank/encrypted/docs-restore
# The clone inherits encryption from the snapshotzfs get encryption,encryptionroot tank/encrypted/docs-restoreSend and Receive with Encryption
Section titled “Send and Receive with Encryption”This topic is covered in detail in the Send and Receive section below. Key points:
- Normal
zfs senddecrypts data on the source and sends plaintext. zfs send -w(raw mode) sends encrypted data without decrypting, preserving the encryption.- Raw sends are the only way to replicate encrypted datasets without exposing the plaintext to the receiving system.
Scrub and Resilver with Encryption
Section titled “Scrub and Resilver with Encryption”Scrubbing reads all data and verifies checksums. For encrypted datasets, the checksum is computed on The ciphertext (the encrypted block). This means:
- Scrub verifies the integrity of the encrypted data, not the plaintext.
- Scrub does not need the encryption key loaded to verify checksums.
- Scrub can detect and repair corrupted ciphertext blocks from redundancy.
# Scrub works on encrypted datasets without loading keyszpool scrub tank
# The scrub verifies checksums of encrypted blocks# If a checksum mismatch is found, ZFS repairs from redundancy (mirror/parity):::info ZFS can scrub encrypted datasets even when the encryption key is not loaded. The checksum Covers the encrypted data, so integrity verification does not require decryption. This is a Significant advantage — you can schedule scrubs on encrypted datasets without worrying about key Availability. :::
Resilvering after a drive replacement also does not require the encryption key. The data is copied At the block level (encrypted ciphertext), and checksums are verified against the stored values.
Encryption and Special Vdevs
Section titled “Encryption and Special Vdevs”Special vdevs (metadata and small block allocation) work with encrypted datasets. The metadata Stored on the special vdev is encrypted with the dataset’s key. When the key is unloaded, metadata On the special vdev is also inaccessible.
Encryption and ZFS Properties
Section titled “Encryption and ZFS Properties”Some ZFS properties interact with encryption in specific ways:
| Property | Interaction with Encryption |
|---|---|
compression | Applied before encryption. Recommended to keep on. |
dedup | Useless on encrypted data. Always keep off. |
recordsize | No interaction. Set independently. |
sync | No interaction. Set independently. |
atime | No interaction. Set independently. |
copies | Additional copies are encrypted with the same key. |
xattr | Extended attributes are encrypted with the data. |
Send and Receive
Section titled “Send and Receive”Raw Send for Encrypted Datasets
Section titled “Raw Send for Encrypted Datasets”Definition. Raw send (zfs send -w) transmits the raw encrypted blocks from the source dataset Without decrypting them. The receiving system receives encrypted data that it cannot read without The encryption key. This is the only secure way to replicate encrypted datasets to an untrusted Destination.
# Raw send of an encrypted dataset (preserves encryption)zfs send -w tank/encrypted/docs@snap1 | zfs recv backup/encrypted/docs
# Raw send with properties and verbose outputzfs send -wpv tank/encrypted/docs@snap1 | zfs recv backup/encrypted/docs
# Recursive raw send (entire hierarchy)zfs send -Rw tank/encrypted@snap1 | ssh remote-nas zfs recv -F backup/encryptedNormal Send vs. Raw Send
Section titled “Normal Send vs. Raw Send”| Aspect | Normal Send (zfs send) | Raw Send (zfs send -w) |
|---|---|---|
| Data form | Plaintext (decrypted on source) | Ciphertext (encrypted) |
| Key required | Source key must be loaded | Source key not required |
| Destination | Can read the data | Cannot read without key |
| Compression | Decompressed and re-compressed | Preserved as-is |
| Properties | Sent as plaintext values | Preserved including encryption settings |
| Cross-algorithm | Can change encryption algorithm | Preserves original algorithm |
| Use case | Trusted destination | Untrusted destination |
Raw Incremental Send
Section titled “Raw Incremental Send”Raw mode works with incremental sends as well:
# Initial raw sendzfs send -Rw tank/encrypted@base | ssh backup-server zfs recv -F backup/encrypted
# Incremental raw sendzfs send -Rwi tank/encrypted@base tank/encrypted@snap1 | \ ssh backup-server zfs recv backup/encryptedReceiving Raw Sends
Section titled “Receiving Raw Sends”When you receive a raw send, the destination dataset inherits the encryption settings from the Source:
# The destination is created as an encrypted dataset# It has the same encryptionroot, keyformat, and algorithm as the sourcezfs send -w tank/encrypted/docs@snap1 | zfs recv backup/docs
# Check the destinationzfs get encryption,encryptionroot,keyformat,keystatus backup/docs# NAME PROPERTY VALUE SOURCE# backup/docs encryption aes-256-gcm received# backup/docs encryptionroot backup/docs received# backup/docs keyformat passphrase received# backup/docs keystatus unavailable -The destination dataset has the encryption settings from the source but the key is not available (you would need the source’s key to decrypt the data).
Cross-Host Transfer with Raw Send
Section titled “Cross-Host Transfer with Raw Send”Raw send is ideal for sending encrypted backups to a remote system that should not have access to The plaintext:
# Send encrypted backup to a remote NAS (remote cannot read the data)zfs send -Rwv tank/encrypted@monthly-2026-04 | \ pv --rate-limit 100m | \ ssh -c aes256-gcm backup-server zfs recv -F backup/offsite
# The remote system stores encrypted data# To restore, you would raw-send back to a system with the key:ssh backup-server "zfs send -Rw backup/offsite@monthly-2026-04" | \ zfs recv -F restore/encryptedSend Flags for Encrypted Datasets
Section titled “Send Flags for Encrypted Datasets”| Flag | Effect on Encrypted Datasets |
|---|---|
-w | Raw mode: send encrypted blocks |
-R | Recursive: include all child datasets |
-p | Include dataset properties |
-c | Compress during transfer (applied after raw read) |
-L | Large block send |
-v | Verbose output |
-i | Incremental between two snapshots |
Limitations of Raw Send
Section titled “Limitations of Raw Send”- You cannot change the encryption algorithm during a raw send. The destination uses the same algorithm as the source.
- You cannot perform a raw send from an unencrypted dataset (there is nothing to preserve).
- If the source dataset’s key is not loaded, you can still raw send (the data is already encrypted on disk), but you cannot perform a normal send (which requires decryption).
- Raw send of zvols preserves the encryption of the block device.
Backup Implications
Section titled “Backup Implications”Cloud Backup of Encrypted Datasets
Section titled “Cloud Backup of Encrypted Datasets”When backing up encrypted datasets to cloud storage, you have two options:
Option 1: ZFS native encryption + raw send to a local backup pool, then cloud sync.
The cloud sync uploads encrypted data. The cloud provider cannot read the data.
Option 2: Client-side encryption via TrueNAS Cloud Sync.
TrueNAS Cloud Sync can encrypt data before uploading, adding a second layer of encryption on top of ZFS encryption (or as the sole encryption for unencrypted datasets).
# Option 1: Raw send to backup pool, then cloud sync the backup pool# Step 1: Raw send to local backupzfs send -Rw tank/encrypted@snap1 | zfs recv -F backup/encrypted
# Step 2: Cloud Sync uploads the encrypted data from backup/encrypted# Configure in TrueNAS: Data Protection > Cloud Sync# Source: backup/encrypted# Destination: S3 bucket# The cloud receives encrypted blocksKey Management for Backups
Section titled “Key Management for Backups”The most critical aspect of encrypted backups is key management. If you have encrypted backups but Lose the keys, the backups are worthless.
Key storage strategy for encrypted backups:
- Primary key: Stored in a password manager (Bitwarden, 1Password, Vaultwarden).
- Secondary key: Printed on paper and stored in a physical safe deposit box.
- Tertiary key: Stored on a USB drive in a fireproof safe at a different physical location.
- Key escrow: A trusted person (attorney, family member) has access to a sealed envelope with the passphrase.
:::caution Never store encryption keys in the same location as the encrypted data. If a fire Destroys both the NAS and the paper with the passphrase, the data is lost. Distribute keys across Multiple physical locations. :::
Disaster Recovery with Encrypted Datasets
Section titled “Disaster Recovery with Encrypted Datasets”Disaster recovery for encrypted datasets follows the same process as unencrypted datasets, with the Additional step of key loading:
- Procure replacement hardware.
- Install TrueNAS.
- Import the pool:
zpool import tank - Restore encryption keys: Load keys from your backup location.
- Load the keys:
zfs load-key -r tank/encrypted - Verify data integrity:
zpool scrub tank - Restore from offsite backup if needed: Raw send the backup to the new pool.
Backup Verification with Encryption
Section titled “Backup Verification with Encryption”When testing backup restores for encrypted datasets, verify both the encryption and the data:
# 1. Receive raw backup to a test locationzfs send -Rw backup/encrypted@snap1 | zfs recv -F test/encrypted
# 2. Load the encryption keyzfs load-key test/encrypted
# 3. Mount the datasetzfs mount test/encrypted
# 4. Verify file contentsfind /mnt/test/encrypted -type f -exec md5sum {} \; > /tmp/backup_checksums.txt# Compare against production checksumsSecurity Considerations
Section titled “Security Considerations”Key Protection
Section titled “Key Protection”The encryption key is the single point of failure for encrypted datasets. Protect the key with the Same rigor as the data it protects:
| Threat | Mitigation |
|---|---|
| Key file on same pool | Store key file on separate media |
| Key file stolen | Encrypt the key file itself (e.g., GPG) |
| Passphrase brute-force | Use high-entropy passphrase + high pbkdf2iters |
| Passphrase forgotten | Store in password manager + physical backup |
| Key file deleted | Keep multiple backups of key files |
| Memory dump attack | See RAM security below |
RAM (Memory) Security
Section titled “RAM (Memory) Security”When an encryption key is loaded, it resides in kernel memory for as long as the dataset is mounted. This has several implications:
- Cold boot attack: If an attacker gains physical access to a running system, they may be able to extract encryption keys from RAM by rapidly dumping memory contents before they decay. This is a sophisticated attack that requires physical access and specialized tools.
- Hibernation: If the system hibernates, the RAM contents (including encryption keys) are written to disk in the hibernation image. This image may be accessible to an attacker with physical access.
- Kernel memory access: Any process with access to kernel memory (e.g., via a kernel exploit) can potentially extract encryption keys. Minimize attack surface by keeping the system updated.
Mitigations:
- Power off the system when not in use (keys are not persisted across power cycles).
- Use secure boot and measured boot to detect unauthorized kernel modifications.
- Restrict physical access to the NAS.
- Keep the kernel and ZFS modules updated.
Algorithm Choices
Section titled “Algorithm Choices”| Algorithm | Security Level | Performance | Recommendation |
|---|---|---|---|
| aes-256-gcm | Excellent | Best (AES-NI) | Default, use for everything |
| aes-128-gcm | Very Good | Very Good | Adequate, marginally faster |
| aes-256-ccm | Good | Moderate | Legacy hardware only |
| chacha20-poly1305 | Good | Good | CPUs without AES-NI |
| aes-256-xts | Good | Good | zvols / block devices only |
crypt vs. Crypt2 Format
Section titled “crypt vs. Crypt2 Format”ZFS encryption has two internal key wrapping formats: crypt (legacy) and crypt2 (current):
| Format | Key Wrapping Algorithm | Key Length Support | Recommendation |
|---|---|---|---|
| crypt | PBKDF2-SHA512 | 256-bit only | Legacy |
| crypt2 | HKDF-SHA512 | 128-bit, 256-bit | Use this |
The crypt2 format (available since OpenZFS 2.1 / TrueNAS SCALE 22.02) supports both 128-bit and 256-bit key lengths and uses HKDF (HMAC-based Key Derivation Function) for key wrapping, which is More efficient and secure than PBKDF2 for this purpose. New encrypted datasets on TrueNAS SCALE use crypt2 by default.
# Check the encryption formatzfs get keyformat,encryption tank/encrypted
# The format is not directly exposed as a property; it is determined by# the ZFS version and the keyformat/encryption propertiesForward Secrecy Considerations
Section titled “Forward Secrecy Considerations”ZFS native encryption does not provide forward secrecy. Once a key is loaded, all data encrypted With that key (past, present, and future) can be decrypted. If a key is compromised, all data Encrypted with that key is compromised.
For forward secrecy, you would need to re-encrypt data with a new key after each access session and Securely destroy the old key. This is not practical for a NAS workload. Instead, focus on strong key Protection and regular key rotation.
Key Rotation Strategy
Section titled “Key Rotation Strategy”While ZFS does not support automatic key rotation, you can implement a manual rotation strategy:
# Annual key rotation:# 1. Create a new encrypted dataset with a new keyzfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt \ tank/secret-v2
# 2. Copy data from old to newzfs send -R tank/secret@snap1 | zfs recv tank/secret-v2
# 3. Verify the new datasetzfs load-key tank/secret-v2diff -r /mnt/tank/secret /mnt/tank/secret-v2
# 4. Destroy the old datasetzfs unload-key tank/secretzfs destroy -r tank/secret:::caution Key rotation is a manual, time-consuming process that requires enough free space to hold A copy of the data. Plan key rotation during maintenance windows and verify data integrity before Destroying the old dataset. :::
TrueNAS SCALE Specific
Section titled “TrueNAS SCALE Specific”Web UI Encryption Setup
Section titled “Web UI Encryption Setup”TrueNAS SCALE provides a graphical interface for creating and managing encrypted datasets:
- Navigate to Storage → Pools.
- Click the three-dot menu next to the pool → Add Dataset.
- In the dataset creation dialog:
- Encryption: Select
AES-256-GCM(or your preferred algorithm). - Key Format: Choose
Passphrase``HexOrRaw. - Key Location: For passphrase, leave as
Prompt. For hex/raw, provide the file path. - PBKDF2 Iterations: Leave at default (350000) or increase for stronger passphrases.
- Click Save.
Key Management in TrueNAS
Section titled “Key Management in TrueNAS”TrueNAS SCALE manages encryption keys through the Storage → Encryption section:
- Lock/Unlock Datasets: Lock (unload key) or unlock (load key) encrypted datasets from the UI.
- Rekey: Change the passphrase or key for an encrypted dataset.
- Key Files: Manage key files stored on the TrueNAS system.
# List all encrypted datasets and their key statusmidclt call pool.dataset.query | \ jq '.[] | select(.encrypted == true) | {name, key_status: .key_status, encryptionroot}'
# Unlock an encrypted dataset via CLImidclt call pool.dataset.unlock '{"id": "tank/encrypted", "name": "tank/encrypted", "key": "your-passphrase"}'
# Lock an encrypted dataset via CLImidclt call pool.dataset.lock '{"id": "tank/encrypted", "force_umount": true}'System Dataset Encryption
Section titled “System Dataset Encryption”TrueNAS SCALE supports encrypting the system dataset (which stores configuration data, SMB Passwords, and other system state). This is separate from user data encryption:
- Navigate to System → Advanced.
- Set the System Dataset Pool to an encrypted dataset.
- The system dataset will be encrypted with a key managed by TrueNAS.
The system dataset encryption key is automatically managed by TrueNAS and stored in the boot pool. This protects system configuration if the data pool drives are stolen, but the boot pool must also Be protected.
Boot Key Loading
Section titled “Boot Key Loading”On TrueNAS SCALE, encrypted datasets are handled at boot as follows:
- The pool is imported.
- Datasets with
keyformat=passphraseremain locked until manually unlocked via the web UI or CLI. - Datasets with
keyformat=raworhexandkeylocation=file://are automatically unlocked if the key file is accessible. - The TrueNAS web UI shows locked datasets with a lock icon, and you can unlock them by entering the passphrase.
# Check which datasets are locked at bootzfs get keystatus -r tank# Any dataset with keystatus=unavailable is locked
# Unlock via CLIzfs load-key tank/encryptedTrueNAS SCALE Encryption Best Practices
Section titled “TrueNAS SCALE Encryption Best Practices”- Use
keyformat=passphrasefor sensitive data. The passphrase provides an additional factor — even if an attacker gains access to the TrueNAS web UI, they cannot unlock the dataset without the passphrase. - Use
keyformat=raworhexfor automated workloads. If you need datasets to auto-mount at boot (e.g., app storage), use key files with restricted permissions. - Store key files on a separate pool or USB drive. Never store key files on the same pool they decrypt.
- Document all passphrases and key locations. Maintain a spreadsheet or document listing every encrypted dataset, its key format, key location, and where the key/passphrase is stored.
- Test key recovery quarterly. Verify that you can load the key and access the data for every encrypted dataset.
Common Pitfalls
Section titled “Common Pitfalls”Forgetting the Passphrase (Data Loss)
Section titled “Forgetting the Passphrase (Data Loss)”This is the most catastrophic pitfall. There is no recovery mechanism. The data is permanently lost.
Prevention:
- Use a password manager as the primary key store.
- Write the passphrase on paper and store it in a fireproof safe.
- Store a copy in a bank safe deposit box.
- Use a Diceware passphrase with 7+ words for memorability and strength.
- Test key loading monthly to ensure the passphrase is correct and the process works.
Key File Deletion
Section titled “Key File Deletion”If a key file is deleted and no backup exists:
- If
keyformat=passphraseUsezfs change-keyto switch to passphrase mode (you need the original passphrase to do this). - If
keyformat=raworhexwith no passphrase backup, the data is lost.
Prevention:
- Keep at least two backups of every key file on separate physical media.
- Use version control (git) for key files stored in an encrypted repository.
- Document the location of all key file backups.
Mounting Without Loading the Key
Section titled “Mounting Without Loading the Key”If you attempt to mount an encrypted dataset without loading the key first:
# Error: cannot mount 'tank/encrypted': encryption key not loadedzfs mount tank/encrypted
# Solution: load the key firstzfs load-key tank/encryptedzfs mount tank/encryptedOr use the combined flag:
# Mount and load key in one commandzfs mount -l tank/encryptedInheriting Encryption Unexpectedly
Section titled “Inheriting Encryption Unexpectedly”When you create a child dataset inside an encrypted parent, the child inherits encryption Automatically. If you did not intend this, the child will be encrypted with the parent’s key:
# Parent is encryptedzfs create -o encryption=on -o keyformat=passphrase tank/secret
# Child inherits encryption (this may be unexpected)zfs create tank/secret/public-data
# tank/secret/public-data is now encrypted# To create an unencrypted child inside an encrypted parent, you must explicitly disable encryption:# (This is NOT supported by ZFS -- you cannot have unencrypted children inside an encrypted parent):::caution You cannot create an unencrypted child dataset inside an encrypted parent. All children Of an encrypted dataset are encrypted, period. If you need a mix of encrypted and unencrypted Datasets, create them as siblings (not parent-child) within an unencrypted pool. :::
Performance Without AES-NI
Section titled “Performance Without AES-NI”On CPUs without AES-NI support, AES-256-GCM encryption can add 20-40% overhead. This is particularly Noticeable on:
- Low-power ARM SoCs without crypto extensions
- Older Intel/AMD CPUs (pre-2010)
- Virtual machines where AES-NI is not passed through
Mitigation:
# Check for AES-NI supportlscpu | grep -i aes
# If AES-NI is not available, use ChaCha20-Poly1305 insteadzfs create -o encryption=chacha20-poly1305 \ -o keyformat=passphrase -o keylocation=prompt \ tank/encryptedChaCha20-Poly1305 is a stream cipher that does not rely on AES-NI and provides competitive Performance on CPUs without hardware AES support.
Storing Key Files on the Same Pool
Section titled “Storing Key Files on the Same Pool”This is a common configuration mistake that provides a false sense of security:
# WRONG: Key file stored on the same pool it decryptszfs create -o encryption=on -o keyformat=raw \ -o keylocation=file:///mnt/tank/keys/secret.raw \ tank/encrypted
# If the pool is stolen, the attacker has both the encrypted data and the key# CORRECT: Key file stored on separate media# Option A: Separate pool on internal SSDzfs create -o encryption=on -o keyformat=raw \ -o keylocation=file:///mnt/keypool/keys/secret.raw \ tank/encrypted
# Option B: USB drive mounted at /mnt/usbzfs create -o encryption=on -o keyformat=raw \ -o keylocation=file:///mnt/usb/keys/secret.raw \ tank/encryptedDisabling Compression on Encrypted Datasets
Section titled “Disabling Compression on Encrypted Datasets”Some administrators disable compression on encrypted datasets, assuming that encrypted data cannot Be compressed. This is incorrect for ZFS because compression happens before encryption:
# WRONG: Disabling compression on encrypted datasetzfs set compression=off tank/encrypted/docs
# CORRECT: Keep compression enabled (it runs before encryption)zfs set compression=zstd tank/encrypted/docsThe data flow is: Plaintext → Compress → Encrypt → Write. Compression operates on the plaintext, so It works normally on encrypted datasets. Only disable compression if the plaintext data is already Incompressible (encrypted files, compressed archives, media files).
Not Testing Key Recovery
Section titled “Not Testing Key Recovery”The worst time to discover you have lost the encryption key is when you need to restore from backup. Test key recovery regularly:
#!/bin/bash# Test that all encryption keys can be loaded
DATASETS=$(zfs get -r -H -o name encryption | grep -v "^-$" | cut -f1)FAILED=0
for ds in $DATASETS; do STATUS=$(zfs get -H -o value keystatus "$ds") if [ "$STATUS" = "unavailable" ]; then echo "Testing key load for: $ds" # This will prompt for passphrase; in production, automate with expect or key files if ! zfs load-key "$ds" 2>/dev/null; then echo "FAILED: Could not load key for $ds" FAILED=$((FAILED + 1)) fi fidone
if [ $FAILED -gt 0 ]; then echo "WARNING: $FAILED dataset(s) have key issues"fiUsing Short or Weak Passphrases
Section titled “Using Short or Weak Passphrases”A weak passphrase (e.g., “password”, “nas123”, your dog’s name) can be brute-forced in seconds to Minutes. The pbkdf2iters property slows down brute-force attacks, but a sufficiently weak Passphrase can still be cracked.
Minimum passphrase requirements:
- At least 20 characters if randomly generated.
- At least 6-7 Diceware words if using a word-based passphrase.
- No dictionary words, names, dates, or common patterns.
- Unique per dataset (do not reuse passphrases across encrypted datasets).
Ignoring Key Status After Updates
Section titled “Ignoring Key Status After Updates”After a TrueNAS update or pool upgrade, verify that all encryption keys are still loaded and all Encrypted datasets are accessible:
# Check key status after system updatezfs get keystatus -r tank
# Any dataset showing "unavailable" needs its key reloaded# For passphrase datasets:zfs load-key tank/encrypted/dataset
# For key file datasets:zfs load-key -L file:///path/to/key tank/encrypted/dataset
# Verify all datasets are mountedzfs mount -aSummary
Section titled “Summary”This topic covers the core concepts of zfs encryption, including underlying theory, practical implementation, and key applications.
Key concepts include:
- Big O notation and complexity analysis
- searching algorithms (binary, linear)
- sorting algorithms (bubble, merge, quick)
- graph algorithms (Dijkstra, BFS, DFS)
- dynamic programming
Understanding these concepts thoroughly is essential for both examinations and practical programming, and requires both theoretical knowledge and hands-on practice.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.