Btrfs Snapshots and Snapper on the Desktop
An undo button for the update that ate your afternoon

Contents
The scenario is familiar to anyone who runs a rolling distribution. You update on a Friday evening because you always do, something in the graphics stack moves, and you reboot into a machine that either shows you a black screen or shows you a desktop that has forgotten how to draw windows. It is 22:40. You have plans tomorrow that involve this computer.
With Btrfs and Snapper configured properly, that evening costs you ninety seconds: reboot, pick the pre-update snapshot from the boot menu, confirm the rollback, carry on. Without it, you are in a TTY with a phone in one hand reading a forum thread from someone with a different GPU.
I have run this layout on my workstation for four years and used the rollback maybe six times. Six times in four years is a low number that is entirely worth the setup, because the alternative on each of those six evenings was several hours.
Why the subvolume layout is the whole game
Snapper is a scheduler and a UI. Btrfs does the work. And Btrfs can only snapshot a subvolume, which means the decision that determines whether any of this works is how you carved up the filesystem before you installed anything.
The layout that works is the one openSUSE popularised and most distributions now default to:
| |
The reasoning behind each split matters more than the names.
@ and @home are separate because they need different policies. Rolling / back to Tuesday to undo a bad kernel should not roll your documents back to Tuesday and eat three days of work. That is the single most important line in this article, and getting it wrong is how people conclude that Btrfs ate their files. Btrfs did what you asked; you asked for the wrong thing.
@var_log is excluded from root snapshots because rolling back the system should keep the logs that explain why you rolled back. Losing the evidence during the recovery is a special kind of annoying.
@pkg is excluded because the package cache is large, entirely regenerable, and there is no version of “I want last week’s downloaded package files back” that is worth the space.
Mounting looks like this, and the options are doing real work:
| |
compress=zstd:1 is worth turning on and forgetting. Level 1 costs almost no CPU and typically gives 30–50% on a general-purpose root, and compressed data means fewer bytes moved off the SSD, so on modern hardware it is frequently faster than no compression. noatime stops every read from becoming a write, which on a copy-on-write filesystem means every read stops creating new metadata extents.
Configuring Snapper
Two configs: one for root, one for home. They want quite different settings.
| |
Snapper’s create-config makes its own .snapshots subvolume, which on some distributions collides with the @snapshots you already made and mounted. If your installer set this up, do not run create-config for root — the config already exists.
The root config wants number-based cleanup driven by package transactions, and the timeline off:
| |
The home config wants the opposite — a timeline, no package hooks, and a longer tail:
| |
Why the asymmetry? Root only changes meaningfully when packages change, so hourly snapshots of it are almost all identical and pure waste. Home changes constantly, and the thing you want from it is “the version from before lunch”, which is a time question rather than an event question.
Then enable the timers and the package hook:
| |
snap-pac is the piece that makes the whole thing feel automatic. Every pacman -Syu produces a paired pre/post snapshot with the command line recorded as the description, so snapper -c root list becomes a readable history of what you did to the machine and when.
The boot menu, which is the actual feature
Snapshots you have to boot a live USB to reach are a consolation prize. grub-btrfs puts them in the boot menu, and that turns a recovery session into a menu selection.
| |
grub-btrfsd watches /.snapshots and regenerates the menu whenever a snapshot appears, so the entry for tonight’s bad update is there on the next boot without you doing anything.
Booting a snapshot gives you a read-only system. That is enough to confirm the rollback fixes the problem — boot it, see the desktop draw correctly, and you now know. To make it permanent:
| |
snapper rollback is cleverer than it looks. It makes a read-write copy of snapshot 142, sets it as the default subvolume, and keeps your broken root around as a snapshot in case the diagnosis was wrong. Nothing is destroyed, which means the rollback itself is reversible, which means you can do it at 22:40 without a knot in your stomach.
For systemd-boot users there is no equivalent as slick, and the honest answer is that the boot-menu integration is a genuine reason to stay on GRUB for a desktop.
The boring case: you deleted a file
Rollback is the dramatic feature and the rare one. The thing I actually use weekly is much smaller: retrieving a file I mangled an hour ago.
Every Snapper snapshot is a normal directory. The one for home lives at /home/.snapshots/<number>/snapshot, and it is a complete, browsable copy of your home directory as it was at that moment. No special tool, no restore procedure. You cd into it and copy the file out with cp.
| |
Snapper also has a proper diff, which is the part worth learning because it answers “what did that installer actually do to my machine”:
| |
undochange is the surgical tool: it reverts named paths to their state in the earlier snapshot and leaves the rest of the filesystem alone. Compared with a rollback, which is a sledgehammer, this is a scalpel, and it is the right answer perhaps nine times in ten.
The ALLOW_USERS and SYNC_ACL settings in the home config exist for exactly this. With them set, you can run snapper -c home without sudo and read your own snapshots as yourself, which removes the friction that otherwise stops you from bothering. A recovery mechanism you have to think about is a recovery mechanism you will not use for a file you are only 60% sure you want back.
Troubleshooting
“No space left on device” with df showing 40% free. The classic Btrfs confusion. df lies on Btrfs; use btrfs filesystem usage /. You have run out of unallocated chunks while data and metadata chunks sit half-empty. Fix with a filtered balance, which is safe and rarely needs to be repeated:
| |
Snapshots take no space, then suddenly take 60 GB. Working as designed, and worth internalising. A snapshot costs nothing at creation and grows as the live filesystem diverges from it. A big update rewrites a lot of blocks, and the old blocks stay pinned by every snapshot referencing them. snapper -c root list plus a btrfs filesystem usage after a large update tells the story.
btrfs filesystem df takes thirty seconds and everything hangs. Quota groups. Snapper enables qgroups to report per-snapshot space usage, and qgroups on Btrfs have poor performance characteristics that get worse with snapshot count. If you can live without the space column, btrfs quota disable / and the machine gets noticeably quicker.
The GRUB menu has no snapshot entries. grub-btrfsd is not running, or /.snapshots is not mounted where it expects, or you generated the config before the first snapshot existed. Check the service, then re-run grub-mkconfig.
Rolling back root broke a service that has state in /var/lib. Root went back a week; the database in /var/lib/postgresql went back a week with it. On a desktop this is rarely a problem. On a server it is the reason /var/lib deserves its own subvolume with its own policy.
A snapshot cannot be deleted: “directory not empty”. Nested subvolumes. A subvolume created inside @ after a snapshot was taken has to go first. btrfs subvolume list / shows the tree, and this is precisely why Docker’s Btrfs storage driver and Snapper are a miserable combination — put /var/lib/docker on its own subvolume, excluded.
What this does not do
Snapshots live on the same filesystem as the data. A dead NVMe drive takes every snapshot with it, cheerfully and instantly. This is the whole argument in snapshots are not backups, and Btrfs makes the mistake easier to make because the snapshot list looks so much like a backup history.
The bridge is btrfs send, which produces a stream of the difference between two snapshots and sends it somewhere else entirely:
| |
That is the same idea as ZFS replication — I went through the ZFS version in time travel for a home filesystem, and the concepts map one to one. Btrfs’s implementation is less mature and the tooling around it is thinner; btrbk is the tool that makes it survivable, and it is what I would reach for rather than writing the pipeline by hand.
For a desktop, the pragmatic answer is that Snapper handles “I broke it” and something like restic handles “the disk died”. Those are different problems and one tool does both badly. Getting the pair right is most of actually implementing 3-2-1 on a workstation.
Btrfs or ZFS, for a desktop
The question always comes up, so: on a desktop, Btrfs, and the reasons have little to do with which filesystem is better engineered.
Btrfs is in the mainline kernel. That single fact decides it. ZFS on Linux lives outside the tree for licensing reasons, which means a DKMS module that has to rebuild against every new kernel, which means the day your distribution ships a kernel that ZFS has not caught up with is the day your desktop does not boot. On a server pinned to an LTS kernel that is a manageable annoyance. On a rolling desktop that updates the kernel every three weeks, it is a recurring hazard, and it is a poor fit with a machine whose selling point is that you can recover from bad updates.
Btrfs also handles the thing desktops do that servers do not: change size. Adding a drive, removing a drive, converting between profiles — all online, all with the filesystem mounted. ZFS wants you to plan the pool geometry up front and largely stick to it. Desktops get a second SSD on a whim.
Where ZFS wins is everything that matters at scale — checksummed redundancy that self-heals, zfs send that has been reliable for twenty years, ARC caching, and RAID handling that Btrfs still cannot match. Btrfs RAID5/6 remains unsafe for real data, has been the subject of the same warning for a decade, and the honest position is to treat single-disk and RAID1 as the supported configurations and route around the rest.
For a single-drive workstation none of the ZFS advantages apply and all of the Btrfs advantages do. Use Btrfs, use RAID1 if you have two drives, and put ZFS on the NAS where its strengths are the whole point.
The verdict
If you run a rolling distribution — Arch, Tumbleweed, anything where updates are frequent and occasionally exciting — this is close to mandatory, and openSUSE has shipped it by default for a decade for good reason. The setup costs an evening if you are doing it at install time, considerably more if you are converting an existing ext4 system, which realistically means a reinstall.
If you run Debian stable and update once a month with unattended upgrades doing the security patches, the case is weaker. Your updates rarely break, and when they do, apt can usually walk backwards. Btrfs is still nice to have; it is no longer the difference between a ninety-second recovery and a lost evening.
The mistake I see most is treating the snapshot list as a safety net for everything. It is an undo button for the system, and it is superb at that one job. Ask it to be your backup and it will let you down at the exact moment a filesystem-level answer cannot help you, which is the moment the filesystem itself is gone.




