Skip to content
Go back

Snapper for Btrfs Snapshots on Root Filesystems

By SumGuy 10 min read
Snapper for Btrfs Snapshots on Root Filesystems

You Ran the Update. The X Server Didn’t Survive.

It’s a Tuesday evening. You run dnf update on your Fedora workstation, reboot, and the display manager refuses to start. You stare at a blinking cursor. You know something in that package batch broke the Nvidia driver handshake, but you have no idea which one and no fast way back.

If you were on openSUSE with Snapper configured, this conversation ends here: you reboot, pick the pre-update snapshot from GRUB, log into your working system, run one command to roll back, and carry on with your evening. Instead you’re Googling nouveau driver black screen fedora 41 at midnight.

That’s the pitch. Let’s set it up properly.


What Snapper Actually Is

Snapper started as an openSUSE project and is now distro-agnostic. It’s a command-line tool (and optionally a daemon) that manages Btrfs snapshots — creating them, scheduling them on a timeline, cleaning up old ones by configurable retention policies, and integrating with package managers so every install/remove gets an automatic before/after pair.

The key insight is that Btrfs snapshots are near-instant and almost free at creation time because they’re copy-on-write. The space cost only accumulates as the live filesystem diverges from the snapshot. A fresh snapshot of your root costs roughly nothing. That’s what makes “snapshot before every update” actually practical, unlike rsync-based approaches.

Snapper handles:


The Subvolume Layout That Makes This Work

Here’s where a lot of people stub their toes. Snapper snapshots live inside the Btrfs filesystem. If your entire root is one flat Btrfs volume with no subvolume structure, snapshots of / will include /home, /var/log, /var/cache — and rolling back will also roll back your home directory to the state it was in before the update. That’s almost never what you want.

The correct layout, which openSUSE ships by default and which you should replicate on any distro:

Btrfs pool (mounted at /mnt during install)
├── @ → mounted at /
├── @home → mounted at /home
├── @var → mounted at /var (or @var-log at /var/log)
├── @tmp → mounted at /tmp (or just noatime on @)
└── @snapshots → .snapshots directory (managed by snapper)

When Snapper snapshots @, it creates a read-only copy of just the root subvolume. Your home directory lives in @home and is completely unaffected. Your package cache in @var doesn’t bloat the snapshot chain.

Check your current layout:

Terminal window
sudo btrfs subvolume list /

If you’re installing fresh, set this up during partitioning. If you’re retrofitting an existing Btrfs root with no subvolumes, this is a painful migration — you’ll need to boot from a live USB, create the subvolume structure, and move data. Worth doing right the first time.

Your /etc/fstab should look roughly like this for a properly segmented layout:

UUID=<your-uuid> / btrfs subvol=@,compress=zstd,noatime 0 0
UUID=<your-uuid> /home btrfs subvol=@home,compress=zstd,noatime 0 0
UUID=<your-uuid> /var btrfs subvol=@var,compress=zstd,noatime 0 0
UUID=<your-uuid> /.snapshots btrfs subvol=@snapshots,noatime 0 0

The @snapshots subvolume being separate matters: it means snapshots themselves don’t get recursively snapshotted when you snapshot @.


Installing Snapper

Fedora / RHEL

Terminal window
sudo dnf install snapper python3-dnf-plugin-snapper grub-btrfs

Debian / Ubuntu

Terminal window
sudo apt install snapper snapper-gui inotify-tools
# apt hooks require a bit more manual setup — covered below

openSUSE

It’s already installed and pre-configured. Lucky you.

Arch Linux

Terminal window
sudo pacman -S snapper snap-pac grub-btrfs

First-Time Configuration

Create a Snapper config for your root filesystem:

Terminal window
sudo snapper -c root create-config /

This creates /etc/snapper/configs/root and the /.snapshots directory inside your Btrfs pool. Take a look at what it generated:

Terminal window
sudo cat /etc/snapper/configs/root

The key fields you’ll want to tune:

