Skip to content
Menu

Linux Storage9 min read

btrfs

Linux-native copy-on-write filesystem

What btrfs Is

btrfs (B-Tree FS, pronounced butter FS or better FS) started at Oracle in 2007 and has been in the mainline Linux kernel since 2.6.29 — no licensing friction, no out-of-tree module, no DKMS. It is a modern copy-on-write filesystem whose goals closely parallel ZFS: integrate filesystem + volume management + snapshots + checksums into one coherent system.

Core ideas shared with ZFS

  • Copy-on-write: no in-place overwrite of live data
  • Checksums on every block (CRC32C default; xxhash / SHA-256 / BLAKE2 since 5.5)
  • Snapshots as CoW pointer operations — instant, cheap
  • Efficient send / receive replication
  • Multi-device pools with built-in RAID profiles
  • Transparent compression (zlib, lzo, zstd)
  • Subvolumes — named trees within one filesystem

What btrfs does differently

  • In-tree. Ships with every modern Linux kernel.
  • Online flexibility. Add / remove / replace devices and convert RAID profile without unmounting — a huge operational win versus mdadm and versus ZFS.
  • Subvolumes instead of datasets. Similar semantics, different internal model.
  • Single filesystem per pool. Unlike ZFS which has a pool plus independent datasets, a btrfs “filesystem” is one object; subvolumes are trees within it.
  • Caveat: parity RAID (5/6) is still not safe as of 2024 — more on this below.

Subvolumes

A subvolume is a named, separately-mountable tree within a btrfs filesystem. Think of it as a directory with its own identity: it can be snapshotted independently, mounted with different options, given its own quotas, and replicated on its own.

Crucially, snapshots are subvolumes. A snapshot is just a CoW copy of a subvolume's tree root — the two are indistinguishable to the kernel apart from their origin.

Typical desktop layout

text
bash

/  # mounted with subvol=@
├── home        # mounted with subvol=@home
├── var
│   ├── log     # mounted with subvol=@var_log
│   └── cache   # mounted with subvol=@var_cache
└── .snapshots  # mounted with subvol=@snapshots
  

Each @xxx is a separate subvolume inside the single btrfs filesystem on the root device. Snapshotting @ before a package transaction captures the root FS without touching @home. Rollback reverts only the root.

console
bash
root@host:~# btrfs subvolume create /mnt/@data
Create subvolume '/mnt/@data'

root@host:~# btrfs subvolume list /mnt
ID 256 gen 14 top level 5 path @
ID 257 gen 22 top level 5 path @home
ID 258 gen 22 top level 5 path @var_log
ID 260 gen 30 top level 5 path @data

root@host:~# btrfs subvolume delete /mnt/@data
Delete subvolume (no-commit): '/mnt/@data'

Snapshots

A snapshot is a subvolume created from another subvolume, sharing all blocks CoW. Writable by default; pass -r for read-only (required for btrfs send). Nesting is legal — snapshot of a snapshot is fine.

console
bash
root@host:~# btrfs subvolume snapshot /mnt/@ /mnt/@snapshots/pre-upgrade
Create a snapshot of '/mnt/@' in '/mnt/@snapshots/pre-upgrade'

root@host:~# btrfs subvolume snapshot -r /mnt/@home /mnt/@snapshots/home-ro-2026-04-20
Create a readonly snapshot of '/mnt/@home' in '/mnt/@snapshots/home-ro-2026-04-20'

Reflinks — instant file copies

cp --reflink=always SRC DST inside a btrfs filesystem creates a CoW-shared copy — instant, regardless of file size, taking no additional space until one side is modified. Indispensable for VM image cloning, large media, backup rotations:

console
console
root@host:~# cp --reflink=always ubuntu-base.qcow2 vm-042.qcow2
# returns instantly even for a 40 GB file

Built-in RAID Profiles

btrfs sets data and metadata profiles independently via -d and -m. You can, for example, have data=single + metadata=raid1 — duplicated metadata trees but single-copy data. Profiles can be changed online via btrfs balance.

ProfileBehaviourMin devicesNotes
singleOne copy, no redundancy1Pure capacity.
dupTwo copies on the same device1Metadata default on single-disk FSes — survives sector loss.
raid0Stripe, no redundancy2Capacity × N, speed, risk.
raid1Two copies on different devices2Block-level mirroring — not whole-device like mdadm.
raid10Stripe of mirror pairs4Capacity / 2, good performance.
raid1c3Three copies3Survives 2 device losses. Good for metadata on large arrays.
raid1c4Four copies4Survives 3 device losses.
raid5Parity stripe, 1 disk tolerance3Write hole unresolved — avoid.
raid6Double parity, 2 disk tolerance4Write hole unresolved — avoid.

