BTRFS Snapshots

Reading Time: 11 min
Category: software

Looking for a quick way to backup your Linux computer? Linux’s BTRFS file system provides a “snapshot” capability that quickly saves the current state of a BTRFS subvolume. Snapshots use minimal disk space when first created.

This post describes the advantages and disadvantages of BTRFS snapshots, and how they can be used as part of a 3-2-1 backup strategy. It also provides an example that demonstrates how BTRFS’s Copy-on-Write feature efficiently manages snapshot disk usage.

Overview of BTRFS

BTRFS is one of the many file systems supported by Linux (others include ext3/4 and xfs). BTRFS is a Copy-on-Write (COW) file system. Per Wikipedia:

In traditional file systems, modifying a file overwrites the original data blocks in place. In a copy-on-write (COW) file system, the original blocks remain unchanged. When part of a file is modified, only the affected blocks are written to new locations, and metadata is updated to point to them, preserving the original version until it’s no longer needed.

BTRFS Snapshot Advantages

  • Because of COW, BTRFS backups consume very little disk space when they are first created.
  • Snapshots are created very quickly (within a few seconds).
  • Each snapshot is a subvolume that is mounted into the BTRFS file system. This makes snapshots easily accessible.

BTRFS Snapshot Caveats

  1. A snapshot as well as the original data is stored on the same disk (in fact, per the description of COW above, the snapshot IS the original data). If that disk fails, then your data and all snapshots of that data are lost. Therefore, snapshots are not “real” backups. You should always backup important data using the “3-2-1” rule:
    • Keep at least 3 copies of your data
    • Use 2 or more different media
    • 1 copy should be stored offsite
  2. A snapshot creates a copy of an entire BTRFS subvolume (more details on what this means below). You should therefore organize your BTRFS subvolumes anticipating how you will use snapshots.
  3. A snapshot of a BTRFS subvolume does not include snapshots of BTRFS subvolumes mounted within (underneath) the subvolume being snapshot. For example, if / and /home are both subvolumes, a snapshot of / WILL NOT include /home.

How the Retired Engineer uses BTRFS Snapshots

My 3-2-1 Backup Strategy

I use an Arch Linux laptop formatted with BTRFS for my daily work. My backup strategy consists of:

  1. BTRFS snapshots as “quick and dirty” backups as described below. These protect primarily against my own stupidity (which should never be underestimated!)
  2. Back up important data to a Network Attached Storage (NAS) which uses three ZFS-formatted disks as a RAIDZ1 array. This NAS is located on-site within my home. This backup protects against hardware failure with my laptop.
  3. The NAS periodically encrypts and backs up its data to off-site S3-compatible cloud storage. This protects against a catastrophic failure (whole house power spike, town destroyed by volcano or hurricane, etc).

In summary, I have at least 3 copies of my data (on laptop, backed up on a local NAS, and off-site in cloud storage). I use at least 2 different media (laptop SSD, NAS HDDs, and cloud). One copy is stored off-site in the cloud.

How I Use BTRFS Snapshots

Arch Linux is installed on a BTRFS file system on my laptop (/). Within that top-level / file system, I have the following BTRFS subvolumes: /home, /opt as well as /var/cache and var/log. Within /home, I have the following BTRFS subvolumes: /home/re/swdev, /home/re/media and several other directories.

Here are my top-level BTRFS subvolumes as seen by the df command:

~% df -h -t btrfs
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme1n1p2  300G  233G   66G  79% /
/dev/nvme1n1p2  300G  233G   66G  79% /var/cache
/dev/nvme1n1p2  300G  233G   66G  79% /var/log
/dev/nvme1n1p3  1.6T  1.1T  442G  72% /opt
/dev/nvme1n1p3  1.6T  1.1T  442G  72% /home

And here are the subvolumes within /home as seen by the btrfs subvolume list command:

~/bin% sudo btrfs subvolume list /home
ID 256 gen 664286 top level 5 path @home
ID 262 gen 662269 top level 256 path re/media
ID 268 gen 664283 top level 256 path re/swdev
.....
ID 258 gen 509698 top level 256 path snapshots/home/20250202
ID 265 gen 517079 top level 256 path snapshots/media/20250210
.....
ID 269 gen 616062 top level 256 path snapshots/swdev/20250729
.....