/etc/snapper/configs/root
SUBVOLUME="/"
FSTYPE="btrfs"
# Timeline snapshot creation
TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
# Retention counts
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="0"
TIMELINE_LIMIT_YEARLY="0"
# Number-based cleanup for pre/post pairs
NUMBER_CLEANUP="yes"
NUMBER_MIN_AGE="1800"
NUMBER_LIMIT="50"
NUMBER_LIMIT_IMPORTANT="10"

For a homelab root filesystem, TIMELINE_LIMIT_DAILY="7" and NUMBER_LIMIT="50" are reasonable starting points. Tune them based on how much free space you have and how often things break.

Enable and start the timer:

Terminal window
sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer

Take a manual snapshot to confirm it works:

Terminal window
sudo snapper -c root create --description "initial baseline"
sudo snapper -c root list

Output looks like:

# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+--------------------------+------+---------+---------------------+---------
0 | single | | | root | | current |
1 | single | | Fri May 23 10:14:22 2026 | root | | initial baseline |

Pre/Post Hooks Around Package Managers

This is where Snapper gets genuinely useful. The package manager plugin intercepts every install, update, or remove, takes a snapshot before, runs the transaction, then takes a snapshot after. You end up with matched pairs you can diff or roll back from.

Fedora / DNF

Terminal window
sudo dnf install python3-dnf-plugin-snapper

That’s it. From now on, every dnf install or dnf update automatically creates a pre/post pair. Test it:

Terminal window
sudo dnf install htop
sudo snapper -c root list

You’ll see a pre and post pair tagged with the package name.

openSUSE / Zypper

The snapper-zypp-plugin package handles this. On openSUSE Tumbleweed it’s already active. On Leap you may need:

Terminal window
sudo zypper install snapper-zypp-plugin

Debian / Ubuntu (apt)

Apt doesn’t have a first-class plugin for this, but you can wire it up with apt hooks in /etc/apt/apt.conf.d/80snapper:

DPkg::Pre-Invoke { "snapper -c root create --description 'pre-apt' --cleanup-algorithm number --userdata 'important=yes'"; };
DPkg::Post-Invoke { "snapper -c root create --description 'post-apt' --cleanup-algorithm number --userdata 'important=yes'"; };

Crude but functional. The important=yes userdata flag keeps these from being swept up by the aggressive number-based cleanup.


Booting Into a Snapshot with grub-btrfs

A rollback command is great, but sometimes you need to boot into a snapshot first — like when your system won’t boot at all and you need to assess the damage before committing to anything.

grub-btrfs scans your Btrfs snapshots and adds them as GRUB boot entries automatically.

Terminal window
# Fedora
sudo dnf install grub-btrfs
# Arch
sudo pacman -S grub-btrfs
# Then update GRUB
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Fedora/RHEL
sudo grub-mkconfig -o /boot/grub/grub.cfg # Debian/Arch

Enable the inotify watcher so GRUB entries update whenever a snapshot is created:

Terminal window
sudo systemctl enable --now grub-btrfsd

On next reboot, you’ll see a “Arch Linux snapshots” (or distro equivalent) submenu in GRUB. Boot into one, poke around, and if everything looks good, roll back.


Rolling Back: The Actual Point of All This

You booted into a snapshot and confirmed the pre-update state is working. Now you want to make it permanent.

First, identify your snapshot numbers:

Terminal window
sudo snapper -c root list
# | Type | Pre # | Date | User | Cleanup | Description | Userdata
---+--------+-------+--------------------------+------+---------+----------------------+---------
0 | single | | | root | | current |
14 | pre | | Tue May 20 19:02:11 2026 | root | number | dnf update |
15 | post | 14 | Tue May 20 19:04:33 2026 | root | number | dnf update |
16 | single | | Tue May 20 20:00:01 2026 | root | timeline| timeline |

Your broken update is wrapped in snapshots 14 (pre) and 15 (post). You want to roll back to 14:

Terminal window
sudo snapper -c root rollback 14

Snapper creates a new writable subvolume from snapshot 14 and sets it as the default Btrfs subvolume. On next reboot, you’re back to your pre-update state.

