When dealing with production systems, migrating 3TB of data between datasets isn't just time-consuming - it introduces unnecessary risks. The traditional approach of:
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=/mnt/temp tank/encrypted
rsync -avz /original/data /mnt/temp/
zfs destroy original_dataset
zfs set mountpoint=/original/path tank/encrypted
creates several pain points: data transfer windows, temporary storage requirements, and most critically - loss of existing snapshots.
ZFS actually provides a more elegant way through its send/receive functionality combined with encryption properties:
# Step 1: Create encrypted parent dataset
zfs create -o encryption=on -o keyformat=passphrase tank/encrypted
# Step 2: Stream original dataset to encrypted child
zfs send original/dataset | zfs receive -o encryption=on -o keyformat=passphrase tank/encrypted/dataset
# Step 3: Verify data integrity
zfs list -r tank/encrypted
zfs diff original/dataset tank/encrypted/dataset
While the above method maintains the data structure, ZFS currently doesn't support converting existing snapshots to encrypted format. Your options are:
- Keep the original dataset mounted read-only as an archive
- Recreate critical snapshots manually after encryption
- Use zfs hold to preserve the most important snapshots during migration
When encrypting large datasets (3TB+), consider these tunables:
# For better throughput during initial encryption
zfs set compression=off tank/encrypted
zfs set sync=disabled tank/encrypted
# After initial transfer
zfs set compression=lz4 tank/encrypted
zfs set sync=standard tank/encrypted
For batch processing multiple datasets:
#!/bin/bash
SOURCE_POOL="tank"
TARGET_POOL="tank"
ENCRYPTION_ROOT="encrypted"
PASSPHRASE="your_secure_passphrase"
for dataset in $(zfs list -H -o name | grep "^${SOURCE_POOL}/"); do
base_name=$(basename $dataset)
echo "Processing $dataset..."
# Create encrypted parent if not exists
zfs list -H "${TARGET_POOL}/${ENCRYPTION_ROOT}" ||
zfs create -o encryption=on -o keyformat=passphrase \
-o pbkdf2iters=1000000 "${TARGET_POOL}/${ENCRYPTION_ROOT}"
# Stream with encryption
zfs send "$dataset" | zfs receive -o encryption=on \
-o keyformat=passphrase -o pbkdf2iters=1000000 \
"${TARGET_POOL}/${ENCRYPTION_ROOT}/${base_name}"
# Preserve original properties
zfs inherit -r mountpoint "${TARGET_POOL}/${ENCRYPTION_ROOT}/${base_name}"
done
ZFS encryption, introduced in OpenZFS 0.8.0, provides native encryption at the dataset level. Unlike traditional methods that require separate encryption layers, ZFS handles encryption transparently while maintaining all its advanced features like compression, deduplication, and snapshots.
Here's the complete process for encrypting an existing dataset:
# Create encrypted dataset
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=/mnt/temp_encrypt tank/new_encrypted
# Copy data (preserving permissions)
rsync -avzX --progress /original/dataset/ /mnt/temp_encrypt/
# Verify data integrity
diff -r /original/dataset /mnt/temp_encrypt
# Destroy original dataset
zfs destroy -r tank/original
# Set final mountpoint
zfs set mountpoint=/original/path tank/new_encrypted
1. ZFS Send/Receive with Encryption:
# Create encrypted target
zfs create -o encryption=on -o keyformat=passphrase tank/encrypted_backup
# Send/receive with raw flag
zfs send tank/original@snapshot | zfs receive -o encryption=on -o keyformat=passphrase -F tank/encrypted_backup
2. Using zfs rename for seamless transition:
# After creating and populating encrypted dataset
zfs unmount tank/original
zfs rename tank/original tank/original_old
zfs rename tank/new_encrypted tank/original
zfs set mountpoint=/original/path tank/original
- Snapshot handling: Existing snapshots cannot be directly encrypted. You'll need to:
# Recreate snapshots after migration
zfs snapshot -r tank/encrypted@$(date +%Y%m%d)
For multiple datasets, create a bash script:
#!/bin/bash
SOURCE=$1
TARGET=$2
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=/mnt/zfs_temp ${TARGET}
rsync -avzX --progress ${SOURCE}/ /mnt/zfs_temp/
zfs destroy -r ${SOURCE}
zfs set mountpoint=${SOURCE} ${TARGET}
Mountpoint conflicts: Always use temporary mountpoints during migration
Permission errors: Run rsync as root or use sudo
Inherited properties: Reset them explicitly:
zfs inherit -r compression tank/encrypted
zfs inherit -r atime tank/encrypted