Timeshift Snapshots

I have installed timeshift on my Arch system. Each time I update Arch1, timeshift automatically creates a snapshot of / before updating any packages. timeshift also installs a Grub bootloader option that allows me to boot from recent snapshots, in case the latest Arch update messes up my system2.

Because a snapshot will not snapshot BTRFS subvolumes mounted within it (caveat 3 above), a timeshift snapshot of / includes all the system files installed in /, but does not snapshot anything in /home, /opt, /var/cache or /var/log.

This effectively causes timeshift to back up system files (which are modified by Arch updates, such as /etc, /bin, /usr/, etc) but not back up my personal files (all of which are stored either in /home or in /opt which are BTRFS subvolumes). Transient files such as caches and logs (/var/logs and /var/caches) are also not snapshotted, since those are stored on BTRFS subvolumes.

Personal File Snapshots

While timeshift snapshots are performed automatically as part of the system update process, I manually snapshot subvolumes within /home and /opt.

Because I set up /home/re/swdev and /home/re/media as BTRFS subvolumes, I can snapshot them individually whenever I feel is appropriate.

/home/re/media contains my photos, music, ebooks, and videos. I add to this media directory but never delete. Because of COW, multiple snapshots of an unchanged file do not use any additional disk space. My media directory snapshots thus use disk space very efficiently.

/home/re/swdev changes often as I work on various personal projects. Sometimes I will perform a major, messy refactor of a project’s source code. Before doing this, I create a snapshot. That way, if I totally mess things up, I can easily rollback to the time when the snapshot was taken. I can also pull individual files from a snapshot to recover those. And because all snapshots are mounted3, I can easily diff the modified file with the original4.

BTRFS Snapshot Example

Let’s see BTRFS in action!

The example below demonstrates how to create a BTRFS snapshot, and shows that the snapshot uses almost no additional disk space.

Example Overview

An empty file is created to hold a BTRFS filesystem. Once the empty file is formatted with a BTRFS file system, it becomes a disk image file. This disk image is then mounted using the Linux loop device. With this technique, you can experiment with BTRFS without modifying your existing disk partitions. The disk image file can be unmounted and deleted once the example is finished.

Dummy files of different sizes are created, and the disk usage is checked. A snapshot is taken, and the disk usage does not increase after the snapshot is created (due to BTRFS’s COW feature).

One of the dummy files is then totally rewritten with different data, and the disk usage increases by the size of the modified file (because both the modified file contents as well as the original snapshotted file contents are now stored on your disk).

We then restore the snapshotted version of the file, overwriting the modified version of the file. The disk usage decreases back to the original size, showing that the modified file is no longer stored on the disk, and that restoring the original file from the snapshot did not result in duplicated data.

In summary, this example demonstrates that BTRFS snapshots manage disk space efficiently!

Prerequisites

  • Install “btrfs-progs” with your package manager (pacman, apt, etc)
  • You must have “sudo” access on the computer where you run this example. Sudo is required to mount and unmount the BTRFS volume.

Steps

Create empty file to hold BTRFS file system

Create a 10 GB file that will be formatted with the BTRFS filesystem. The size and the file name test.img are arbitrary.

truncate -s 10GB test.img

Create BTRFS filesystem within file

Format the file with the BTRFS filesystem. This turns the file into a disk image that can be mounted and used just as you might use an external disk (example: USB flash drive).

mkfs.btrfs test.img

Create BTRFS filesystem mount point called “mnt”

Your BTRFS filesystem will be mounted in this directory. The directory name mnt is arbitrary.

mkdir -p mnt

Mount BTRFS image into “mnt”

sudo mount -o loop test.img mnt

Note that mounting the disk image requires “sudo” privilege.

Your newly mounted BTRFS filesystem can be seen using the df command:

% sync; df -h /dev/loop?
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      9.4G  5.8M  8.8G   1% /home/re/swdev/btrfs/mnt

The sync command flushes any cached data from memory to disk. Calling this ensures that disk usage is reported accurately.

The ? wildcard matches any single character (in case your computer already has other loop devices mounted).

Create two BTRFS subvolumes

Create two BTRFS subvolumes within your newly mounted BTRFS filesystem – a subvolume for working files and a subvolume for snapshots.

