Safely Receive a Btrfs Snapshot from a Send Stream
You will receive a Btrfs send stream into an existing filesystem and keep the destination protected while it runs. Then you verify the result properly rather than trusting a clean exit code. The examples use btrfs-progs 6.6.3, installed here as package version 6.6.3-1.1build2.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow 15 to 30 minutes, excluding the time needed to copy the stream. You need a readable stream produced by btrfs send, a mounted destination Btrfs filesystem, and enough free space. Receiving changes the destination filesystem, so take a checkpoint before starting. The normal receive command needs elevated privileges on many systems.
1. Check the installed command
Confirm the binary and its version before relying on option details:
$ command -v btrfs
/usr/bin/btrfs
$ btrfs --version
btrfs-progs v6.6.3
$ btrfs receive --help
usage: btrfs receive [options] <mount>
The subcommand takes a destination path for normal receiving. It reads the stream from standard input unless you give -f FILE. Do not confuse the destination path with an ordinary directory on another filesystem: it must be in the Btrfs filesystem that will contain the received subvolume.
Checkpoint
Write down the exact stream file and destination path you intend to use. If either is a placeholder, stop here and replace it before continuing.
2. Inspect the stream without changing the filesystem
Use --dump when you need to validate a stream and see its metadata without providing a destination:
$ btrfs receive --dump -f /path/to/snapshot.stream
subvol path=project-2026-09-22
uuid uuid=REPORTED-UUID
transid transid=REPORTED-TRANSACTION
... ...
The exact operations, UUID and transaction values depend on the stream. The useful result is one metadata operation per line. In this mode the filesystem remains unchanged, and the path argument is not required. A non-zero exit status means the validation failed. Keep the stream if you need to investigate the error; do not edit it in place.
There is a distraction trap here: --dump is a receiver-side stream inspection mode, not a dry run for a normal destination. It validates and prints metadata, but it does not prove that a particular destination has the required mount layout or free space.
3. Confirm the destination mount
Inspect the destination before granting the receive process access to it:
$ findmnt --target /srv/btrfs-receive
TARGET SOURCE FSTYPE OPTIONS
/srv/btrfs-receive /dev/... btrfs ...
$ btrfs filesystem usage /srv/btrfs-receive
Overall:
Device size: ...
Device allocated: ...
Device unallocated: ...
Use a real path in place of /srv/btrfs-receive. The mount must expose the top level of the destination filesystem, not merely a nested subvolume. The receive documentation treats a changed default subvolume or a mount that is not at the filesystem top level as a failure condition.
If /proc is not available, such as inside a chroot, pass the root mount point with -m /path/to/root-mount. This option tells receive where the destination filesystem is mounted; it does not mount the filesystem for you. Mounting or changing storage layout is an administrative action outside this workflow.
4. Protect the receiving path
Before receiving, stop jobs and users that can write below the destination path. A successful receive makes the new subvolume read-only only after the operation finishes. During the copy, a user with write access can add, remove or modify files, leaving a result that is not an exact copy of the sent snapshot.
This is a security and integrity boundary, not a cosmetic precaution. Use the maintenance controls appropriate to your host, such as stopping a writer service or restricting access at the mount and directory level. Record what you changed so it can be restored after verification. Do not make a live production path writable to everyone as a quick workaround.
Checkpoint
Verify that the stream has passed inspection, the destination is the intended Btrfs top level, and writers are excluded. If you cannot answer all three, do not start receive.
5. Receive the stream
For a stream file, run the ordinary receive as the account that has the required Btrfs access:
$ sudo btrfs receive -f /path/to/snapshot.stream /srv/btrfs-receive
At subvol project-2026-09-22
Receiving snapshot project-2026-09-22
Alternatively, use a pipe when the sender and receiver are connected directly:
$ sudo btrfs send /path/to/read-only-snapshot | sudo btrfs receive /srv/btrfs-receive
Only use the pipeline when the source snapshot is read-only and the sender and receiver have been checked independently. Keep the sender's output and receiver's errors visible while testing. A zero exit status means receive succeeded; any other status means it failed.
-e makes the receiver terminate after an end marker in the stream. Without it, the receiver waits for end-of-file or an error. Use it only when the stream format and transport are under your control. -E N changes the error limit from its default of one; -E 0 means no limit. Raising that limit can allow more stream errors before termination, so it is not a general repair option.
6. Verify the received subvolume
Check the command status immediately, then inspect the destination subvolume:
$ printf 'receive exit status: %s\n' "$?"
receive exit status: 0
$ sudo btrfs subvolume list /srv/btrfs-receive
ID ... gen ... top level ... path project-2026-09-22
$ sudo btrfs property get -ts /srv/btrfs-receive/project-2026-09-22 ro
ro=true
The subvolume name is taken from the stream, so replace project-2026-09-22 with the path shown by the list command. The read-only property is the important completion check. Preserve the original stream and source snapshot until you have checked the files and any application-specific backup test.
There is no normal undo operation that returns the received data to a previous state. If the receive created an unwanted subvolume, identify it exactly with btrfs subvolume list and remove it only after confirming that it contains no needed data. Subvolume deletion is destructive and should be a separately reviewed maintenance action, not an automatic error handler.
7. Diagnose the common failures
- Already exists: the receiving subvolume name is present. Choose a clean destination or remove the existing subvolume only after a deliberate data review.
- Changed received parent: a previously received subvolume was modified after receipt. Restore its expected read-only, unchanged state or create a new receive target; do not force an incremental stream past this check.
- Wrong mount level: remount or select the filesystem top level. If
/procis unavailable, check whether-msupplies the real root mount. - Unexpected stream failure: rerun
--dumpagainst the saved stream and check the sender and receiver versions, paths and available space. - Untrusted stream: stop. Btrfs receive is not a safe parser for arbitrary input. A specially crafted stream can create reflinks to arbitrary files in the same filesystem. Receive only trusted streams, and protect trusted streams when transporting them across an untrusted network.
Do not treat a verbose log as proof of data integrity. --verbose shows more performed operations, while --quiet suppresses normal messages and leaves errors. Use the mode that helps you observe the operation, then verify the subvolume and its contents separately.
Done means
- Version and stream checked. The installed
btrfs-progsversion and stream format were confirmed. - Stream inspected safely.
--dumpvalidated the saved stream without changing the destination filesystem. - Destination confirmed. It is the intended Btrfs top-level mount with enough space.
- Writers excluded. Nothing could write below the destination until receive completed and the subvolume became read-only.
- Exit status checked. Receive returned zero, and
btrfs subvolume listfound the expected path. - Read-only confirmed. The received subvolume reports
ro=true, and the original stream remains available for recovery or audit.