Replicating an entire FreeBSD system using ZFS
I had a FreeBSD setup I wanted to replicate to another, identical computer. The source system runs ZFS and so should the receiving system. A recursive snapshot in combination with the zfs send
and zfs receive
commands proved most fruitful.
I began by booting the latest snapshot for stable/10 amd64 on the receiving system. There, I chose Shell from the menu.
First, I wiped the drive clean:
dd if=/dev/zero of=/dev/ada0 bs=128M
I now needed to replicate the GPT partitioning. On the source system, the command
gpart backup ada0
showed what needed to be typed to a file on the receiving system. I stored the following in the file /tmp/ada0.gpart.txt
on the receiving system:
GPT 128 1 freebsd-boot 40 1024 gptboot0 2 freebsd-swap 1064 8388608 swap0 3 freebsd-zfs 8389672 303905488 zroot0
Yes, this system is 4Kn ready, even if the current harddrives are 512n.
Next, I issued the command:
gpart restore -l < /tmp/ada0.gpart.txt
This created the same partition layout as on the source system, and set the GPT labels to the names indicated.
The receiving harddrive is slightly larger than then source harddrive, leaving about 140 MiB of gap between the end of the ZFS partition and the end of the drive. This gap is 100 MiB exactly on the source system.
Now, it’s time to put the bootblocks in place:
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
The receiving ZFS pool needed to be erected:
hostname hostB.FQDN kldload zfs sysctl vfs.zfs.auto_min_ashift=12 zpool create -o autoreplace=on -o failmode=continue -o cachefile=/tmp/zpool.cache -O mountpoint=legacy zroot gpt/zroot0
Yes, this is a single-disk ZFS pool.
The receiving system was running an incomplete system, and the network interface needed some handholding:
ifconfig em0 inet6 accept_rtadv -ifdisabled dhclient em0 rtsol em0 ifconfig em0
The last command will reveal the IP addresses assigned to the network interface. Choose any of these. I chose the public IPv6 address.
On the receiving system, type and hit ENTER:
nc -6 -l 88 | zfs receive -u -v -F zroot
If you don’t use IPv6, then omit the -6
option. Feel free to use any port number, but remember to type the right one on the source system. I do recommend using a privileged port number, i.e. less than 1024.
On the source system, type these commands and hit ENTER after typing the final command:
zfs snapshot -r zroot@transfer zfs send -R -v zroot@transfer | nc -N -v IP-address-of-the-receiver 88
If all goes well, you should have replicated the source system onto the receiving system. In my case, the entire transfer took roughly 20 minutes at near gigabit ethernet wire speed.
The receiving system needs some tuning before it’s ready for use.
First, you need to set the bootfs
property to the preferred BE, if you use BEs at all. In my case:
zpool set bootfs=zroot/ROOT/20150227-r279351 zroot
Next, you should mount the preferred BE or the initial dataset on /mnt, in my case:
mount -t zfs zroot/ROOT/20150227-r279351 /mnt
Edit /mnt/etc/rc.conf
as needed. Do the same to other files within /mnt/etc
and possibly /mnt/boot
.
Copy /tmp/zpool.cache
to /mnt/boot/zfs/zpool.cache
, overwriting the existing cache file. Unmount /mnt
.
Treat other datasets as you see fit.
On both the source and receiving systems, type:
zfs destroy -r zroot@transfer
I rebooted the receiving system, and the new system works pretty well.