How to Migrate an Existing Directory to a Separate ZFS Dataset While Preserving Data


2 views

When working with ZFS on Linux/FreeBSD systems, we often encounter situations where directories initially created in the root pool need to be promoted to independent datasets. The rpool/etc scenario presents unique challenges because:

  • System directories contain critical configuration files
  • Simple zfs create would make an empty dataset
  • We need atomic migration to prevent data loss

Here's the safest procedure I've used in production environments:

# 1. Create temporary mountpoint
sudo mkdir /mnt/temp_etc

# 2. Create new dataset with temporary mountpoint
sudo zfs create -o mountpoint=/mnt/temp_etc rpool/etc

# 3. Copy data with proper permissions (rsync preserves metadata)
sudo rsync -avzP /etc/ /mnt/temp_etc/

# 4. Verify data integrity
sudo diff -qr /etc/ /mnt/temp_etc/

# 5. Remount dataset to proper location
sudo zfs set mountpoint=/etc rpool/etc

# 6. Remove temporary directory
sudo rmdir /mnt/temp_etc

For production systems, consider these additional safeguards:

# Create snapshot before migration
sudo zfs snapshot rpool@pre_etc_migration

# Use ZFS send/receive for large directories
sudo zfs send rpool/etc@init | sudo zfs receive rpool/etc_backup

# Set custom properties on the new dataset
sudo zfs set compression=lz4 rpool/etc
sudo zfs set atime=off rpool/etc
  • If services break after migration, check mount | grep etc to verify proper mounting
  • For permission issues, use getfacl/setfacl to preserve ACLs
  • Consider setting canmount=noauto for critical system directories

This method has worked reliably across Ubuntu 20.04+, FreeBSD 13+, and various ZFS implementations. The key advantage is maintaining data integrity throughout the process while allowing rollback options if needed.


When managing ZFS pools, you might encounter situations where an existing directory (like /etc in your root pool) should become a separate dataset. This brings benefits like independent snapshots, compression, and quota management. Here's the proper migration method.

# 1. First create the target dataset with same permissions
sudo zfs create -o mountpoint=/etc rpool/etc

# 2. Temporarily change mountpoint to avoid conflicts
sudo zfs set mountpoint=/etc-temp rpool/etc

# 3. Copy data while preserving all attributes
sudo rsync -avHAX /etc/ /etc-temp/

# 4. Rename original directory (safety measure)
sudo mv /etc /etc-old

# 5. Restore proper mountpoint
sudo zfs set mountpoint=/etc rpool/etc

# 6. Verify data integrity
sudo diff -qr /etc-old /etc

# 7. Optional: Remove backup when confirmed working
# sudo rm -rf /etc-old

Mount Order Matters: For system directories like /etc, ensure this runs before dependent services start. You may need to create a zfs-mount.service dependency.

Permissions Preservation: The rsync -HAX flags maintain hard links, ACLs, and extended attributes. Alternative for huge directories:

sudo tar -cf - -C /etc . | sudo tar -xpf - -C /etc-temp

Here's how I recently moved logging directories to a compressed dataset:

# Create dataset with lz4 compression
sudo zfs create -o compression=lz4 -o mountpoint=/var/log-temp rpool/var_log

# Copy 45GB of log files
sudo rsync -avHAX --progress /var/log/ /var/log-temp/

# Switch the datasets
sudo mv /var/log /var/log-old
sudo zfs set mountpoint=/var/log rpool/var_log
sudo systemctl restart rsyslog
  • If services fail, check mount | grep /etc to verify dataset mounting
  • For boot issues, have a Live USB ready to zfs set mountpoint=legacy
  • Test with non-critical directories first (like /opt)

For zero-downtime migrations on critical systems:

sudo mkdir /etc-new /etc-oldroot
sudo zfs set mountpoint=/etc-new rpool/etc
sudo rsync -avHAX /etc/ /etc-new/
sudo mount --bind /etc /etc-oldroot
sudo mount --bind /etc-new /etc
# After verification:
sudo umount /etc-oldroot