Warning

Do not use btrfs raid5/raid6 for anything you value. The parity write hole — the window where a crash during a partial stripe write leaves parity inconsistent with data — has been known since 2016 and remains unresolved in mainline as of 2024. Especially dangerous on metadata: a metadata write hole can corrupt the filesystem tree irrecoverably.

Safe patterns:
  • Use raid1 / raid10 for data and metadata; or
  • Run btrfs on top of a mdadm RAID 5/6 array and use single/dup profiles — you lose btrfs's self-healing but get working parity RAID.

Online Multi-Device Operations

Unlike ZFS, btrfs allows essentially every device-level operation to happen online, on a mounted filesystem. This is arguably btrfs's biggest operational advantage.

btrfs device add /dev/sdX /mntAdd a device to the FS. New blocks can be allocated on it immediately.
btrfs device remove /dev/sdX /mntEvacuate data off the device, then remove it. Rebalances automatically.
btrfs device replace start /dev/OLD /dev/NEW /mntLive replacement — reads from the old device (or reconstructs from redundancy if it's dead) and writes to the new one, without dropping below the profile's redundancy.
btrfs balance start /mntRewrite all chunks. Used after adding/removing devices to redistribute data, or to reclaim metadata space.
btrfs balance start -dconvert=raid10 -mconvert=raid10 /mntConvert data and metadata profiles online.
console
bash
root@host:~# btrfs device add /dev/sdd /mnt
root@host:~# btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt
Done, had to relocate 412 out of 412 chunks

Scrub & Self-Healing

Like ZFS, btrfs has a built-in scrub that reads every block, verifies the checksum, and repairs corruption from redundant copies. Self-healing requires a redundant profile (raid1/raid10/raid1c3/raid1c4 or dup for metadata).

console
bash
root@host:~# btrfs scrub start /mnt
scrub started on /mnt, fsid 6a2f... (pid=12345)

root@host:~# btrfs scrub status /mnt
UUID:             6a2f...
Scrub started:    Sun Apr 20 09:00:00 2026
Status:           running
Duration:         0:12:33
ETA:              2:41:40
Total to scrub:   4.2TiB
Rate:             540.2MiB/s
Error summary:    no errors found

Tip

Schedule btrfs scrub weekly on SSDs, monthly on spinning rust. Without scrub, silent bit rot on a block only referenced by cold files is caught only when you try to read it — by which time the other copy may already have rotted too.

send / receive

btrfs's replication model mirrors ZFS's closely: send a stream derived from the block-pointer delta of a read-only snapshot, receive it into another btrfs filesystem.

console
console
# Initial full send
root@host:~# btrfs subvolume snapshot -r /mnt/@home /mnt/@snapshots/home-2026-04-19
root@host:~# btrfs send /mnt/@snapshots/home-2026-04-19 | btrfs receive /backup/

# Incremental — only the delta since home-2026-04-19
root@host:~# btrfs subvolume snapshot -r /mnt/@home /mnt/@snapshots/home-2026-04-20
root@host:~# btrfs send -p /mnt/@snapshots/home-2026-04-19 \
                  /mnt/@snapshots/home-2026-04-20 | btrfs receive /backup/

Read-only snapshots are required on the source — the stream references a concrete frozen tree. The receiving side reconstructs subvolumes with matching content.

Checksum Algorithms

AlgorithmWidthWhen
CRC32C32 bitDefault. Hardware-accelerated on all modern CPUs. Fast, decent collision resistance for non-adversarial bit-rot detection.
xxhash64 bitFaster than CRC32C on some CPUs, wider → far fewer collisions.
SHA-256256 bitCryptographic. Slow without dedicated instructions. Overkill for most use cases.
BLAKE2b256 bitCryptographic, faster than SHA-256 in software.

Set at filesystem creation only: mkfs.btrfs --csum xxhash /dev/sdX. Not changeable after the fact.

Inline Data & Compression

Inline data

Files smaller than roughly 4 KB can be stored directly inside the metadata b-tree (inline extent), avoiding a separate data block allocation. Excellent for directories full of small config files, dotfiles, mail spools.

Transparent compression

btrfs compresses extents on write. Three algorithms: zlib (older, good ratio, slow), lzo (fast, modest ratio), zstd (modern sweet spot, tunable levels 1–15). Incompressible extents are detected and stored raw.

console
console
# Filesystem-wide via mount option
root@host:~# mount -o compress=zstd:3 /dev/sdX /mnt

# Or per-file
root@host:~# btrfs property set /mnt/logs compression zstd

Tool Surface

CommandPurpose
btrfs subvolume {create,list,delete,snapshot}Subvolume lifecycle
btrfs filesystem showDevices and FSIDs
btrfs filesystem df /mntAllocation by block group type (data / metadata / system)
btrfs filesystem usage /mntThe one you actually need. Reconciles raw vs usable space honestly.
btrfs filesystem resize <SIZE|max> /mntOnline grow/shrink
btrfs device {add,remove,replace,stats}Device lifecycle and IO/checksum error counters
btrfs balance {start,pause,cancel,status}Rewrite chunks — rebalance after add/remove, convert profile, reclaim space
btrfs scrub {start,cancel,status}Checksum-verify the whole FS
btrfs send / btrfs receiveReplication
btrfs checkOffline fsck. Modern kernels rarely need it — it's also historically been risky. Read the wiki before running with --repair.

Operational Quirks

df is misleading

Because btrfs separates chunk allocation from block usage, and because CoW makes “free space” a fuzzy concept when snapshots exist, df numbers on btrfs are essentially advisory. Always cross- check with btrfs filesystem usage.

console
bash
root@host:~# df -h /mnt
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb        3.6T  1.9T  1.7T  53% /mnt

root@host:~# btrfs filesystem usage /mnt
Overall:
    Device size:                   3.64TiB
    Device allocated:              2.10TiB
    Device unallocated:            1.54TiB
    Used:                          1.87TiB
    Free (estimated):              1.65TiB      (min: 878.50GiB)
    Data ratio:                        1.00
    Metadata ratio:                    2.00
    Global reserve:              512.00MiB      (used: 0.00B)

ENOSPC when df shows free

btrfs allocates space in chunks (typically 1 GiB for data, 256 MiB for metadata). You can run out of metadata chunks while plenty of raw data space remains → the kernel returns ENOSPC even though df says you have free space.

Fix: btrfs balance start -dusage=50 /mnt — rewrites half-empty data chunks, returns their underlying space to the unallocated pool so metadata can grow.

Warning

On small or heavily-churned filesystems, schedule periodic balances with usage filters (e.g. -dusage=50 -musage=50). Full balances on a large FS can take days.

Snapper & Timeshift

Snapper (openSUSE, SUSE, Ubuntu)

Snapper is the killer feature of btrfs-on-Linux for workstations and servers. It hooks into package management to take pre and post snapshots of @ around every transaction, plus timeline snapshots on a cron schedule.

openSUSE rollback model

  1. zypper update starts → Snapper takes a pre snapshot of @
  2. Update runs
  3. Snapper takes a post snapshot
  4. If the system boots and everything works — done.
  5. If the system breaks: reboot, pick the pre snapshot from GRUB, boot into it, run snapper rollback@ is reset to that snapshot.

Timeshift (Linux Mint, Ubuntu)

GUI-driven snapshot manager. Supports both rsync (to any backup target) and btrfs (using subvolume snapshots) backends. Its btrfs mode creates named snapshots of @ and @home on a schedule. Less tightly integrated with packaging than Snapper but easier for users unfamiliar with the command line.

btrfs vs ZFS — Honest Comparison

btrfs wins
  • In-tree — no DKMS, no licensing discussion
  • Online flexibility — add / remove / replace devices, convert RAID profile without unmounting
  • Simpler single-disk story — default on Fedora Workstation, openSUSE, Mint; root-on-btrfs is boring
  • Snapper integration — rollback-a-bad-update is a killer feature unmatched on Linux
  • Reflinkscp --reflink is one command away
ZFS wins
  • Mature at scale — 20+ years in Solaris, large production deployments
  • Working parity RAID — raidz1/2/3 have no write hole
  • Richer cache tiering — ARC, L2ARC, SLOG, special vdevs
  • Per-dataset encryption with raw send/receive to untrusted destinations
  • Generally faster under heavy concurrent load
  • Proxmox first-class support — native replication, HA integration

Verdict

  • Workstation / laptop: btrfs. Snapper rollback is worth its weight; kernel-upgrade friction is zero because it's in-tree.
  • Storage server / NAS / backup target: ZFS. The cache tiering, mature parity RAID, and send/receive performance matter more than the online device gymnastics.
  • Proxmox hosts: ZFS. It's first-class; btrfs support exists but is less polished.
  • Desktops with large RAID arrays needing parity: either btrfs on top of mdadm, or ZFS with raidz. Not btrfs raid5/6.

Tip

Both work, neither is obviously wrong. Pick based on the workload and the team's familiarity. A badly-operated ZFS pool will lose data just as effectively as a badly-operated btrfs filesystem. The integrity story of both is light-years ahead of ext4 / XFS on any underlying RAID.

Solidnines — solidnines.com