Getting Started with ZFS Snapshots
I use the snapshot feature of ZFS to track daily changes in my home directory on FreeBSD. This enables me to do things like restore files accidentally deleted and make backups to remote devices.
Introduction
FreeBSD offers excellent integration of the ZFS file system in its base operating system, and during installation I chose to use ZFS and put the entire root filesystem on a single "pool" on my storage device to hold all my "datasets" (auto-created by the FreeBSD installer). The FreeBSD Handbook has a very helpful chapter explaining ZFS and its terminology.
After booting into my new system, I can view the ZFS layout with zfs-list(8) in the format [pool]/[dataset]/[child_dataset]
.
A sample output:
$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 184G 684G 96K /zroot
zroot/ROOT 9.71G 684G 96K none
zroot/ROOT/default 9.71G 684G 8.16G /
zroot/home 173G 684G 104K /home
zroot/home/dwa 171G 684G 170G /home/dwa
zroot/tmp 592K 684G 592K /tmp
zroot/usr 1.66G 684G 96K /usr
zroot/usr/ports 837M 684G 837M /usr/ports
zroot/usr/src 861M 684G 861M /usr/src
zroot/var 1.30M 684G 96K /var
zroot/var/audit 96K 684G 96K /var/audit
zroot/var/crash 96K 684G 96K /var/crash
zroot/var/log 804K 684G 804K /var/log
zroot/var/mail 144K 684G 144K /var/mail
zroot/var/tmp 96K 684G 96K /var/tmp
First Snapshot
From the FreeBSD Handbook:
Snapshots are one of the most powerful features of ZFS. A snapshot provides a read-only, point-in-time copy of the dataset. With Copy-On-Write (COW), ZFS creates snapshots fast by preserving older versions of the data on disk... Snapshots preserve disk space by recording just the differences between the current dataset and a previous version... [and] use no extra space when first created, but consume space as the blocks they reference change.
Sounds good!
EXAMPLE
I want to track changes to data in the home directory of my user - /home/dwa
- which is stored in the ZFS dataset zroot/home/dwa
. This is the dataset I will use for the examples provided below.
By default, snapshots are created by the root
user. I give my user the ability to snapshot my home directory (and only my home directory) by using zfs-allow(8) to grant the snapshot
permission:
# zfs allow [user] [permission] [dataset]
which translates as:
# zfs allow dwa snapshot zroot/home/dwa
Snapshots are created using zfs-snapshot(8) in the form:
$ zfs snapshot [dataset]@[snapshot]
The @
symbol is the delimiter between the dataset and the name of the snapshot.
For the first snapshot of my home I use the name @test1
, which translates as:
$ zfs snapshot zroot/home/dwa@test1
List snapshots and their properties with zfs-list(8):
$ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/home/dwa@test1 0B - 170G -
The USED
size of the snapshot at creation is zero, though it REFERences the 170G
of data in the home directory. That size will increase as data is removed or modified in home, while the snapshot preserves its original contents.
As a test, I add/delete/modify a few files inside home
, and USED
has increased in size:
$ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/home/dwa@test1 9.88M - 170G -
Use zfs-diff(8) to list the differences between the time the zroot/home/dwa@test1
snapshot was taken and the current state of my home directory:
$ zfs diff zroot/home/dwa@test1
Unable to obtain diffs:
The sys_mount privilege or diff delegated permission is needed
to execute the diff ioctl
My user only has the snapshot
permission that was assigned earlier for this dataset. Grant my user the diff
permission:
zfs allow dwa diff zroot/home/dwa
Now I can list what has been +
added, M
modified, and -
deleted:
$ zfs diff zroot/home/dwa@test1
M /home/dwa/.local/state/nvim/swap
- /home/dwa/tmp/video_list
+ /home/dwa/z13245.mp4
Snapshots are stored in a hidden .zfs/snapshot/
directory inside the dataset. List the contents by directly calling on that directory:
$ ls /home/dwa/.zfs/snapshot/
test1/
I can restore the missing video_list
file simply by copying it from the snapshot back to the original directory:
$ cp /home/dwa/.zfs/snapshot/test1/tmp/video_list /home/dwa/tmp
Second Snapshot
Take another snapshot:
$ zfs snapshot zroot/home/dwa@test2
List the snapshots again and their properties:
$ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/home/dwa@test1 15.0M - 170G -
zroot/home/dwa@test2 0B - 171G -
Changes continue to accumulate in home
that are being tracked since the creation of @test1
. Snapshot @test2
was just created and uses zero space.
Automate Snapshot Creation
Instead of creating snapshots of home
manually, I create a script that performs the task:
#!/bin/sh
snap="zroot/home/dwa@$(date +%Y-%m-%d_%H%M%S)"
zfs snapshot $snap
Save as zfs_snapshot_home.sh
in ~/bin
.
Edit the user's crontab
to run the script daily:
$ crontab -e
Create a cronjob that will run the script daily at a designated time (12:15):
#minute hour mday month wday command
15 12 * * * /home/dwa/bin/zfs_snapshot_home.sh
Destroy Snapshots
WARNING
When destroying snapshots, make sure there is an @
in the snapshot name portion of the command. Otherwise, you will destroy the underlying file system!
The zfs-destroy(8) command is used to delete snapshots. Again, by default, its a function only root
can perform.
Allow my user to destroy snapshots by granting permission for destroy
and mount
, limited to the zroot/home/dwa
dataset:
# zfs allow dwa destroy,mount zroot/home/foo
View what users and permissions have been assigned to them on a dataset:
$ zfs allow [dataset]
which translates in this example as:
$ zfs allow zroot/home/dwa
---- Permissions on zroot/home/dwa ---------------------------------------
Local+Descendent permissions:
user dwa destroy,diff,mount,snapshot
Destroying a snapshot (example: @test2
) is as simple as:
$ zfs destroy zroot/home/dwa@test2
I have quickly come to appreciate how useful snapshots can be. But so far everything has transpired on my local storage, and snapshots are not backups.
Next: How to Use ZFS Snapshots to Make Backups to a Home Server (TODO)
Resources
- FreeBSD Handbook: Managing Snapshots
- Basics of ZFS Snapshot Management
- OpenZFS System Administration Commands
You can like, share, or comment on this post on the Fediverse 💬
« Previous: FreeBSD: After the First Boot