Btrfs deep dive: subvolumes, snapshots, RAID & compression
Btrfs is a B-tree based copy-on-write (CoW) filesystem that has been in the Linux kernel since 2009. It is the default filesystem on Fedora (since 33) and SUSE Linux Enterprise (since 12). Unlike traditional journaling filesystems, Btrfs never overwrites data in place, every modification writes to a new location and atomically updates metadata pointers.
Architecture
Btrfs is built on five main B-trees:
| Tree | Purpose |
|---|---|
| Chunk tree | Maps logical addresses to physical block locations across devices. Stores device information (DEV_ITEM) and chunk mappings (CHUNK_ITEM). |
| Tree of roots | Points to all other trees, including subvolume trees. Tracks the default subvolume and subvolume deletion progress. |
| Extent tree | Records which byte ranges are in use, reference counts, and back-references to the file or tree using each extent. |
| Checksum tree | Stores detached checksums for each data block. Metadata blocks have inline checksums in the B-tree node header. |
| Device allocation tree | Records which physical extents on each device have been allocated into chunks. |
Why CoW matters
- Crash consistency: either the old data or the new data is present after a crash, never a partially updated block
- Snapshots: unchanged blocks are shared between source and snapshot, making snapshots instant and space-efficient
- Checksums: every block is checksummed; corruption is detected on read and self-healed when redundancy exists
Checksum algorithms
| Algorithm | Since kernel | Notes |
|---|---|---|
crc32c | Always | Default, backward compatible |
xxhash64 | 5.5 | Faster than crc32c |
sha256 | 5.5 | Stronger, slower |
blake2b | 5.5 | Strong and fast |
Subvolumes
A subvolume is an independently-rooted namespace within a Btrfs filesystem. It looks like a directory but has its own inode tree and can be snapshotted, mounted independently, and assigned quota limits.
Subvolumes often replace traditional partitions on Btrfs systems; see the partition requirements guide for sizing considerations. Container runtimes can also use Btrfs subvolumes as a storage driver; the OverlayFS and container storage guide compares Btrfs against the default overlay2 driver.
# Create a subvolume
btrfs subvolume create /mnt/data
# List subvolumes
btrfs subvolume list /mnt
# Show subvolume info
btrfs subvolume show /mnt/data
# Delete a subvolume
btrfs subvolume delete /mnt/dataMounting subvolumes
# By name
mount -o subvol=@ /dev/sdb1 /
mount -o subvol=@home /dev/sdb1 /home
# By ID
mount -o subvolid=256 /dev/sdb1 /mnt
# Get the subvolume ID
btrfs subvolume list /mnt
btrfs inspect-internal rootid /mnt/dataThe @ naming convention
The @ prefix is a convention (not a kernel requirement) used by Fedora, openSUSE, and Ubuntu to mark subvolumes in directory listings. A typical layout:
@ → mounted at /
@home → mounted at /home
@var → mounted at /var (excluded from root snapshots)Example /etc/fstab entries:
UUID=xxxx / btrfs subvol=@,defaults 0 0
UUID=xxxx /home btrfs subvol=@home,defaults 0 0Default subvolume
# Get current default
btrfs subvolume get-default /mnt
# Set default (by path or ID)
btrfs subvolume set-default /mnt/@ /mntThe top-level subvolume always has ID 5 and cannot be removed.
Snapshots
A snapshot is a subvolume whose initial content is shared with another subvolume. When either the snapshot or the source is modified, CoW ensures the changes are private.
# Read-write snapshot
btrfs subvolume snapshot /mnt/data /mnt/data_snap
# Read-only snapshot (for send/receive backups)
btrfs subvolume snapshot -r /mnt/data /mnt/data_snapA snapshot of a subvolume containing nested subvolumes will contain empty stubs for the nested subvolumes, not their contents. You must snapshot each nested subvolume separately.
If the underlying disk is damaged, both the snapshot and the source are damaged. Use btrfs send to replicate snapshots to separate storage for a real backup.
Send / receive
# Full send to a backup filesystem
btrfs send /.snapshots/home-day1 | btrfs receive /backup
# Incremental send (parent must exist on receiver)
btrfs send -p /.snapshots/home-day1 /.snapshots/home-day2 \
| btrfs receive /backup
# Stream over SSH to a remote host
btrfs send /.snapshots/home-day1 \
| ssh backuphost "btrfs receive /backup"All snapshots involved in a send must be read-only. Each command in the stream is CRC32C-checksummed.
For backup strategies built on Btrfs send/receive, see the backup and disaster recovery guide.
Compression
| Algorithm | Since kernel | Levels | Notes |
|---|---|---|---|
zlib | Always | 1-9 (default 3) | Balanced speed and ratio |
lzo | 2.6.38 | None | Fast, lower ratio |
zstd | 4.14 | 1-15 (default 3) | Best modern choice, high ratio, fast |
# Mount with compression
mount -o compress=zstd /dev/sdb1 /mnt
mount -o compress=zstd:3 /dev/sdb1 /mnt
# Force compression (bypass heuristics)
mount -o compress-force=zstd /dev/sdb1 /mnt
# Per-file compression attribute
chattr +c file # legacy, sets zlib
btrfs property set file compression zstd
# Compress existing files via defrag
btrfs filesystem defrag -czstd /mnt/largefile
btrfs filesystem defrag -r -czstd /mnt/dataBtrfs uses heuristics (frequency sampling, repeated pattern detection, Shannon entropy) to skip incompressible files. compress-force bypasses this at the cost of wasted CPU on data that won't compress.
Defragmenting a file breaks its shared extents with snapshots and reflink copies, causing space usage to increase. Avoid defrag on subvolumes with snapshots.
CoW and NOCOW
For workloads with heavy random writes (databases, VM disk images), CoW causes severe fragmentation. Disable CoW on a per-file or per-directory basis:
# Set NOCOW on a directory BEFORE creating files in it
chattr +C /mnt/VMs
# Files created in a NOCOW directory inherit the attributechattr +C disables checksums (nodatasum) and compression. Btrfs cannot detect silent corruption on NOCOW files, and on mirrored profiles it cannot determine which copy is good. Put databases and VM images in a separate NOCOW subvolume that is excluded from snapshots.
Checksums and self-healing (scrub)
# Start scrub (runs in background)
btrfs scrub start /mnt
# Run in foreground
btrfs scrub start -B /mnt
# Check scrub status
btrfs scrub status /mnt
# Cancel
btrfs scrub cancel /mnt
# Resume
btrfs scrub resume /mntScrub walks all data and metadata, verifies checksums, and automatically repairs corrupted blocks by copying from a good replica (requires RAID1, RAID1C3, RAID1C4, or DUP profiles). Check per-device error counters:
btrfs device stats /mnt
btrfs device stats -z /mnt # reset countersWhen scrub finds corruption that cannot be self-healed, see the filesystem repair guide for manual recovery with btrfs check.
RAID profiles
| Profile | Copies | Space efficiency | Min devices | Protects against |
|---|---|---|---|---|
single | 1 | 100% | 1 | Nothing |
DUP | 2 (same device) | 50% | 1 | Bit errors (not disk failure) |
RAID1 | 2 | 50% | 2 | 1 disk failure |
RAID1C3 | 3 | 33% | 3 | 2 disk failures |
RAID1C4 | 4 | 25% | 4 | 3 disk failures |
RAID0 | 1 | 100% | 2 | Nothing |
RAID10 | 2 | 50% | 4 | 1 disk per mirror pair |
RAID5 | 1+parity | (N−1)/N | 2 | 1 disk, not production-ready, see below |
RAID6 | 1+2parity | (N−2)/N | 3 | 2 disks, not production-ready, see below |
Setting profiles
# At mkfs time
mkfs.btrfs -d raid1 -m raid1 /dev/sdb /dev/sdc
# Change profiles via balance
btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt
# Convert to single (for device removal)
btrfs balance start -f -dconvert=single -mconvert=dup /mntRAID 5/6 status: not recommended for production
Btrfs RAID5/6 has historically suffered from the write hole: parity and data writes are not atomic, and Btrfs has no battery-backed cache or CoW transaction safety for parity stripes. Kernel 6.2 introduced read-modify-write (RMW) checksum verification that mitigates the write hole by verifying all data checksums before overwriting parity during a RMW cycle, but the implementation is still not considered production-ready and the stripe-tree rewrite remains incomplete (RFC stage as of 2026). Use RAID1C3 or RAID1C4 for higher redundancy, or use ZFS RAIDZ if parity RAID is required.
Balance
Balance relocates block groups to match profile constraints, reclaim space, or compact the filesystem. Always use filters, a full balance on a large filesystem can take days.
# Convert data to RAID1
btrfs balance start -dconvert=raid1 /mnt
# Only balance chunks that are >70% used (reclaims space)
btrfs balance start -dusage=70 -musage=70 /mnt
# Pause / resume / cancel
btrfs balance pause /mnt
btrfs balance resume /mnt
btrfs balance cancel /mnt
# Check status
btrfs balance status /mntDevice management
# Add a device
btrfs device add /dev/sdc1 /mnt
# Remove a device (data is relocated off it first)
btrfs device remove /dev/sdb1 /mnt
# Remove a missing/failed device
btrfs device remove missing /mnt
# Replace a failing drive online
btrfs replace start /dev/sdb1 /dev/sdc1 /mnt
btrfs replace status /mnt
btrfs replace cancel /mnt
# Show filesystem and device info
btrfs filesystem show /mnt
btrfs filesystem usage /mnt
btrfs filesystem df /mntQuota groups (qgroups)
# Enable quotas
btrfs quota enable /mnt
# Set a limit on a subvolume (by qgroup 0/<subvolid>)
btrfs qgroup limit 100G 0/256 /mnt
# Show qgroup usage
btrfs qgroup show /mnt
# Disable quotas
btrfs quota disable /mntQgroups track rfer (referenced, total space including shared) and excl (exclusive, space that would be freed if the qgroup were deleted). Qgroups have a performance cost on all extent processing; enable only if you need them.
Btrfs swapfiles
Swapfiles on Btrfs require NOCOW (which implies no checksums, no compression, no snapshots of the containing subvolume) and must be fully preallocated with no holes.
# Modern method (btrfs-progs 6.1+)
btrfs filesystem mkswapfile --size 4G /swap/swapfile
swapon /swap/swapfile
# Get resume offset for hibernation
btrfs inspect-internal map-swapfile /swap/swapfileDo not use fallocate, it can create holes incompatible with swap. The btrfs filesystem mkswapfile command handles NOCOW, preallocation, and mkswap correctly in one step.
For broader swap configuration including zram and swap partitions, see the swap space guide.
Key mount options
| Option | Effect |
|---|---|
compress=zstd | Enable zstd compression |
compress-force=zstd | Force compression on all files |
noatime | Don't update access times (performance) |
space_cache=v2 | Use free space tree (available since 4.5, default since btrfs-progs 5.15) |
discard=async | Asynchronous TRIM (default since 6.2 when supported) |
subvol=@ | Mount a specific subvolume |
skip_balance | Don't auto-resume an interrupted balance on mount |
autodefrag | Auto-defrag files with random writes |
Common pitfalls
ENOSPC despite free space
Btrfs allocates space in chunks. If all data chunks are full but metadata chunks have space (or vice versa), writes fail with ENOSPC even though df shows free space. Run btrfs balance start -dusage=70 to reclaim, or add a device.
Balance hanging
A full balance without filters on a large or nearly-full filesystem can hang for days or hit ENOSPC mid-operation. Always use -dusage / -musage filters, run during maintenance windows, and mount with skip_balance to prevent auto-resume of an interrupted balance.
Snapshot accumulation
Many snapshots accumulate metadata overhead even with shared data extents. Implement cleanup policies (snapper, timeshift) and monitor with btrfs qgroup show.
btrfs-progs
The userspace toolset (btrfs-progs) provides the btrfs command. Current stable as of 2026 is v7.1. Check your version:
btrfs version
# btrfs-progs v7.1Sources & references
- Btrfs Documentation — canonical Btrfs documentation site maintained by the Btrfs project
- Btrfs Subvolumes — official documentation covering subvolume architecture and snapshot behavior
- Btrfs Send/Receive — documentation of btrfs send/receive for incremental backup and replication streams
- Btrfs Compression — official documentation of zlib, lzo, and zstd compression algorithms and mount options
- btrfs-subvolume(8) — Linux manual page — canonical man page for btrfs subvolume and snapshot commands
- btrfs-send(8) — Linux manual page — man page for btrfs send covering full and incremental send modes
Frequently asked questions
What is a Btrfs subvolume?
A subvolume is an independently-rooted namespace within a Btrfs filesystem. It looks like a directory but has its own inode tree and can be snapshotted, mounted independently, and assigned quota limits.
Are Btrfs snapshots recursive?
No. A snapshot of a subvolume containing nested subvolumes contains empty stubs for the nested ones, not their contents. You must snapshot each nested subvolume separately.
Is a Btrfs snapshot a backup?
No. If the underlying disk is damaged, both the snapshot and the source are damaged. Use `btrfs send` to replicate snapshots to separate storage for a real backup.
Should I use Btrfs RAID5 or RAID6?
No. Btrfs RAID5/6 is not production-ready due to the write hole and an incomplete stripe-tree rewrite. Use `RAID1C3` or `RAID1C4` for higher redundancy, or ZFS RAIDZ if parity RAID is required.
How does Btrfs compression work?
Btrfs supports zlib, lzo, and zstd (the best modern choice). Heuristics skip incompressible files automatically; `compress-force` bypasses them. Note that defragmenting a file breaks its shared extents with snapshots.
What is NOCOW and when should I use it?
NOCOW (`chattr +C`) disables copy-on-write for databases and VM disk images that suffer fragmentation from random writes. It also disables checksums and compression, so put NOCOW files in a separate subvolume excluded from snapshots.
Why does Btrfs report ENOSPC when df shows free space?
Btrfs allocates space in separate data and metadata chunks. If all data chunks are full but metadata has space (or vice versa), writes fail with ENOSPC. Run `btrfs balance start -dusage=70` to reclaim, or add a device.