STORAGE ENGINEERING

Btrfs deep dive: subvolumes, snapshots, RAID & compression

Category: Copy-on-Write FilesystemTechnologies: Btrfs, btrfs-progs, CoW, subvolumes, RAID1C3/C4, zstd

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 15). 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:

TreePurpose
Chunk treeMaps logical addresses to physical block locations across devices. Stores device information (DEV_ITEM) and chunk mappings (CHUNK_ITEM).
Tree of rootsPoints to all other trees, including subvolume trees. Tracks the default subvolume and subvolume deletion progress.
Extent treeRecords which byte ranges are in use, reference counts, and back-references to the file or tree using each extent.
Checksum treeStores detached checksums for each data block. Metadata blocks have inline checksums in the B-tree node header.
Device allocation treeRecords which physical extents on each device have been allocated into chunks.

Why CoW matters

Checksum algorithms

AlgorithmSince kernelNotes
crc32cAlwaysDefault, backward compatible
xxhash645.5Faster than crc32c
sha2565.5Stronger, slower
blake2b5.5Strong 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.

# 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/data

Mounting 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/data

The @ 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 0

Default subvolume

# Get current default
btrfs subvolume get-default /mnt

# Set default (by path or ID)
btrfs subvolume set-default /mnt/@ /mnt

The 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_snap
Snapshots are NOT recursive

A 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.

A snapshot is not a backup

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.

Compression

AlgorithmSince kernelLevelsNotes
zlibAlways1-9 (default 3)Balanced speed and ratio
lzo3.1NoneFast, lower ratio
zstd4.141-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/data

Btrfs 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.

Defrag breaks reflinks

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 attribute
NOCOW implications

chattr +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 /mnt

Scrub 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 counters

RAID profiles

ProfileCopiesSpace efficiencyMin devicesProtects against
single1100%1Nothing
DUP2 (same device)50%1Bit errors (not disk failure)
RAID1250%21 disk failure
RAID1C3333%32 disk failures
RAID1C4425%43 disk failures
RAID01100%2Nothing
RAID10250%41 disk per mirror pair
RAID51+parity(N−1)/N21 disk, not production-ready, see below
RAID61+2parity(N−2)/N32 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 /mnt

RAID 5/6 status: not recommended for production

Do not use Btrfs RAID5/6 for production data

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.5+ introduced comprehensive RAID56 read-modify-write checksum verification that significantly mitigates the write hole, but the implementation is still not considered production-ready and the stripe-tree rewrite remains incomplete. 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 /mnt

Device 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 /mnt

Quota 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 /mnt

Qgroups 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/swapfile

Do not use fallocate, it can create holes incompatible with swap. The btrfs filesystem mkswapfile command handles NOCOW, preallocation, and mkswap correctly in one step.

Key mount options

OptionEffect
compress=zstdEnable zstd compression
compress-force=zstdForce compression on all files
noatimeDon't update access times (performance)
space_cache=v2Use free space tree (default since 4.5)
discard=asyncAsynchronous TRIM (default since 6.2 when supported)
subvol=@Mount a specific subvolume
skip_balanceDon't auto-resume an interrupted balance on mount
autodefragAuto-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.1