btrfs Snapshots and Backups
One of the btrfs killer-features is snapshots and its CoW.
A Snapshot is a subvolume which has a parent subvolume.
It stores only the changes by creating a copy on writes.
Snapshots can be read-only.
Like any other subvolumes snapshots can be mounted.
Snapshots introduced
A Simple test run
# Create a snapshot
echo before snap > /home/$USER/test
btrfs subvolume snapshot /home /mnt/snaps/home-old
echo after snap > /home/$USER/test
cat /home/$USER/test
# after snap
cat /mnt/snaps/home-old/$USER/test
# before snap
as expected :)
btrfs subvolume delete /mnt/snaps/home-old
Snapshots for Backups
Snapshot are excellent for backups.
- Since they are ordinary mounts we need no fancy tool for recovering.
Just jump into the directory and pick the files you need. - No worry about permissions, ownerships, ACLs …
- No need to iterate over ~1e12 small files and compare mtime.
- No changes during the backup process
You’ll ask yourself: Backup on the same disk?
Of course not.
btrfs is capable to create a readable binary stream from a subvolume
using btrfs send
. It’s counterpart btrfs receive
writes updates to a given
subvolume. Additionally, it allows to transfer the difference between two snapshots:btrfs send -p snapYesterday snapToday
.
Note: btrfs-send needs read-only snapshots!
Assumed backup.home
is a machine with btrfs and ssh. We can now transfer
the subvolume over the network.
# Create a read-only snapshot
btrfs subvolume snapshot -r /home /mnt/backup
# Initially transfer the whole subvolume
btrfs send /mnt/backup | ssh root@backup.home btrfs receive /mnt/homeFromPC
# Transfer the changes since `backup`
btrfs subvolume snapshot -r /home /mnt/backup-new
btrfs send -p /mnt/backup /mnt/backup-new | ssh root@backup.home btrfs receive /mnt/homeFromPC
Neat, isn’t it?
A script could help to create daily backup snapshots, transfer them and delete the outdated. Fortunately there are nice tools around: Available Backup Tools
I picked btrbk cause its fits perfect for my case.
Backups to a none-btrfs target
Obviously, my target is not btrfs powered.
The solution is sshfs + image files.
# Mount the remote target
sshfs -o uid=0 -o gid=0 -o reconnect remote:backups /mnt/remote
# Create a image file (100GB)
dd if=/dev/zero of=/mnt/remote/btrfs-1.img bs=1 seek=100G count=1
# Make it btrfs
mkfs.btrfs /mnt/remote/btrfs-1.img
# Mount it
mount /mnt/remote/btrfs-1.img /mnt/backup
You could now use /mnt/backup
as btrfs backup target.
Out of space
Time is passing, things are growing.
No Problem for btrfs.
Just add another device
# Create another image file: btrfs-2.img
dd if=/dev/zero of=/mnt/remote/btrfs-2.img bs=1 seek=100G count=1
# Add it
btrfs device add /mnt/remote/btrfs-2.img /mnt/backup
# Check it
btrfs filesystem show /mnt/backup
Untrusted target? Encryption!
Thanks to the image files it is possible to add an encryption layer.
# Create a image file (100GB)
dd if=/dev/zero of=/mnt/remote/btrfs-1.img bs=1 seek=100G count=1
# Create a password key file
echo "my-32-char-super-secret-password" > /root/luksKey
# Encrypt the image
cryptsetup luksFormat --key-file=/root/luksKey /mnt/remote/btrfs-1.img
# Decrypt and create a mapper device
cryptsetup luksOpen --key-file=/root/luksKey /mnt/remote/btrfs-1.img backup_btrfs-1
You can now use the /dev/mapper/backup_btrfs-1
device to setup
your encrypted remote btrfs backup target.
That’s it :)
PS: I use this setup currently on one of my Hetzner root servers in conjunction with a StorageBox.