ZFS Snapshots and Backups Part 2: Use Snapshots to Make Backups to a Home Server
Part 1: Getting Started with Snapshots
Part 2: Use Snapshots to Make Backups to a Home Server
In Part 1 I explained how I've started using the ZFS filesystem on FreeBSD and how quickly I've come to appreciate one of its most powerful features: snapshots.
Snapshots are useful for tracking changes in my home directory, but these snapshots are stored locally on the device. They are not backups of the data. To employ ZFS snapshots as part of my personal backup strategy, these local snapshots need to be copied to a remote device.
I use the commands zfs-send(8) and zfs-receive(8) to make the first full backup from my laptop CLIENT to my home SERVER. Subsequent incremental backups track changes over time as measured against that full backup.
In this HOWTO both my CLIENT and SERVER are running FreeBSD and are using the ZFS filesystem.
- Encrypted Dataset
- Locks and Mounts
- Testuser
- SSH Keys
- ZFS Allow
- First Backup
- Incremental Backups
- Next: Automate
- Resources
Encrypted Dataset
On SERVER use zpool-list(8) to display existing pools:
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
zroot 896G 4.46G 892G - - 0% 0% 1.00x ONLINE -
In pool zroot
I create a new encrypted dataset called backup
using zfs-create(8) to hold the snapshots of data copied over from CLIENT.
Encryption in ZFS is per-dataset and applied at creation, and in this instance we want to be prompted to enter a passphrase whenever we mount the dataset:
# zfs create -o encryption=on -o keyformat=passphrase -o keylocation=prompt zroot/backup
Enter new passphrase:
Re-enter new passphrase:
I organize the backups by sub-directories of [hostname_of_CLIENT]/[dataset]
, so I create a child dataset as:
# zfs create zroot/backup/[hostname_of_client]/[dataset]
In this HOWTO I use the example hostname of oumuamua
for my CLIENT, and I will be backing up the home
dataset of my user. This translates as:
# zfs create zroot/backup/oumuamua/home
Child datasets inherit the encryption property by default from the parent dataset. Verify with the zfs-get(8) command:
# zfs get -r encryption,keylocation,keyformat zroot/backup
NAME PROPERTY VALUE SOURCE
zroot/backup encryption aes-256-gcm -
zroot/backup keylocation prompt local
zroot/backup keyformat passphrase -
zroot/backup/oumuamua encryption aes-256-gcm -
zroot/backup/oumuamua keylocation none default
zroot/backup/oumuamua keyformat passphrase -
zroot/backup/oumuamua/home encryption aes-256-gcm -
zroot/backup/oumuamua/home keylocation none default
zroot/backup/oumuamua/home keyformat passphrase -
Locks and Mounts
There are two parts to accessing encrypted datasets: (un)loading encryption keys, and (un)mounting the datasets.
Once I enter the passphrase to load the encryption key, I can zfs-mount(8) and unmount the dataset without being prompted again for the passphrase (key stays in memory):
# zfs umount zroot/backup
# zfs mount zroot/backup
If I both unmount and zfs-unload-key(8), the dataset can't be re-mounted:
# zfs umount zroot/backup
# zfs unload-key -r zroot/backup
1 / 1 key(s) successfully unloaded
# zfs mount zroot/backup
cannot mount 'zroot/backup': encryption key not loaded
Its necessary to load the key first, then (re)mount the encrypted dataset:
# zfs load-key zroot/backup
Enter passphrase for 'zroot/backup':
1 / 1 key(s) successfully loaded
# zfs mount zroot/backup
Testuser
Before I start using ZFS to move large amounts of data for my primary user, I create a new user account for the express purpose of testing backups.
On both my CLIENT and SERVER systems, I use adduser(8) to create testuser
. The adduser
utility is interactive and walks through the steps for creating a new user account:
# adduser
Accept all the defaults.
SSH Keys
ZFS will use SSH to send data from the CLIENT to SERVER.
I use a combination of SSH keys and keychain(1) to provide passwordless logins between local and remote devices. This will become especially useful in the future when the process of sending and receiving data is scripted and automated.
See my earlier post Configure SSH on FreeBSD for Passwordless Logins to Servers about configuring testuser
on both CLIENT and SERVER to use SSH keys for login.
ZFS Allow
By default, many ZFS commands require root
privileges to execute.
I want to avoid excessive use of root
. Use zfs-allow(8) to delegate to testuser
the ability to create, send, receive, and destroy snapshots for home
on CLIENT and SERVER.
For testuser
on CLIENT
To run zfs send
I use this combination:
# zfs allow testuser compression,create,destroy,diff,hold,mount,send,snapshot zroot/home
View permissions:
# zfs allow zroot/home
---- Permissions on zroot/home -----------------------------------
Local+Descendent permissions:
user testuser compression,create,destroy,diff,hold,mount,send,snapshot
For testuser
on SERVER
To run zfs receive
the testuser
requires more extensive delegation of privileges to manage snapshots on the remote device. I use this combination:
# zfs allow testuser atime,compression,create,destroy,diff,hold,mount,mountpoint,readonly,receive,refreservation,release,rollback,snapshot,userprop zroot/backup/oumuamua/home
First Backup
WARNING
Confirm before sending a snapshot that SERVER has sufficient space to receive a full backup of data from CLIENT.
The first time ZFS is used to send a snapshot from CLIENT to SERVER, it performs a full backup (or replication) of the data.
Switch to the testuser
account:
$ su -l testuser
$ whoami; pwd
testuser
/home/testuser
Take a snapshot labelled @test1
of home
:
$ zfs snapshot zroot/home/testuser@test1
NOTE
When sending a snapshot of a dataset, you must append a [location]
name to the name of the destination. The only restriction to the name is that it must not already exist on the destination.
The command to replicate that snapshot is:
$ zfs send [options] [snapshot] | ssh [server_ip_address] zfs receive [options] [dataset][location]
This translates in the example below as a command that:
- creates a
send
data stream with verbose stats (v
) of the zroot/home/testuser@test1
snapshot, then- pipe that stream to SSH SERVER (address:
192.168.1.11
) using - passwordless login courtesy of the SSH key pair created earlier, so that
- SERVER can
receive
the stream with - options
-o atime=off -o readonly=on
and - option
-u
to ensure that the received filesystem remains unmounted, and - save the stream to
zroot/backup/oumuamua/home/testuser
$ zfs send -v zroot/home/testuser@test1 | ssh 192.168.2.10 zfs receive -o atime=off -o readonly=on -u zroot/backup/oumuamua/home/testuser
NOTE
See https://unix.stackexchange.com/q/467001 for the reasons behind options atime=off
and readonly=on
.
SSH into SERVER and confirm that the snapshot was created:
$ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/backup/oumuamua/home/testuser@test1 0B - 500K -
Incremental Backups
Once the initial replication is complete, you can test sending incremental snapshots.
Copy a few files into /home/testuser
and take another snapshot:
$ zfs snapshot zroot/home/testuser@test2
To replicate the differences between the first and second snapshots on the receiving SERVER, add the increment switch (-I
) and specify the name of the two snapshots.
This command requires that the first mentioned snapshot to already exist on SERVER (which is true for our example):
$ zfs send -v -I zroot/home/testuser@test1 zroot/home/testuser@test2 | ssh 192.168.2.10 zfs receive -o atime=off -o readonly=on -u zroot/backup/oumuamua/home/testuser
Note that -I
would also include any snapshots taken between the two specified snapshots. If @test2
was the last snapshot previously sent to SERVER, and I subsequently took snapshots @test3
, @test4
, @test5
, and @test6
, a single command is able to transfer all the new snapshots:
$ zfs send -v -I zroot/home/testuser@test2 zroot/home/testuser@test6 | ssh 192.168.2.10 zfs receive -o atime=off -o readonly=on -u zroot/backup/oumuamua/home/testuser
Next: Automate
Once satisfied with the process of taking snapshots on the CLIENT and sending to the SERVER for backup purposes using testuser
, I implement the above configuration for my primary user.
After configuring that to my satisfaction, the next step is twofold: first, create a schedule of snapshots/backups that retains a certain number of snapshots; second, automate the whole process of creating/destroying and sending/receiving snapshots between CLIENT and SERVER.
Next: Automate Snapshots and Backups (TODO)
Resources
- Introduction to ZFS Replication
- OpenZFS System Administration Commands
- FreeBSD Handbook: The Z File System (ZFS)
You can like, share, or comment on this post on the Fediverse 💬
« Previous: ZFS Snapshots and Backups Part 1: Getting Started with Snapshots