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/receivereplication - 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
/ # 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.
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.
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:
root@host:~# cp --reflink=always ubuntu-base.qcow2 vm-042.qcow2
# returns instantly even for a 40 GB fileBuilt-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.
| Profile | Behaviour | Min devices | Notes |
|---|---|---|---|
single | One copy, no redundancy | 1 | Pure capacity. |
dup | Two copies on the same device | 1 | Metadata default on single-disk FSes — survives sector loss. |
raid0 | Stripe, no redundancy | 2 | Capacity × N, speed, risk. |
raid1 | Two copies on different devices | 2 | Block-level mirroring — not whole-device like mdadm. |
raid10 | Stripe of mirror pairs | 4 | Capacity / 2, good performance. |
raid1c3 | Three copies | 3 | Survives 2 device losses. Good for metadata on large arrays. |
raid1c4 | Four copies | 4 | Survives 3 device losses. |
raid5 | Parity stripe, 1 disk tolerance | 3 | Write hole unresolved — avoid. |
raid6 | Double parity, 2 disk tolerance | 4 | Write hole unresolved — avoid. |
Warning
Safe patterns:
- Use
raid1/raid10for data and metadata; or - Run btrfs on top of a
mdadmRAID 5/6 array and usesingle/dupprofiles — 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 /mnt | Add a device to the FS. New blocks can be allocated on it immediately. |
btrfs device remove /dev/sdX /mnt | Evacuate data off the device, then remove it. Rebalances automatically. |
btrfs device replace start /dev/OLD /dev/NEW /mnt | Live 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 /mnt | Rewrite all chunks. Used after adding/removing devices to redistribute data, or to reclaim metadata space. |
btrfs balance start -dconvert=raid10 -mconvert=raid10 /mnt | Convert data and metadata profiles online. |
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 chunksScrub & 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).
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 foundTip
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.
# 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
| Algorithm | Width | When |
|---|---|---|
| CRC32C | 32 bit | Default. Hardware-accelerated on all modern CPUs. Fast, decent collision resistance for non-adversarial bit-rot detection. |
| xxhash | 64 bit | Faster than CRC32C on some CPUs, wider → far fewer collisions. |
| SHA-256 | 256 bit | Cryptographic. Slow without dedicated instructions. Overkill for most use cases. |
| BLAKE2b | 256 bit | Cryptographic, 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.
# 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 zstdTool Surface
| Command | Purpose |
|---|---|
btrfs subvolume {create,list,delete,snapshot} | Subvolume lifecycle |
btrfs filesystem show | Devices and FSIDs |
btrfs filesystem df /mnt | Allocation by block group type (data / metadata / system) |
btrfs filesystem usage /mnt | The one you actually need. Reconciles raw vs usable space honestly. |
btrfs filesystem resize <SIZE|max> /mnt | Online 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 receive | Replication |
btrfs check | Offline 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.
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
zypper updatestarts → Snapper takes a pre snapshot of@- Update runs
- Snapper takes a post snapshot
- If the system boots and everything works — done.
- 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
- 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
- Reflinks —
cp --reflinkis one command away
- 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.