When you initially create a ZFS pool with zpool create tank /dev/loop0
, the system automatically generates a root filesystem mounted at /tank
. This becomes problematic when you later need hierarchical organization:
zfs list
NAME USED AVAIL REFER MOUNTPOINT
tank 591G 2.10T 591G /tank
The error message cannot rename to 'tank/mydata': datasets must be within same pool
occurs because:
- ZFS treats the root dataset differently from child datasets
- The rename operation expects both source and target to be regular datasets
- Pool root has special properties regarding mountpoints and delegation
Here's the step-by-step method I've successfully used in production environments:
# 1. Create the target child dataset with identical properties
zfs create -o mountpoint=/tank tank/mydata
# 2. Temporarily disable automatic mounting
zfs set mountpoint=none tank
# 3. Move data using rsync with hardlinks (instant operation)
rsync -aH --delete --numeric-ids /tank/ /tank/mydata/
# 4. Verify data integrity
diff -qr /tank /tank/mydata
# 5. Destroy original root filesystem contents
zfs destroy tank
# 6. Recreate the root as empty container
zfs create -o mountpoint=/tank tank
# 7. Optional: Set correct permissions
chmod 755 /tank
For environments where rsync isn't preferred:
# Create intermediate snapshot
zfs snapshot tank@migration
# Send to new child dataset
zfs send tank@migration | zfs recv tank/mydata
# Verify and clean up
zfs list -t snapshot
zfs destroy tank@migration
Remember to handle mount points carefully:
# For legacy mount handling
zfs set mountpoint=legacy tank/mydata
echo "/tank/mydata /tank zfs rw,noatime 0 0" >> /etc/fstab
This approach offers several advantages:
- Near-zero downtime (only during mountpoint changes)
- No physical data movement on disk
- Preserves all ZFS properties including compression settings
- Maintains existing block pointers and checksums
When working with ZFS, you might encounter a situation where you need to restructure your pool hierarchy. A common scenario is moving data from the root filesystem into a child dataset - but the standard zfs rename
command throws the frustrating "datasets must be within same pool" error even when working with a single pool.
The naive approach using mv
or similar tools performs a full data copy followed by deletion, which is inefficient for large datasets. Even ZFS's built-in rename operation refuses to work across hierarchy levels due to architectural constraints.
The most efficient method uses ZFS's native data transfer capabilities:
# Create the new target structure
zfs create tank/mydata
# Set temporary mountpoint
zfs set mountpoint=/tank-temp tank/mydata
# Send the data (no -r flag since we have no snapshots)
zfs send tank | zfs receive tank/mydata
# Verify data integrity
diff -qr /tank /tank-temp
# Destroy original data
zfs destroy tank
# Reset mountpoint
zfs set mountpoint=/tank tank/mydata
For environments where you can't afford downtime:
# Create temporary pool
zpool create temp /dev/loop1
# Send data to temp
zfs send tank | zfs receive temp/mydata
# Destroy original pool
zpool destroy tank
# Recreate with proper structure
zpool create tank /dev/loop0
zfs create tank/mydata
# Send data back
zfs send temp/mydata | zfs receive tank/mydata
# Cleanup
zpool destroy temp
The "datasets must be within same pool" error occurs because ZFS treats rename operations as strictly intra-pool operations that can't change the dataset's position in the hierarchy. This is a design limitation rather than a bug.
While this appears to be a "copy" operation, ZFS send/receive is actually:
- Block-level efficient (only transfers used blocks)
- Preserves all ZFS properties
- Can be resumed if interrupted
- Works with encryption and compression natively