ZXFER(8)

πŸ“¦ version 1.1.7, in nixos 26.05, nixpkgs unstable

✨ zxfer(8) ✨

okay so this is straight out of the System Manager's Manual, section 8, and it's a whole thing πŸ’… buckle up bestie


πŸŽ€ NAME πŸŽ€

zxfer β€” transfer ZFS filesystems, snapshots, properties, files and directories πŸ¦‹


🌸 SYNOPSIS 🌸

zxfer [-dFnPsv] [-k | e] [-b | B] {-N path/to/src | -R path/to/src} [-m
[c FMRI|pattern[ FMRI|pattern]...] ]] [-O user@host] [-T user@host] [-o
option1=value1,option2=value2...]  [-g days] destination
zxfer {-S} [-dilpPnv] [-k | e] [-b | B] [-f /path/to/rsfile] {-R
/path/to/src1,path/to/src2... | -N /path/to/src1,path/to/src2...} [-o
option1=value1,option2=value2...]  [-E pattern] [-L value] [-u snapshot]
destination
zxfer [-h]

where destination is a ZFS filesystem, like poolname/fsname β€” yes, exactly like that πŸ’–


πŸ¦‹ DESCRIPTION πŸ¦‹

okok so zxfer does two main character things, no cap 🫢

First up: it can replicate ZFS filesystems (snapshots and properties included, obviously ✨) and it uses zfs(8) send to do all the heavy lifting.

Second: it can also transfer files and directories β€” and the filesystems underneath them β€” using rsync(1). How it does that is honestly kind of iconic: it first recursively snapshots those filesystems so there's an atomic snapshot of all the files and directories sitting there as a base. Then it creates a replica of the source filesystem hierarchy all the way down to the pool level at the destination. Then it transfers the files and directories. This is the whole vibe when you've got different snapshotting regimes on source and destination and you just want to copy across the latest contents of the filesystem(s). slay.

Both zfs(8) send and rsync(1) use checksums/hashes to verify that the data copied to the destination is the same data that was sent from the source β€” so we get a similar degree of surety of end-to-end data integrity as the ZFS filesystem itself. iykyk πŸ’…

Either method lets the properties of each filesystem be transferred, and lets specific properties be overridden as necessary. For example, compression, readonly, and copies are properties that are likely to be useful to override if we're using this utility to make backups. And if the destination filesystems don't exist? zxfer will automatically create them. not me being helpful 😌

Both methods also allow the backup and restore of the original filesystem properties, so if it's desirable to back up the filesystem(s) for archival purposes using compression etc. as overrides, the original properties may be restored without you having to make a note of what those original properties even were. queen behaviour πŸ‘‘

IMPORTANT: switching between these two modes is done with -S. Depending on the mode, the workings of an option may be nonexistent, subtly or grossly different. Don't assume an option works the exact same way that it did with the other mode. fr fr, read the section, bestie 😭

Also note that zxfer should be run from root, as to do anything useful zfs(8) must also be run from root.

And note that at present, the usage of spaces in zfs(8) filesystem names is NOT supported. There is no plan to support it without someone else doing the coding or a good funding proposal coming my way πŸ’€


πŸ’€ DISCLAIMER/WARNING πŸ’€

You MUST read, understand and agree to the disclaimer in the BSD license before applying any of these examples or using zxfer. (See the file COPYING that should have been included with this utility.) πŸ“

I eat my own cooking β€” I use zxfer as per the examples given and as my primary form of backup. That being said, you are strongly urged to have a look at the script and try it out on some non-risky pools and filesystems before using it in production. At this stage, straying too far from the given examples will probably furnish you with the material to submit some bug reports. Run a backup before using it and don't shoot me if something goes wrong. This software is about beta level, which means that if you are going to use it for backups you should treat it more like alpha level software β€” i.e. trust it at your own risk πŸ˜­πŸ™ˆ

Modifying an example first is a good way to start using the script, as there are some options (e.g. -d and -F in normal mode) that are in practice always used. ✨


🌈 SNAPSHOTS 🌈

zxfer in Normal mode as a backup/restore solution is designed to be used hand-in-hand with a regular snapshotting regime. If there's no regular snapshotting regime in place then the results won't be nearly as good. zxfer would still be useful to reliably and easily copy a pool, for example β€” but you'd be missing the main point of the program. it's giving "used half of my power" energy tbh πŸ’…


