One of the impressive tools in ZOL (ZFS On Linux) is snapshots. Snapshots are a way to make a ‘instant backup’ of the files on the system. The name backup, is not completely correct. If the system dies, the snapshots won’t save you, since snapshots are on the same storage as the data, they don’t eat any unneeded extra space.


A snapshot is the state of the pool/dataset when you created the snapshot. In ZFS snapshots are cheap and fast, so its smart to use the feature to your advantige. The snapshot will remember how the files looked when you create the snapshot, and will only require extra space when changes to those files are made.

How to use

Creating a snapshot :

zfs snapshot [email protected]
zfs snapshot pool/[email protected]

Destroy a snapshot :

zfs destroy [email protected]
zfs destroy pool/[email protected]

Rename a snapshot :

zfs rename pool/[email protected] pool/[email protected]_name

List snapshots :

zfs list -t snapshot 
zfs list -t snapshot -r pool/dataset

Rollback from a snapshot :

zfs rollback pool/[email protected]

note : take care when doing a rollback.

Demo

Lets see this in a simple example. I first created a demo filesystem.

zfs create tank/demo

Lets create a first snapshot on this empty system :

zfs snapshot tank/[email protected]

Getting a list of the current snapshots : zfs list -t snapshot There is an option to show snapshots on zfs list but thats makes it even more cloudy to work.

# zfs list -t snapshot
NAME                                                USED  AVAIL  REFER  MOUNTPOINT
tank/[email protected]                                      0      -  35.9K  -

Now to generate some data, in ZFS (most COW FS) fallocate won’t work. So I use a fancy trick with head but in essence this just generates a file of approx. 10M.

head -c 10M </dev/urandom >data
head -c 10M </dev/urandom >data2
head -c 10M </dev/urandom >data3

Nothing special going on here :

# ls -lh
total 31M
-rw-r--r-- 1 root root 10M Jan 22 19:07 data
-rw-r--r-- 1 root root 10M Jan 22 19:07 data2
-rw-r--r-- 1 root root 10M Jan 22 19:07 data3

# zfs list
NAME                     USED  AVAIL  REFER  MOUNTPOINT
tank                    9.96T  11.1T  3.48G  /tank
tank/demo               30.1M  11.1T  30.0M  /tank/demo

As expected, when you see zfs list there is about 30M in use. Now since this is data that was not in our original (empty) set. The snapshot should not store any changes.

# zfs list -t snapshot
NAME                                                USED  AVAIL  REFER  MOUNTPOINT
tank/[email protected]                                  20.2K      -  35.9K  -

note : the 20.2k are meta-data changes.

Now its time to take a snapshot on the important data we just generated :

# zfs snapshot tank/[email protected]

# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
tank/[email protected] 20.2K - 35.9K -
tank/[email protected] 0 - 30.0M -

Now lets try snapshots, I did a move, a deletion, a overwrite (update) and a new file creation.

mv data data_moved
rm -rf data2
cp data_moved data3
head -c 10M </dev/urandom >data4


# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
tank/[email protected] 20.2K - 35.9K -
tank/[email protected] 20.0M - 30.0M -

Exactly what we expected, the snapshot took a “backup” of data2 and data3 which where removed and overwritten, since data1 was not changed or removed, it does not occupy extra space and data4 is a new file, so it was not there during creation of the snapshot.

Now how do we get the data back ? There are two options, first for small issue’s like accidental removals, you can use the hidden .zfs directory, for larger issue’s you can mount the snapshot on a seperate folder. In every directory on ZFS there is a hidden folder .zfs. Note that even ls -la will not pick that directory up.

# ls -ls .zfs/snapshot/
total 1
0 dr-xr-xr-x 1 root root 0 Jan 22 19:25 initial
1 drwxr-xr-x 2 root root 5 Jan 22 19:07 today

# ls -ls .zfs/snapshot/today/
total 30729
10243 -rw-r--r-- 1 root root 10485760 Jan 22 19:07 data
10243 -rw-r--r-- 1 root root 10485760 Jan 22 19:07 data2
10243 -rw-r--r-- 1 root root 10485760 Jan 22 19:07 data3

Voila, there are our data files, prior to removal/overwrite/rename. Note that with this method the snapshot is auto-mounted to /tank/demo/.zfs/snapshot/today as you might have guessed, you can also manually mount snapshots to a location. This can be done in the ‘normal’ Linux way :

# mkdir today_snapshot
# mount -t zfs tank/[email protected] today_snapshot

# ls -l today_snapshot/
total 30729
-rw-r--r-- 1 root root 10485760 Jan 22 19:07 data
-rw-r--r-- 1 root root 10485760 Jan 22 19:07 data2
-rw-r--r-- 1 root root 10485760 Jan 22 19:07 data3

To unmount just use umount

umount tank/[email protected]

Our snapshot name was not chosen very long-term minded, you can rename them to something more verbose :

zfs rename tank/[email protected] tank/[email protected]

The last thing we want to do, is remove a snapshot, lets remove the initial snapshot, since there was no data :

zfs destroy tank/[email protected]

There is another option, which I don’t trust much beside this example and I would only advice it to use with extreme care. Rollback, can be done straight back to a state of the system at the time of the snapshot creation, however only back to the latest snapshot. If you want to go back further, you need to destroy the snapshots in between.

zfs rollback -r tank/[email protected]

Note the -r will destroy any snapshots between current state and @22jan2017 snapshot.

Relevant

  • Snapshots are so cheap, people tend to forget to make them. However, cheap is not free, so making a cron to generate a snapshot every month/week/day/hour will eventually put your tank full with snapshots. Therefor I suggest using a snapshot systems that creates and prunes snapshots. I chose sanoid, a good system, this article can help you install sanoid.
  • With multiple datasets, and each having multiple snapshots, its sometimes difficult to get a good overview of howmany snapshots each dataset has. You can use this shell cmd to check :
    zfs list -t snapshot | tail -n +2 | sed 's/@/\t/g' | cut -f 1 | uniq -c

    Which generates something like :

    # zfs list -t snapshot | tail -n +2 | sed 's/@/\t/g' | cut -f 1 | uniq -c
         30 huginn/complgen3
          2 jbod1/breva
          1 jbod1/users
          5 jbod1/users/flylab
          5 jbod1/users/proteo
          2 jbod1/users/servers
          5 jbod1/users/userA