Btrfs Send/Receive: Efficient Incremental Backups at the Block Level
AI generated
$
/etc
Linux
Btrfs Send/Receive
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.

10 min read Linux Btrfs Backup

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

11. FAQ: Btrfs Send/Receive

1Does the target filesystem also have to be Btrfs?
Yes, both source and target must be Btrfs filesystems, since the receiver turns the binary send stream directly into a Btrfs subvolume structure. A transfer onto ext4 or XFS is not possible with btrfs receive.
2What happens if the parent snapshot is missing on the target side?
The incremental transfer fails then, because Btrfs computes changes relative to that exact state. In that case, only a fresh full transfer without the -p option remains an option.
3Why must snapshots be read only for send?
Only read only snapshots guarantee a frozen, consistent state throughout the entire transfer. A writable snapshot could change during the send operation, which would result in an inconsistent data stream.
4How does send/receive compare to rsync with millions of small files?
Send/receive determines changes directly from the filesystem structure without walking the file tree and is therefore practically independent of file count. rsync, by contrast, has to check every file individually, which costs noticeable time with millions of small files.
5Can an aborted transfer be resumed?
With the -e option on btrfs receive, an interrupted transfer can be resumed under certain conditions, provided the partially received subvolume still exists. For permanently failed attempts, the incomplete subvolume should be deleted before retrying.
6Which tool helps automate send/receive?
btrbk is a widely used tool that orchestrates snapshot creation, transfer, and retention rules through a central configuration file. It also supports multiple targets and local plus remote replication at the same time.
7How secure is the transfer over a public network?
Over an SSH pipe, the data stream is automatically transferred encrypted, and SSH compression can additionally be enabled. A restricted SSH key with a command restriction limited to btrfs receive further increases security on the target side.
8Does send/receive detect data corruption during transfer?
Btrfs already validates checksums on every block read, so corruption introduced during a transfer is usually caught at the latest on the next access to the received subvolume. For critical backups, a regular scrub on the target side is worth adding as well.
9Does send/receive replace a full 3-2-1 backup strategy?
No, send/receive works excellently as one of the storage technologies within the 3-2-1 rule, but should not remain the only backup method due to the shared dependency on the Btrfs implementation.
10Can different Btrfs versions on source and target cause problems?
Yes, newer send stream formats are sometimes not understood by older receive implementations. It is advisable to run a comparably current Btrfs version on both sides to avoid compatibility issues.