Advanced

BTRFS Snapshots

Format Debian's root filesystem with BTRFS and use Snapper to instantly roll back a risky update or config change, as a local complement to Backrest's off-site 3-2-1 backups.

BTRFS is a Linux filesystem with one killer feature for a homelab: snapshots. A snapshot freezes the exact state of a filesystem in an instant, at essentially zero cost, without copying a single byte of data upfront. Break something ten minutes after an apt full-upgrade, or overwrite the wrong .env file, and you can go back to exactly how things were before, without touching a backup at all.

This is not what Backrest and the 3-2-1 rule are for, so it's worth being precise about the difference before setting anything up.

Snapshots are not backups

A snapshot lives on the exact same disk as the data it protects. It's instant, needs no network, and it's perfect for undoing a mistake you just made, but it does nothing at all the day that disk itself dies, gets stolen, or your server burns down. That's what a real 3-2-1 backup is for: a copy on different media, ideally off-site.

Think of it this way:

  • Snapshot = an undo button. Instant, local, cheap, only useful while the disk is alive.
  • Backup = insurance. Slower, off-site, the only thing that survives the disk itself failing.

Keep both. Snapshots make you fearless about updates and experiments, backups make sure a dead drive stays an inconvenience instead of a catastrophe.

This guide assumes the root partition was formatted with Btrfs during Debian's installation. Btrfs can't be safely bolted onto an existing ext4 root after the fact, so this is a choice you make once, at install time.

Formatting the root partition with BTRFS

Debian's Guided partitioning only ever offers ext4. To get Btrfs, pick Manual partitioning instead, at the same step the main install guide describes:

Select Manual partitioning

At the partitioning method screen, choose Manual instead of Guided - use entire disk.

Create a partition table

Select the disk, confirm creating a new empty partition table, then select the resulting FREE SPACE and choose Create a new partition.

Set the partition size and type

Give the partition the rest of the disk (minus a small EFI partition if you're on UEFI, handled the same way as a Guided install), and set Use as to Btrfs journaling file system, with the mount point /.

Finish partitioning

Finish partitioning and write changes to disk, then confirm with Yes.

Done!

Everything else in the installer stays the same.

Installing Snapper

Rather than juggling raw btrfs subvolume commands by hand, Snapper is a small tool that manages the whole snapshot lifecycle: creating them, storing them tidily, pruning old ones, and automatically bracketing every apt operation with a snapshot pair.

Terminal
sudo apt install snapper
sudo snapper -c root create-config /

create-config registers a config named root for the / filesystem, and creates a dedicated .snapshots subvolume under it to store every snapshot, locked down to root. No @/@home subvolume split needed, this works fine on the plain single-subvolume layout the manual partitioning above just created.

Turn the config on by editing /etc/default/snapper:

/etc/default/snapper
SNAPPER_CONFIGS="root"

Then enable the timers that run Snapper's periodic snapshots and cleanup:

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

snapper-timeline.timer fires every hour by default, that's fixed by the unit itself, not by a config option. Each run takes one snapshot and files it under the appropriate bucket (hourly, daily, monthly, yearly); the TIMELINE_LIMIT_* settings covered further down only control how many of each bucket survive, not how often a snapshot is taken. snapper-cleanup.timer runs once a day (plus once 10 minutes after boot) to enforce those limits.

✨ To take timeline snapshots at a different frequency, for example every 6 hours instead of every hour, override the timer instead of editing the package's unit file directly:
Terminal
sudo systemctl edit snapper-timeline.timer
override.conf
[Timer]
OnCalendar=
OnCalendar=*-*-* 0/6:00:00
The empty OnCalendar= line clears the packaged hourly value first, since systemd otherwise adds new OnCalendar lines to the existing ones instead of replacing them. Apply it with sudo systemctl daemon-reload && sudo systemctl restart snapper-timeline.timer.
Debian's package also ships an APT hook (/etc/apt/apt.conf.d/80snapper) that kicks in the moment a config is listed in SNAPPER_CONFIGS: from now on, every apt install, apt upgrade or apt full-upgrade automatically gets a snapshot right before and right after it runs, with zero extra effort on your part.

Taking a snapshot

For anything outside apt, like editing a systemd unit or a Docker Compose file, take one yourself with a description that will actually mean something later:

Terminal
sudo snapper create --description "before compose change on swag"

List every snapshot with:

Terminal
sudo snapper list
Output
 # | Type   | Pre # | Date                     | Description
---+--------+-------+--------------------------+---------------------------------
0  | single |       |                          | current
1  | single |       | Tue 09 Sep 2026 18:30:00 | before compose change on swag
2  | pre    |       | Tue 09 Sep 2026 19:00:01 | apt install unattended-upgrades
3  | post   | 2     | Tue 09 Sep 2026 19:00:14 | apt install unattended-upgrades

Restoring files from a snapshot

Snapper keeps the full state of the filesystem at snapshot time under /.snapshots/<number>/snapshot, browsable like any other folder:

Terminal
ls /.snapshots/1/snapshot/etc/ssh/

To see exactly what changed between a snapshot and the live system (0 always means "current") before touching anything:

Terminal
sudo snapper status 1..0

That prints every file created, modified or deleted since. To undo those changes automatically:

Terminal
sudo snapper undochange 1..0

Or restore a single file by hand, which is often the safer, more surgical choice:

Terminal
sudo cp -a /.snapshots/1/snapshot/etc/ssh/sshd_config /etc/ssh/sshd_config
This covers the vast majority of real homelab accidents: a bad config, a deleted file, a package upgrade that broke one thing. Snapper can also rollback the entire root filesystem to an earlier snapshot (it creates a new default subvolume and boots into it on the next restart), for a system in a genuinely bad state rather than just missing one file. It's a more delicate, less common operation, worth testing once on a machine you don't mind rebooting before you actually need it for real. And if the disk itself is the problem, that's exactly the scenario your off-site Backrest backup is for anyway.

Managing and pruning snapshots

Snapshots are cheap, not free: the moment the live filesystem diverges from one, the old blocks it still references stick around. Left unchecked, months of snapshots on a server that changes a lot (container images, logs) can quietly eat real disk space.

The snapper-cleanup.timer enabled above already prunes automatically, based on the config at /etc/snapper/configs/root:

SettingDefaultWhat it does
TIMELINE_LIMIT_HOURLY / _DAILY / _MONTHLY / _YEARLY10How many timeline snapshots of each granularity to keep
TIMELINE_LIMIT_WEEKLY0Off by default
NUMBER_LIMIT50How many manual (single) snapshots to keep

Adjust these to taste, then delete one immediately by hand if you need the space back right now:

Terminal
sudo snapper delete 1

Check actual disk usage with df -h /: the numbers above only cap how many snapshots exist, not the space a very active server can still burn through between cleanups.

That's it: a rolling, automatic undo button for your server, quietly bracketing every update and pruning itself, running alongside the real backups Backrest is already taking off-site.

Copyright © 2026