sudo btrfs subvolume create mnt/working
sudo btrfs subvolume create mnt/snapshots

Create four test files in working directory (size 50M, 100M, 250M, 500M)

The size of the test files is arbitrary. The files are filled with randomly generated data generated by the Linux random number generating device /dev/random

sudo dd if=/dev/random of=mnt/working/file1 bs=1M count=50
sudo dd if=/dev/random of=mnt/working/file2 bs=1M count=100
sudo dd if=/dev/random of=mnt/working/file3 bs=1M count=250
sudo dd if=/dev/random of=mnt/working/file4 bs=1M count=500

The Linux dd command copies from the if (input file, which in this case is the random number generator) to the of (output file). The command uses a block size (bs) of 1M, with the count specifying how many blocks to write. So, the first line generates 50MB of random numbers which are written to file mnt/working/file1.

Create snapshot of working directory

Dump BTRFS filesystem disk usage before and after snapshot

% sync; df -h /dev/loop?
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      9.4G  908M  8.0G  11% /home/re/swdev/btrfs/mnt

% sudo btrfs subvolume snapshot -r mnt/working  mnt/snapshots/`date -I`
Create readonly snapshot of 'mnt/working' in 'mnt/snapshots/2025-10-09'

% sync; df -h /dev/loop?
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      9.4G  908M  8.0G  11% /home/re/swdev/btrfs/mnt

NOTE: The snapshot requires no additional space, because the contents of the “working” directory and the “snapshots/2025-10-09” directories are identical.

The date -I is replaced by your shell with the current date, because the command is enclosed with backticks (a cool feature of Linux shells).

List BTRFS subvolumes underneath “mnt”

% sudo btrfs subvolume list mnt
ID 256 gen 13 top level 5 path working
ID 257 gen 13 top level 5 path snapshots
ID 258 gen 13 top level 257 path snapshots/2025-10-09

Note that the snapshot snapshots/2025-10-09 is itself a subvolume. In other words, snapshotting an existing subvolume results in the creation of a new subvolume with identical contents to the original subvolume. The snapshot does not consume any additional disk space.

Change contents of 500 MB file

Overwrite existing 500 MB file with newly generated random data.

% sudo dd if=/dev/random of=mnt/working/file4 bs=1M count=500
500+0 records in
500+0 records out
524288000 bytes (524 MB, 500 MiB) copied, 1.65737 s, 316 MB/s

% sync; df -h /dev/loop?
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      9.4G  1.4G  7.5G  16% /home/re/swdev/btrfs/mnt

Because the contents of mnt/working/file4 and mnt/snapshots/2025-10-09/file4 are now entirely different, the BTRFS filesystem now uses 1.4G of disk space versus 908M used previously.

Restore 500 MB file from snapshot

Restore mnt/working/file4 with the contents of the snapshot. This causes the working version of file4 and the snapshot of file4 to once again be identical.

% sudo cp mnt/snapshots/2025-10-09/file4  mnt/working
% sync; df -h /dev/loop?
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      9.4G  908M  8.0G  11% /home/re/swdev/btrfs/mnt

Note that after file4 is restored from the snapshot, making the working version of file4 and the snapshot of file4 identical, the reported disk usage decreases back to 908M.

Clean up

You can now unmount the BTRFS disk image and delete it.

% sudo umount mnt
% rm test.img

Before unmounting, make sure that your current working directory is not within the BTRFS disk image (i.e. within mnt). If it is, you will get a “device in use” error message when you try to unmount.

Source Code

The source code for this example is available at https://github.com/retired-engineer/blog-examples.

Conclusion

I hope you enjoyed learning about BTRFS snapshots. If you would like to see additional posts on BTRFS, let me know in the comments below.


  1. I update Arch Linux weekly because Arch follows a rolling release model (updated packages are released as soon as they are available rather than all updates being packaged into a single large release). ↩︎

  2. In the 3+ years that I’ve used Arch Linux, an update has never rendered my system unbootable. I have however had an update fail before completing (due to corruption caused by a program I use), and booting into a snapshot allowed me to recover. ↩︎

  3. I store my snapshots at /home/snapshots/swdev/--date--↩︎

  4. I could use git (or any configuration management tool) to serve the same purpose, but I often find BTRFS snapshots easier to use. ↩︎