BTRFS Snapshots
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.
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.
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:
SNAPPER_CONFIGS="root"
Then enable the timers that run Snapper's periodic snapshots and cleanup:
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.
sudo systemctl edit snapper-timeline.timer
[Timer]
OnCalendar=
OnCalendar=*-*-* 0/6:00:00
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./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:
sudo snapper create --description "before compose change on swag"
List every snapshot with:
sudo snapper list
# | 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:
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:
sudo snapper status 1..0
That prints every file created, modified or deleted since. To undo those changes automatically:
sudo snapper undochange 1..0
Or restore a single file by hand, which is often the safer, more surgical choice:
sudo cp -a /.snapshots/1/snapshot/etc/ssh/sshd_config /etc/ssh/sshd_config
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:
| Setting | Default | What it does |
|---|---|---|
TIMELINE_LIMIT_HOURLY / _DAILY / _MONTHLY / _YEARLY | 10 | How many timeline snapshots of each granularity to keep |
TIMELINE_LIMIT_WEEKLY | 0 | Off by default |
NUMBER_LIMIT | 50 | How many manual (single) snapshots to keep |
Adjust these to taste, then delete one immediately by hand if you need the space back right now:
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.