Efficient incremental backups at the block level
rsync compares files one by one and burns valuable time on the comparison alone once millions of small files are involved. Btrfs send and receive sidesteps that problem entirely by determining the differences between two snapshots directly at the block level, without ever walking the file tree at all.
Table of Contents
- 1. Why block level incremental backups make the difference
- 2. How send and receive fundamentally work
- 3. Full versus incremental snapshots
- 4. Practical workflow: setting up offsite replication
- 5. Comparison to rsync based backups
- 6. Automation and retention strategy
- 7. Error handling and verifying the transfer
- 8. Practical case: restoring from a received snapshot
- 9. Best practices for production use
- 10. Summary
- 11. FAQ
1. Why block level incremental backups make the difference
Classic backup tools such as rsync determine changes by walking the entire file tree of source and target and comparing metadata such as size and modification time. With directories holding millions of small files, as seen in Magento media directories or log archives, this comparison step alone becomes the bottleneck before a single byte of payload data has even been transferred.
Btrfs send and receive solves this problem at the root, since the kernel reads the differences between two snapshots directly from the filesystem's internal B tree structure instead of comparing files individually. The result is a data stream containing only the actually changed blocks, which can be compressed, encrypted, or piped straight to a target system at will.
2. How send and receive fundamentally work
btrfs send produces a binary data stream from a read only snapshot containing every operation needed to recreate the same state on another Btrfs filesystem, such as creating files, writing specific blocks, or setting metadata. This stream is not interpreted directly, but has to be turned back into an actual subvolume on the receiving side with btrfs receive.
Both commands can simply be connected through a pipe when source and target sit on the same system, or chained over SSH when the target is a remote server. It is important that both the source snapshot and, for incremental transfers, the required parent snapshot are read only at the time of sending, since Btrfs otherwise cannot guarantee a consistent starting point.
# Transfer a full snapshot locally to another Btrfs filesystem
btrfs send /.snapshots/mysql-2026-08-08 | btrfs receive /mnt/backup
# Transfer directly to a remote server over SSH
btrfs send /.snapshots/mysql-2026-08-08 \
| ssh backup-host btrfs receive /mnt/backup
3. Full versus incremental snapshots
A full transfer with btrfs send without further options transfers every single block of the snapshot and only makes sense for the very first synchronization or very small data volumes accordingly. For day to day operation, the incremental variant using the -p option is used instead, specifying a parent snapshot as a reference point and transferring only the blocks that actually changed since that reference point.
A prerequisite for an incremental transfer is that the snapshot used as parent already exists identically on the target side, since Btrfs computes the changes relative to exactly that state. If the parent snapshot is missing on the target side or was deleted there, the incremental transfer fails, and only a fresh full transfer remains as a way out.
# Incremental transfer relative to the previous snapshot
btrfs send -p /.snapshots/mysql-2026-08-07 \
/.snapshots/mysql-2026-08-08 \
| ssh backup-host btrfs receive /mnt/backup
4. Practical workflow: setting up offsite replication
A typical replication workflow starts with a one time full send to initialize the backup target, followed by regular incremental transfers that each reference the most recently successfully transferred snapshot as the parent. A script should persist the name of the most recently transferred snapshot after every successful run, so the next run knows the correct reference point.
For transfers over a public network, SSH level compression plus a dedicated SSH key with restricted permissions is worth adding, allowing only btrfs receive calls on the target side, for example through a command= restriction inside the authorized_keys file.
#!/usr/bin/env bash
# scripts/btrfs-offsite-sync.sh: incremental offsite replication
set -euo pipefail
TS=$(date +%Y-%m-%d)
LAST=$(cat /var/lib/btrfs-sync/last-snapshot)
btrfs subvolume snapshot -r /mnt/data/mysql /.snapshots/mysql-$TS
btrfs send -p /.snapshots/mysql-$LAST /.snapshots/mysql-$TS \
| ssh -C backup-host btrfs receive /mnt/backup
echo "mysql-$TS" > /var/lib/btrfs-sync/last-snapshot
5. Comparison to rsync based backups
On speed, send/receive has a structural advantage, since no file metadata has to be compared at all and only the actually changed blocks make up the data stream. With rsync, the time spent on the pure change comparison grows with the number of files, while with Btrfs send it is practically independent of file count and instead depends on the amount of changed blocks.
On consistency, the advantage clearly sits with send/receive as well: since the transfer is based on a frozen, read only snapshot, the source state cannot change anymore while the transfer is in progress. rsync, by contrast, reads files directly off the live filesystem during operation, so files can change between the start and end of a run, and in the worst case inconsistent intermediate states get transferred, especially with database files lacking separate locking.
6. Automation and retention strategy
For production use, creating new snapshots, transferring them, and cleaning up old snapshots on both sides should be combined into a single, idempotent script that can run regularly via cron or a systemd timer. Tools like btrbk take care of exactly this orchestration and manage retention rules, multiple targets, and error handling, without a custom script having to be maintained from scratch.
One important restriction applies to retention: the oldest snapshot still needed as a parent must not be deleted on either side until at least one newer, already fully transferred snapshot is available as the new reference. Careless automatic cleanup can otherwise cause the next incremental transfer to fail, forcing a fresh full transfer.
7. Error handling and verifying the transfer
If a transfer aborts due to a network error, an incomplete, temporary subvolume is often left behind on the target side, which can be resumed with the -e option on btrfs receive together with a retried transfer from the last intact state, provided the connection was only interrupted briefly. For permanently failed transfers, the incomplete subvolume should be deliberately deleted before a fresh attempt to avoid inconsistencies.
Since Btrfs already validates every block's checksum on read, corruption introduced during a transfer is usually detected at the latest on the next access to the received subvolume. For particularly critical backups, a regular scrub on the target side is still worth running, to catch corruption proactively rather than only at the moment of an actual restore attempt.
8. Practical case: restoring from a received snapshot
The actual restore process is refreshingly simple with send/receive, since the received subvolume on the backup side is already a complete, immediately usable Btrfs subvolume that never needs to be unpacked from an archive format first. Restoring onto the production server just requires sending the desired snapshot back from the backup side, either in reverse via send/receive, or, if only a single folder is needed, by copying it directly out of the read only snapshot.
For a full disaster recovery, for example after a hardware failure on the production server, a documented, regularly tested procedure is worth having: prepare a new pool or subvolume, send the desired snapshot back from the backup side via send/receive, and then activate it through the bootloader or the relevant mount configuration. A restore process that only works in theory but was never actually rehearsed is usually worthless in an actual emergency.
# Send a snapshot back from the backup side to the production server
ssh backup-host btrfs send /mnt/backup/mysql-2026-08-08 \
| btrfs receive /mnt/data/restore
# Put the restored subvolume in place at the production path
mv /mnt/data/mysql /mnt/data/mysql-old
mv /mnt/data/restore/mysql-2026-08-08 /mnt/data/mysql
9. Best practices for production use
Snapshots serving as a parent for incremental transfers should generally stay read only, since a later modification would destroy the basis for future incremental computations. Both sides should also run the same Btrfs kernel version, or at least a compatible feature set, since newer send stream formats are not always understood by older receive implementations.
For the 3-2-1 backup rule, send/receive works excellently as one of the two different storage technologies, but should not remain the only backup method, since a fundamental bug in the Btrfs implementation could theoretically affect both the source and every copy derived from it. An additional, technologically independent backup therefore still makes sense even with consistent use of send/receive.
| Aspect | Btrfs Send/Receive | rsync |
|---|---|---|
| Change detection | Direct from filesystem metadata, block based | File tree traversal and metadata comparison |
| Speed with many small files | Independent of file count | Drops noticeably as file count grows |
| Consistency during transfer | Guaranteed by frozen read only snapshot | Depends on concurrent write activity |
| Prerequisite | Btrfs required on source and target | Works on any filesystem |
| Incremental transfer | Native via parent snapshot reference | Approximated via modification time and file size |
Mironsoft
Server administration, Docker hosts, and performance tuning
Linux servers nobody on the team really understands anymore?
We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.
Server Audit
Review the existing server configuration for security gaps and performance bottlenecks.
Docker Host Setup
Set up and secure production-ready Docker environments for Magento cleanly.
Monitoring & Tuning
Measure resource usage and tune systemd, kernel, and services with purpose.
10. Summary
Btrfs Send/Receive
Core principle
Differences between two snapshots are determined directly at the block level
Core command
btrfs send -p previous-snapshot new-snapshot | btrfs receive target
Biggest advantage
Speed independent of the number of files in the directory
Most important rule
Parent snapshot must not be deleted on either side before the next transfer