ZFS Dataset Migration Guide: Move Data Without Changing Container Paths

Eva Wong is the Technical Writer and resident tinkerer at ZimaSpace. A lifelong geek with a passion for homelabs and open-source software, she specializes in translating complex technical concepts into accessible, hands-on guides. Eva believes that self-hosting should be fun, not intimidating. Through her tutorials, she empowers the community to demystify hardware setups, from building their first NAS to mastering Docker containers.

The safe approach is to treat replicate, verify, and atomically cut over the original mountpoint with a rollback dataset retained as a sequence of observable gates, not a single command.

On a ZFS datasets backing a home-server container stack, the practical risk is need to move a ZFS dataset while preserving bind-mount paths used by containers. Record the current identity and recovery point, start with the least invasive discriminator, interpret pass and fail results before changing another variable, and stop when storage becomes unstable or the only recoverable copy would be exposed. The workflow below ends only after the original workload succeeds or the evidence reaches an escalation boundary.

Inventory the dataset and path contract

Record the source dataset, children, snapshots, mountpoint, canmount value, encryption root, quotas, reservations, ACL behavior, and every container bind mount that lands inside it. The contract is the host path containers see; the pool and dataset name may change underneath that path.

ZFS replication can preserve snapshots and properties, so a recursive stream needs deliberate property review rather than a blind receive. An independent ZFS send and receive migration shows how send and receive are used for internal dataset migration and why the target hierarchy deserves inspection before cutover.

Create a current external backup or prove an existing restore before migration. Stop if the source has hidden child datasets, an unknown encryption-key dependency, or a mountpoint that overlaps another live dataset; those conditions can make a correct stream mount in the wrong place.

Receive the first copy without mounting it over production

Create a recursive snapshot such as zfs snapshot -r oldpool/apps@move-0, then send it to a target dataset received with mounting disabled or with a temporary mountpoint. Use the flags appropriate to your encryption and property requirements; do not assume a raw encrypted stream and a decrypted receive have the same key behavior.

After receive, compare zfs list -r -t filesystem,snapshot and zfs get -r mountpoint,canmount,encryptionroot,quota,reservation on both trees. A forum discussion about replicated mountpoint properties illustrates why replicated mountpoint properties can surprise an otherwise successful migration.

The first copy passes when dataset and snapshot ancestry match and the target remains isolated from the production path. If it mounts over the source or changes container-visible files, export or unmount the target and correct properties before any incremental send.

Close the write gap and switch the mountpoint

Take another source snapshot and send the incremental difference while the app still runs. For final cutover, stop all writers, confirm no process has open files under the bind-mount path, take a final snapshot, and send only that delta. Keep the downtime limited to final synchronization and path switching.

Set the source to a non-production mountpoint or canmount=noauto, then assign the original host path to the target and mount it. Never leave two datasets claiming the same mountpoint. Start the database and dependent services before the application front end so errors point to the correct layer.

If the final send fails, remount the source at the original path and restart the stack; do not mix new writes across both copies. The rollback condition is explicit: the source remains intact and no production write is accepted on the target until the final stream and property checks pass.

Validate containers at the unchanged path

Inspect bind mounts from the container runtime, open representative files, create and remove a disposable file through the app, and confirm ownership, ACLs, extended attributes, and free-space reporting. Restart the stack twice and verify that ZFS mounts before containers start.

Run a scrub or other pool-health check according to your maintenance plan, but do not use it as the only migration proof. Compare snapshot GUIDs or a representative hash manifest, restore one small item, and use the ZimaSpace test for whether ZFS replication can resume after interruption before retiring the source lineage.

Retain the source read-only until at least one normal backup and application cycle succeeds. Retire it only when containers use the original paths, scheduled jobs target the new dataset, replication continues from the intended lineage, and rollback is no longer needed; otherwise revert the mountpoint and preserve both histories.

Support & Tips

More to Read

Get More Builds Like This

Stay in the Loop

Get updates from Zima - new products, exclusive deals, and real builds from the community.

Stay in the Loop preferences

We respect your inbox. Unsubscribe anytime.