Reboot:

Terminal window
sudo reboot

After reboot, confirm you’re on the rolled-back system:

Terminal window
sudo snapper -c root list
# You'll see a new "rollback" snapshot documenting what happened

You can also diff between any two snapshots to see exactly what files changed:

Terminal window
sudo snapper -c root diff 14 15

Outputs a diff of every changed file — incredibly useful for hunting down which config file a package update clobbered.


Checking Disk Space

This is where people get surprised. Snapshots accumulate. Let’s look at the real picture:

Terminal window
sudo snapper -c root list
sudo btrfs filesystem df /
Data, single: total=28.00GiB, used=22.14GiB
System, single: total=4.00MiB, used=16.00KiB
Metadata, single: total=1.00GiB, used=456.41MiB
GlobalReserve, single: total=128.00MiB, used=0.00B

The metadata section grows as snapshots accumulate. Run the cleanup manually if you’re burning through space:

Terminal window
sudo snapper -c root cleanup timeline
sudo snapper -c root cleanup number

Or check how much space each snapshot is contributing:

Terminal window
sudo btrfs filesystem du -s /.snapshots/*/snapshot

If you’ve got 6 months of hourly snapshots and nobody touched the cleanup timer, that number can get uncomfortable.


The Gotcha: Snapshots Are Not Backups

Say it with me: a Btrfs snapshot is not a backup.

It lives on the same disk as your live data. Disk dies → you lose the live filesystem and every snapshot on it. RAID is not a backup either — it protects against drive failure, not accidental deletion or ransomware.

Snapshots are an undo button. Backups are your insurance policy.

To turn snapshots into something backup-adjacent, use btrfs send and btrfs receive to ship them to another pool:

Terminal window
# Send the first snapshot (full)
sudo btrfs send /.snapshots/1/snapshot | sudo btrfs receive /mnt/backup/root/
# Send incremental updates (much faster)
sudo btrfs send -p /.snapshots/1/snapshot /.snapshots/14/snapshot | \
sudo btrfs receive /mnt/backup/root/

Tools like btrbk wrap this into a proper scheduled backup workflow with retention policies and remote send over SSH. It’s worth setting up alongside Snapper rather than treating Snapper as your sole safety net.


How It Compares

Timeshift — Popular on Ubuntu/Mint, works on both ext4 (rsync mode) and Btrfs (snapshot mode). Friendlier GUI, but opinionated about the subvolume layout and doesn’t integrate with package managers. Great for desktop users who don’t want to touch a terminal.

ZFS Boot Environments — ZFS’s answer to the same problem. zectl or beadm manage boot environments (essentially whole-pool snapshots you can boot into). More powerful and truly atomic across datasets, but ZFS is a different filesystem entirely with its own licensing drama. If you’re already on ZFS, Boot Environments are excellent and arguably more robust than Snapper.

Snapper’s edge is the package manager integration. The pre/post pairing means you know exactly which transaction caused a problem without having to remember what you did yesterday. That’s the feature that makes the whole thing practical rather than a theoretical safety net you never actually use.


The Bottom Line

Snapper on a properly laid-out Btrfs root is the closest Linux gets to the “time machine” rollback experience that openSUSE users have been taking for granted for years. The setup is maybe an hour if you’re starting from scratch with the right subvolume layout — and zero effort after that because the package manager hooks handle everything automatically.

The workflow that actually works: install Snapper, configure the subvolume layout correctly (do not skip this), enable the DNF/apt/zypper plugin, install grub-btrfs, and then forget about it until something breaks. When something breaks, you’ll be glad you did.

Pair it with btrbk shipping incremental snapshots to external storage and you’ve got both an undo button and a real backup. That’s the combination worth running. Snapshots alone are a seatbelt. Snapshots plus off-pool backups are the seatbelt and the airbag.

Your 2 AM self — the one staring at a broken X server after a kernel update — will thank you.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
A Guide to Fixing OpenH264 Access Issues
Next Post
Kdenlive + OBS Studio: Screencast Pipeline

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts