Multi-boot partitioning / btrfs space accounting
The discrepancy
df reported the btrfs pool at 70% full. ncdu on the root showed 40% of that. The two numbers disagreed by hundreds of gigabytes. This is not a bug — it is a consequence of how btrfs accounts for space, and understanding it prevents wasted hours of investigation.
Why df shows more than ncdu
Three independent causes, each potentially significant.
1. Block group allocation overhead
btrfs allocates disk space in block groups — typically 1G chunks for data and 256M for metadata. A block group can be partially used. If you have many sparse block groups, btrfs has reserved the space on disk but not filled it. df counts allocated block groups as used. du counts file data only.
btrfs fi usage / shows the breakdown:
btrfs filesystem usage /
Overall:
Device size: 887.00GiB
Device allocated: 612.00GiB
Device unallocated: 275.00GiB
Device missing: 0.00B
Used: 480.50GiB
Free (estimated): 394.22GiB
Data ratio: 1.00
Metadata ratio: 1.00
Global reserve: 512.00MiB
Data,single: Size:604.00GiB, Used:478.00GiB
Metadata,single: Size:8.00GiB, Used:2.50GiB
System,single: Size:32.00MiB, Used:16.00KiB
“Device allocated” (612G) is what df uses for its Used figure. “Used” (480G) is what is actually occupied by file data and metadata. The gap is allocated-but-sparse block groups.
2. The Timeshift orphan
The largest single cause of the discrepancy here was a Timeshift snapshot sitting at the pool root, invisible to normal tools.
Timeshift creates btrfs snapshots at subvolid 5 (the btrfs top-level subvolume, the pool root). snapper does not know about Timeshift snapshots. ls / does not show them. ncdu running as a regular user sees only the mounted @void subvolume.
Finding orphaned subvolumes at the pool root requires mounting subvolid=5:
mount -o subvolid=5 /dev/nvme0n1p5 /mnt/pool-root
ls /mnt/pool-root
In this case, that showed a path like 1/snapshot — a 61GB Timeshift snapshot from a previous Arch installation that was never cleaned up. It was not visible to snapper, not visible to ncdu on /, and accounted for a substantial chunk of the discrepancy.
Delete orphaned snapshots by path:
btrfs subvolume delete /mnt/pool-root/1/snapshot
3. Docker’s /var/lib
ncdu without root privileges does not traverse directories it cannot read. Docker’s storage at /var/lib/docker is owned by root and not world-readable. Running ncdu as a regular user silently skips it.
The actual footprint here was 144GB in images and 64GB in build cache:
sudo du -sh /var/lib/docker/
# 208G /var/lib/docker/
docker system df
# Images: 144GB
# Build cache: 64GB
Running ncdu as root (sudo ncdu /) shows the real picture.
btrfs fi df vs btrfs fi usage vs df
These three commands answer different questions.
df -h / — reports from the perspective of the VFS layer: used/available as the kernel presents them. On btrfs, this reflects allocated block groups, not file data. Good for “will I get ENOSPC soon?” but misleading for “how much data do I have?”
df -h /
btrfs fi df / — shows block group allocation by type (Data, Metadata, System) and how much of each is used. Faster than btrfs fi usage but less informative.
btrfs filesystem df /
Data, single: total=604.00GiB, used=478.00GiB
Metadata, single: total=8.00GiB, used=2.50GiB
System, single: total=32.00MiB, used=16.00KiB
btrfs fi usage / — the most complete view. Shows device size, allocated, unallocated, and actual used space. Shows the gap between what is allocated and what is occupied. This is the correct command when diagnosing space issues.
Metadata can fill before data
btrfs metadata and data occupy separate block group pools. It is possible to have data block groups with space available while metadata block groups are 100% full. When metadata is full, the filesystem goes read-only.
The symptom is confusing: df shows available space, writes fail with ENOSPC, and the filesystem is suddenly read-only. Check metadata specifically:
btrfs filesystem df / | grep Metadata
If Metadata is at or near capacity, run a balance to redistribute. See [Multi-boot partitioning / btrfs metadata and balance].
Practical checklist for space investigation
- Run
btrfs fi usage /— get the real picture of allocated vs used - Mount subvolid=5 and
lsthe pool root — look for orphaned snapshots - Run
sudo ncdu /— get file-level breakdown including root-owned directories - Run
docker system dfif Docker is in use — images and build cache are often the largest hidden consumer - Check each btrfs group type separately if ENOSPC appears despite available data space
Connection closed. Returning to terminal...