πŸ’œ OPTIONS πŸ’œ

Common options are listed first, then options as they function specifically with zfs(8) send mode, then options as they work specifically for rsync(1) aka -S mode.

Please note: options with arguments (e.g. -R, -N and the like) can not be concatenated after other options. (e.g. -vFdR source destination will fail. You should use instead: -vFd -R source destination) Otherwise this will confuse getopts and zfs(8) will complain that "dataset already exists". 😭 not the getopts betrayal

The options are as follows:

  • -b β€” Small (b)eep mode. Plays a failure tune before exiting with failure, so you don't have to sit there monitoring the terminal to know when a lengthy backup or restore finishes. (This is not as yet supported in (Open) Solaris.) To save insult being added to injury, the failure beep will not play during errors that zxfer discovers in the initialization phase β€” beeps will only occur if an error has the possibility of occurring after significant time has elapsed. 🎡
  • -B β€” Large (B)eep mode. Similar to -b but suitable for use at the end of a script. It plays a success or failure tune before exit. If you're executing zxfer multiple times, use -b for everything but the last execution, for which you should use -B. πŸ”₯
  • -e β€” R(e)store property mode. This restores all filesystem properties from the backup file mentioned in the previous option.
A word of warning: in order to allow the restoration of filesystems within the hierarchy, zxfer will begin at the level you specify, and traverse the filesystem hierarchy back to the pool level until it finds an appropriate .zxfer_backup_info file or fails. It will use the first such file it comes across, so be careful about backing up from multiple individual levels of the same filesystem hierarchy. πŸ™ˆ
  • -h β€” Prints (h)elp. 🫢
  • -k β€” Bac(k)up property mode. This backs up the original filesystem property settings to a file .zxfer_backup_info.<pool_name>. This file is located in the directory that is the mountpoint of the parent filesystem that will contain the root filesystem you are sending. e.g. if you are backing up zroot to be located as per storage/backups/zroot/... the file /storage/backups/.zxfer_backup_info.zroot would hold all the information about the original values of the filesystem properties, and some other useful info. πŸŽ€

This allows us to use the [-o] option freely β€” e.g. specifying copies, compression etc. β€” without having to remember what the original options were. iconic.

A word of warning: if you intend to back up to one location and further back up that backup to another location, sourcing the properties to use in the final backup property file from the property backup file in the intermediate backup is not yet supported. A workaround is to refrain from using property overrides in the intermediate backup; this will mean that the ultimate backup will be able to store the correct properties of the original. πŸ’€
  • -n β€” Dry ru(n) mode. Prints out the commands that would otherwise execute but does not execute them. This may not work in all circumstances, as it may expect the existence of filesystems etc. that won't be created. πŸ‘€
  • -o β€” (o)verride property mode. Property overrides are specified with commas to separate.

e.g. -o property1=value1,property2=value2...

For example, -o compression=lzjb will cause all destination filesystems to be set to lzjb compression. If using recursive mode, only the root filesystem will be set, and all other filesystems will inherit from this. Note that this option will also override any values that would otherwise be set by "-P". Invoking this option will also create the destination filesystem(s) if they do not already exist. slay πŸ’…

  • -P β€” Transfer (P)roperty mode. This causes the destination to have properties explicitly set (i.e. with property sources as local) to exactly the same properties as the source. If the destination filesystems do not exist, they will be created with the correct properties. ✨

If using recursive modes, child filesystems have property sources (as in, the fourth column of # zfs get all pool/filesystem) that are made either local, (if the source is local) or inherited (if the source is default or inherited) as per the source filesystem. Note that inherited behaves in a similar manner to default. If you were to set a given property of the parent of a child filesystem, the child would have that same property's source listed as "inherited from..." whether that child property source had been default or inherited.

There are several properties that must be set (using zfs create) at filesystem creation time. These are: casesensitivity, normalization, jailed, utf8only. If you are trying to replicate such a filesystem where one of these properties is different from source to destination, destroy the destination filesystems before you begin β€” otherwise the utility will fail with an error. 😭

There are several other properties that may not be technically readonly but it was judged that it would not make sense to try and transfer them. They are: type, creation, used, available, referenced, compressratio, mounted, version, primarycache, secondarycache, usedbysnapshots, usedbydataset, usedbychildren, usedbyrefreservation, version, volsize, mountpoint. There are several properties in FreeBSD 8.2 that are not supported and hence will not be transferred via zxfer, they are: idevices, aclmode, aclinherit, nbmand, shareiscsi, vscan, xattr. If using -S, all filesystems in the pools containing the source directories/files will be created on the destination if they aren't in existence already, whether they are to hold files/directories or not.

  • -S β€” rsync(1) mode. If -S is specified, rsync(1) mode is triggered. If -S is absent, zfs(8) send mode is in effect. Several options have different effects depending on whether -S is present or not. Consult the relevant option section before using. 🧚
  • Still on that note: you can also transfer to read-only destination filesystems, but bestie, that only works if either -o or -P is enabled. That's the whole vibe check on that one πŸ’…
  • -v β€” (v)erbose mode. She talks more. That's it, that's the tweet ✨

🌈 Normal | zfs send/receive mode

(i.e. -S is absent β€” this is the zfs send/zfs receive era, no rsync in sight πŸ”₯)

  • -c β€” a space-delimited list of SMF services, in quotes, that get temporarily disabled before the source gets unmounted, then re-enabled after the destination's mountpoint has been changed. Lowkey essential. Also: this one requires -m 🫢
  • -d β€” (d)elete snapshots on the destination that are absent on the source. Ngl this might just be necessary for things to function properly anyway, in a similar fashion to -F. This will happen if you're using zxfer for routine backups and in the interim you're culling snapshots on the source. The snapshot that corresponds to the most recent snapshot on the destination often gets deleted on the source. And here's the thing: any snapshots on the destination more recent than the most recent common snapshot must be deleted for zfs send to work. Not optional, bestie. Periodt πŸ’€
  • -g β€” (g)randfather protection, the icon of the fam πŸ‘‘. Specify a number of days old (relative to the system date) at and above which snapshots on the destination won't be deleted. For use with -d. This gives you a safeguard to protect the "grandfathers" in a Grandfather-Father-Son (GFS) snapshot collection on a destination. Grandfathers are the snapshots that never get deleted (often yearly, half-yearly or quarterly snapshots) 🧚

Note that for this to work properly, you must set -g so that it does not inadvertently protect the "fathers" β€” those ones will get deleted on the source by your snapshot management tool, and hence will need to be deleted on the destination too. And if you go a long time between backups with an otherwise well-set -g, you may end up with fathers on the destination that are the age of grandfathers 😭 and so you'll need to either relax the number of days you allowed for, or manually delete those fathers. Chaos, honestly.

For example, specifying -g 375 should protect snapshots as old or older than 375 days, which could be useful where grandfathers are yearly and monthly fathers are kept for a period of a year or less. This gives us 9 days of grace period to make a backup without throwing an error β€” but do note that during this grace period, grandfathers less than 375 days old are not protected. πŸ™ˆ

  • -m β€” after sending all snapshots, (m)igrate the source to the destination filesystem by unmounting the source filesystem and changing the new filesystem's mountpoint to that of the source. This option includes -s. It also includes -P. Note that zxfer does not prevent you from migrating a default mountpoint (e.g. pool/filesystem) to something that will be potentially confusing, so be sure this is what you want before executing. Like, be so sure. Main character decision energy, but make it an informed one πŸ’…
Note also that -O and -T (i.e. remote transfers) are not supported with this option enabled. Full stop, no cap.
  • -N β€” replicate the listed filesystem. Note the provisos listed below in -R. It works similarly but is (N)ot recursive β€” hence the name, she's giving selective 🌸. e.g. specifying -N tank/tmp will transfer only tank/tmp.
  • -O β€” specify an (O)rigin user@host. This allows transfers FROM a remote host via ssh. e.g. whatever filesystems you specify as the source are taken to exist on that remote host πŸ¦‹

For this to work, you must have a working ssh connection from your local machine to the remote host, using public key based authentication (so that you won't have to keep entering a password every time a command is sent over ssh). You must have zxfer on your local machine. The remote host must support zfs(1) β€” and so does your local machine, if -T is not also used.

This option has been somewhat tested in FreeBSD 8.2 and Solaris 11 Express and appears to be working. (Consider it alpha level.) Those are the only operating systems it has been tested on, and possibly/probably won't work on others 😬

Note also that transferring between different operating systems, and even different versions of the same operating system (e.g. FreeBSD 8.1 and 8.2), has not been tested and will probably fail or give unexpected results. As zfs(1) has been developed, new properties have come into being, and those properties have either been supported in FreeBSD or they haven't. So it's genuinely difficult to know what a user would like such properties to be when they're copied from one OS (or version) to another. At least between OS versions, you are advised to use the same OS and zpool and zfs version from source to destination. It's not likely this gets tested thoroughly in the near future, as it requires an estimated n^2 number of tested transfers, where n is the number of operating system variations to support. You are welcome to try it though, and report bugs back πŸ™

Note that if at all possible, do not try and be clever and run zxfer with both an origin and a target host. While this may work, the transfer will be piped from the remote origin to the local machine and from there to the remote target machine β€” which will be slow because it is not a direct path. It's giving middleman. It's giving unnecessary layover. ✈️

The syntax for this command is -O user@remote_host.

e.g. -O root@192.168.0.1

Please note that if you are using Solaris, you will most likely need to use "pfexec" (similar to sudo) and have a suitably privileged user. Copy the quotes and spacing exactly:

e.g. -O "user1@192.168.0.1 pfexec"

The reason this works is because the commands that are related to reading/writing/modifying data will be prefaced with it β€” in normal circumstances the command looks like:

ssh root@192.168.0.1 command ...

So with Solaris, specifying the pfexec at the end results in:

ssh user1@192.168.0.1 pfexec command ...
  • -R β€” (R)ecursively replicate all filesystems under the specified source. If you're not specifying -S, you must invoke one and only one of either -R or -N. Also note: if you enable this option, you must specify only one filesystem, and it starts without a /. e.g. specifying -R tank/tmp will transfer tank/tmp, tank/tmp/foo, tank/tmp/foo/bar, etc. β€” the whole tree, obsessed 🌳

Also note that a trailing slash on the source filesystem has a similar effect as it has in cp(1). This would primarily be used in restoring filesystems, especially pools. See Ex3 πŸ‘€

  • -s β€” make a (s)napshot of the source before replication. Note that you might want to transfer a current snapshot at the end of a transfer, since the initial transfer might take a long time β€” which would leave you with an old snapshot on the destination 😭. To do this you'll need to execute your command again at the end, and ensure that a current snapshot is taken before or during the second execution. e.g. use this option to ensure a relatively current snapshot exists, create it manually, or just wait until your own snapshotting regime does the job for you (if you have one). Girlboss delegation πŸ’œ
  • -T β€” specify a (T)arget user@host. This allows transfers TO a remote host via ssh. e.g. whatever filesystem you specify as the destination will be where the source filesystems get sent πŸŽ€

See the section on -O for notes, usage advice and warnings, because this option is very similar in operation. Same energy, opposite direction, iykyk.

The syntax for this command is -T user@remote_host. e.g. -T root@192.168.1.2

Again, please note that if you are using Solaris, you will most likely need to use "pfexec" (similar to sudo) and have a suitably privileged user. Copy the quotes and spacing exactly:

e.g. -T "user1@192.168.0.1 pfexec"


πŸ¦‹ Rsync mode

(i.e. -S is specified β€” this is the rsync girlie era ✨)

  • -d β€” (d)elete files on the destination that do not exist on the source. This is equivalent to rsync --del πŸ—‘οΈ
  • -E β€” pass (E)xclude patterns to rsync(1). e.g. if you want to specify --exclude=/boot/zfs/zpool.cache when rsync(1) is called, then use -E /boot/zfs/zpool.cache. You can specify this option as many times as you like, and it will pass each exclude pattern to rsync(1) 🫢

My understanding of how to get this to work is to specify the exclude as a continuation of the filesystem mountpoint on which the file/directory is stored. e.g. with /boot/zfs/zpool.cache it works because the mountpoint of zroot is effectively /.

  • -f β€” specify a (f)ile that contains a list of options to feed to rsync. In this event, no other options will be given to rsync. (If this option is not enabled, the default options given to rsync are -clptgoD --inplace --relative -H --numeric-ids.) πŸ“‹

It has been suggested that it might be more convenient to be able to specify custom options to rsync by means of a switch and the options in quotes or some other delimiting character. If there's enough feedback, that option might get implemented πŸ‘€ β€” so like, speak up if you want it.

  • -i β€” (i)nclude directories corresponding to ZFS filesystem mountpoints on the destination when transferring. The default is to exclude them, since the destination may correspond to a filesystem from another pool mounted there, which has its own data and would be restored independently. Respect the boundaries πŸ’…
  • -l β€” treat (l)egacy mountpoints as being equal to /. If this is not enabled, zxfer will fail with an error when it encounters a legacy mountpoint. Heads up, bestie 😭
  • -L β€” tells zxfer how many levels deep in the source filesystem tree the filesystem that was πŸŽ€originallyπŸŽ€ a backed-up pool now sits. ngl, you'll basically only need this when you're restoring, because the default level (zero) just makes filesystems from the pool level on down at the target. Example time, bestie: say we're trying to restore /storage/backups/zroot/tmp/stuff.txt, which originally lived in a pool named zroot, and we want it to end up where it came from (like at zroot/tmp/stuff.txt). We'd say -L 2 to show that the original pool is sitting 2 levels deep on the source. If we didn't set that option, the file would end up at /zroot/backups/zroot/tmp/stuff.txt 😭 which is literally not what we want.
  • -N β€” replicate the listed director(y | ies) or file(s). heads up: remember the provisos listed up above in [-R.] β€” it works similarly but it is not recursive. so -N /tmp will transfer only /tmp. that's it. that's the whole vibe. ✨
  • -p β€” (p)ersist through rsync(1) errors πŸ’ͺ. this saves you from having to feed directories into zxfer one by one when you already expect rsync to return an error at some point (e.g. when it tries to overwrite itself πŸ™ˆ).
  • -R β€” (R)ecursively replicate all the directories and files under the specified source(s) πŸ”₯. if you're specifying [-S,] then you must invoke at least one of, or both of, [-R] or [-N.]. the whole idea is that this utility makes an atomic clone of the filesystems you'll need to get your files/directories from, and then you can use rsync to decide what actually transfers within a given pool.

okay so quick note on -R β€” if you enable this option, you can specify as many directories as you like, separated by commas (with no spaces 🫢). Directories must start with a / and may or may not end with a /. go read the rsync(1) man page to get the gist of how that works. e.g. specifying -R /tmp will transfer /tmp, /tmp/foo, /tmp/foo/bar etc. πŸ¦‹

note also that zxfer will only transfer the contents of the directory / if [-l] is invoked, / is mounted as legacy, and it will only transfer across those files and directories belonging to the same ZFS filesystem as / belongs to. iykyk.

another action that is not supported is recursively transferring a directory that contains directories which are in fact filesystems from other pools β€” or in fact any included directories where the filesystem mountpoints diverge from what ZFS would assign by default. so like... don't do that πŸ’€.

  • -u β€” (u)se an already existing snapshot as the source to transfer from 🌸. you really should first check that this snapshot exists on all the filesystems that house the files and directories you're using in the transfer. this gives us the option of restoring files/directories without having to roll back. iconic behaviour, honestly πŸ‘‘

✨ EXAMPLES ✨

heads up bestie: some of these example commands are lengthy, so be sure to fix the line wrapping appropriately πŸŽ€. also, if you're sitting there wondering why zxfer isn't transferring anything, please please PLEASE read the section titled SNAPSHOTS ⭐.

Ex1 - Backup a pool (including snapshots and properties)

so we have a pool called storage and we want to back it up to backup01/pools, along with all of its snapshots πŸ’–. Grandfather snapshots are yearly, fathers are monthly, and they get deleted after 365 days. the resultant filesystem hierarchy should look like so:

  • backup01/pools/storage
  • backup01/pools/storage/home
  • backup01/pools/storage/back
  • etc.

to back this up while:

  • [-g] protecting (grandfather) snapshots older than 375 days
  • [-P] copying across the properties of each filesystem
  • [-k] storing the original filesystem properties in the file backup01/pools/.zxfer_backup_info.storage
  • [-F] forcing a rollback of destination to the most recent snapshot. given that even mounting the filesystem will cause a change, and hence cause zfs receive to fail with an error, enabling this is the way to go πŸ”₯. otherwise you'd be modifying(!) a backup, wanting to keep the changes you're making(!?) and also wanting to copy more stuff to the backup (hence it's still being used as a backup)... well, if that's what you want then don't use this option πŸ’€
  • [-d] deleting stale snapshots that don't exist on the source (e.g. if you're using a snapshot management script such as zfs-snapshot-mgmt(8), snapshots get regularly taken and regularly deleted to leave a range of frequencies of snapshots at different vintages. if you're regularly backing up to another pool that's stored off-site β€” which is highly recommended β€” you may want to delete the stale snapshots on the backup pool without having to manage the snapshots there too. this is especially true for those pools that are usually not connected to a machine, e.g. if you're using HDD as backup media. note that zfs send will also refuse to work if you have newer snapshots on destination than the most recent common snapshot on both, so it's easier to just enable it 🫢)
  • [-v] seeing lots of output (verbose)
  • [-R] copying the source filesystem recursively

use the following command:

zxfer -dFkPv -g 376 -R storage backup01/pools

note that this same command will work for both the initial replication and subsequent replications (which are potentially much faster due to incremental transfers being used ✨). also note that if you don't have any snapshots on the source, nothing will be transferred 😭. you can create a snapshot for this very occasion by adding the -s option.

Ex2 - Backup pool to HDD

using HDDs as backup media was in large part a motivation for writing this utility (using an e-SATA dock is particularly convenient πŸ’…). the source and destination are the same as Ex1.

we'll want to increase the number of copies to at least 2 or more so that we have some protection against bad sectors πŸ›‘οΈ. we won't have protection against an HDD crash, so use more than one HDD if you're doing this (mirrored or otherwise). we may also want to override the compression to make up for the multiplication in disk usage by the number of copies. here's the command:

zxfer -dFkPv -o copies=2,compression=lzjb -R storage backup01/pools

Ex3 - Restore a pool

to restore the pools we backed up in Examples 1 and 2, we'd first make sure there's a new pool named storage to copy the backup to. then we'd issue the following command:

zxfer -deFPv -R backup01/pools/storage/ storage

note that the trailing slash is what lets us copy the zroot filesystem directly to the pool level, and then the child filesystems below that, which is exactly where we want them 🎯. if the pool name is to be changed, the command becomes:

zxfer -deFPv -R backup01/pools/storage/ newpoolname

Ex4 - Backup a filesystem

maybe we just want to back up one filesystem within a pool. that's easily done:

zxfer -dFkPv -N storage/home backup01/filesystems

the resulting filesystem will be backup01/filesystems/home. this example will only replicate that exact filesystem (i.e. it is non-recursive). if we wanted to recursively transfer all filesystems under storage/home at the same time, we could do that by changing the [-N] to an [-R.]

Ex5 - Restore a filesystem

to restore the filesystem backed up in Ex4, we'd do it using the following command:

zxfer -deFPv -N backup01/filesystem/home storage

Ex6 - Recursive directory backup

maybe we want to back up a directory within a pool, using rsync to do the heavy lifting πŸ‹οΈ, while also making a backup of the properties of the filesystems transferred. transferring via [-S] lets us have different snapshotting regimes on source and destination coexisting happily πŸ’–.

zxfer -SkPv -R /storage/home backup01/rsbacks

the resulting filesystem structure will look like: backup01/rsbacks/storage/home.

if we wanted to non-recursively transfer that directory, we'd just change the [-R] to an [-N.]

Ex7 - Recursive directory restore

to restore the directory backed up in Ex6, we'd do it using the following command:

zxfer -SePv -L 2 -N /backup01/rsbacks/storage/home storage

note that we had to specify a drop back of 2 levels of filesystems, so that zxfer would know the pool level is not backup01 but storage. specifying -L of 1 would indicate the pool level was rsbacks, which it was not. leaving out [-L] would be equivalent to L=0, i.e. specifying that the pool level was backup01.

note also that this will re-create all the original filesystems that existed on the pool storage with their original properties, if they've since been deleted ✨. if those filesystems still exist, zxfer will ensure that each such filesystem has the properties it originally had. note that if those filesystems still exist, no data will be changed other than what you've specified to be rsynced across.

if you wish to not have filesystems created or properties set (just the files/directories you want rsynced), just forego [-P] and [-o.] note that this will only work on filesystems that are writable πŸ‘€.

Ex8 - Backup FreeBSD 8.2 root mirror

this will make a recursive snapshot of the root mirror, create similar filesystems on the pool storage, transfer the properties over, rsync(1) across the directories and files needed to restore the system, and destroy the snapshots when done πŸ”₯. to see how to do this in more detail, check out the zxfer wiki.

zxfer -SPkld -R
/bin,/boot,/compat,/etc,/lib,/libexec,/rescue,/root,/sbin,/tmp,/usr,/var,/vshare
-N /.cshrc,/.profile storage/zr_backup

the resulting filesystem structure will look something like: storage/zr_backup/zroot storage/zr_backup/zroot/usr storage/zr_backup/zroot/var etc.

Ex9 - Restore FreeBSD 8.2 root mirror

to restore the config files backed up in Ex8, we'd do it using the following command after installing the system, installing zxfer and booting up in the system 🌈. this uses the snapshot auto-2010-11-14_14.00. note that for this to work properly /var/empty must be set to mutable. also, the flag schg must be turned off to transfer properly ⚠️. for the full sequence of commands, see the zxfer wiki. here is the command for the zxfer part of the procedure (note you will need to add any directories of your creation kept on your zroot e.g. vshare):

...and okay bestie, before we get to Ex10, here's the tail end of that Ex9 command from before, in full, no crumbs left behind:

zxfer -SpldBv -E /boot/zfs/zpool.cache -u auto-2010-11-14_14.00 -L 2 -N
$zrootdir/.cshrc,$zrootdir/.profile -R
$zrootdir/bin,$zrootdir/boot,$zrootdir/compat,$zrootdir/etc,$zrootdir/lib,$zrootdir/libexec,$zrootdir/rescue,$zrootdir/root,$zrootdir/sbin,$zrootdir/tmp,$zrootdir/usr,$zrootdir/var,$zrootdir/vshare
zroot

Yeah. That's the whole vibe: -SpldBv, -E, -u, -L 2, -N, a -R list that goes on forever, and zroot at the end. Iconic.


πŸ¦‹ Ex10 β€” Migrate a filesystem

Sometimes you wanna migrate a filesystem, and yeah, properties come along for the ride. πŸ’… But lowkey important: this is only allowable when the original mountpoint is not the default one (i.e. pool/filesystem).

So in this example the new filesystem lands in new_pool/location/fs. And if the old mountpoint was /path/to/old_fs? Then that's literally going to be the mountpoint of the new filesystem. Carryover queen.

zxfer -PmFdv -N original_pool/fs new_pool/location

🌸 Ex11 β€” Compress a filesystem

So you want a compressed filesystem, bestie? Setting a compression setting on the filesystem itself is not enough β€” that only compresses new files. Which is giving "too little, too late."

What you actually want is to transfer the filesystem to another location where compression is enabled. That's the whole trick.

zxfer -PmFdv -o compression=gzip -N original_pool/fs new_pool/location

πŸŽ€ Ex12 β€” Compress a filesystem...

...and then store it back in the original location. Honestly? This is probably what you want instead of Ex11. πŸ™ˆ

Because usually when we want to compress a filesystem, it's already sitting exactly where we want it β€” we just never realised we wanted it compressed at the time. Or maybe we want something compression-adjacent, like dedup, and it wasn't supported back when we made the filesystem. So having it compressed somewhere new isn't the goal β€” we want it in the original spot.

If that's the case, we gotta migrate the filesystem. Then, if needed, upgrade the original pool so the new filesystem can actually do the thing (dedup, for instance), and then transfer it back. Here are the steps, bestie, in order:

  1. Make sure you've set aside a window where nothing is reading from or writing to the filesystem(s) in question. And if you're doing this to a system filesystem (like something such as zroot/usr), then make sure you're running these operations from a recovery disk (e.g. Fixit # in FreeBSD). Also β€” genuinely, no cap β€” it is a very good idea to have made backups of the filesystems you're about to do this to.
  2. Migrate the filesystems to a new location. e.g.
zxfer -PmFdv -N original_pool/fs new_pool/location
  1. Triple check that the new filesystems are exactly as they should be. Be very, very, very careful here. It's a good idea to have made a backup before the next step. Honestly it's probably worth practicing on a system you don't care about first. And do not β€” repeat, do not β€” blame me if something goes wrong. 😭
  2. Destroy the original filesystem. e.g.
zfs destroy original_pool/fs
  1. Make sure the original pool is upgraded to do what you want it to do (dedup perhaps β€” though you'll need to change the option setting appropriately).
  2. Migrate the filesystem back, but with the overrides you want. e.g.
zxfer -PmFdv -o compression=gzip -N new_pool/location/fs original_pool

✨ Ex13 β€” Backup a pool to a remote host

Notice this is the same as Ex2 except for option [-T,], and it uses the same options that were already explained in that example. Also note: if you're on Solaris you'll most likely need to specify pfexec (go peek at the [-O] section). Here's the command, bestie:

zxfer -dFkPv -o copies=2,compression=lzjb -T root@192.168.123.1 -R
storage backup01/pools

πŸ”₯ Ex14 β€” Restore a pool from a remote host

Reverse it, slay it. Use the following command, assuming you're restoring from a situation like Ex13. And again β€” Solaris girlies, refer to [-O.]

zxfer -deFPv -O root@192.168.123.1 -R backup01/pools/storage/ storage

πŸ’– EXIT VALUES πŸ’–

  • 0 β€” success, we ate πŸŽ‰
  • 1 β€” an error happened 😭
  • 2 β€” a command line option was incorrect πŸ’€

🌈 SEE ALSO 🌈

rsync(1), zfs(1), zpool(1) β€” go read these too, iykyk.


🧚 COMPATIBILITY 🧚

zxfer gets tested (somewhat) before release on FreeBSD 8.2-RELEASE and Solaris Express 11. It was tested on the last version of OpenSolaris and FreeBSD 8.1 as of 0.9.0, and it may keep functioning correctly there β€” but to ease the workload, testing only happens against Solaris Express 11 and whatever version of FreeBSD is being run, which in this case is 8.2.

Translation: it might work elsewhere, no promises, the test matrix is small on purpose. 🫢

🫢 THANKS 🫢

Huge thanks to Constantin Gonzalez for his constant collaboration, guidance, and for being a sounding board for ideas and decisions while this script was being developed. It's no understatement to say this script would not have been developed to the extent it has, and in its present form, if not for his encouragement, his input on design decisions, and β€” often β€” just simply agreeing that some features would be really cool, which spurred things on. πŸ’œ

Constantin's zfs-replicate script formed the original basis for this one, and was very useful as a template to follow. Thanks to Constantin also for generously allowing his code to be licensed under the BSD license, and for encouraging the existence of this project under its own banner.

You can read Constantin's blog "Constant Thinking" at constantin.glez.de β€” if you're at all interested in the world of ZFS it's worth reading regularly (his non-ZFS stuff is good too, fr fr).

Thanks also to the creators of rsync for their excellent tool. πŸ‘‘


πŸ‘‘ AUTHORS πŸ‘‘

The basis for zxfer, zfs-replicate, was written by Constantin Gonzalez. Ivan Nash Dreckman built upon that work to create the additional code and documentation required for zxfer. Constantin was invaluable throughout with feedback on design decisions, suggestions and encouragement β€” not to mention initial testing on Solaris. ✨


πŸ’€ BUGS πŸ’€

(This is a bug of ZFS on FreeBSD and not this script.) There are several properties in FreeBSD that, when set via "zfs create" or "zfs set", will have the source stay as default while others are set to local. This does not have any real impacts because these properties are not inheritable. The properties are: quota, reservation, canmount, refquota, refreservation.

Note that FreeBSD does not support the transfer of several properties. See the [-P] section for details on this.

There are several properties that get skipped over when transferring or setting properties β€” search the script for "readonly_properties" to see what they are. If you find that certain newer ZFS properties are not being transferred, there's a good chance those properties have been added to this list. That's because testing has indicated that attempting to create with, or set, those properties would cause the script to fail. If you can make a good case that the script should heed those property values, feel free to email me.

Send bug reports to ivannashdreckman at fastmailgolf dot fm β€” but not before removing the sport originating in Scotland from the email address. (Yes, really. πŸ™ˆ) And if you like zxfer and find it useful, send some feedback saying how you use it, and consider donating at some stage in the future. πŸ’–


⭐ CREDITS ⭐

zxfer is distributed under the BSD license. See the file COPYING for details.

The website for zxfer is http://www.zxfer.org. For additional help, consult the wiki on that site.


And that's a wrap, bestie πŸŽ€ β€” this page is from May 10, 2011, and yes, it's ZXFER(8), section 8, the whole entire manual. Go forth and replicate responsibly. πŸ’…βœ¨

πŸ“¦ where this page comes from