Balance Btrfs

1. Check

Because of the way that Btrfs works, the command df may not always be accurate. For example it cannot tell how much unallocated disk space is available.

To see details on Btrfs disk usage, we need to use btrfs filesystem usage. This shows how each chunk type is allocated and how much unallocated space is available.

# overall check
btrfs filesystem show

# mount the disk used as a storage by incus
mkdir mnt
mount /dev/nvme1n1 mnt
ls mnt/

# check usage
btrfs filesystem df mnt/
btrfs filesystem usage mnt/
btrfs filesystem usage -T mnt/

# unmount
umount mnt
ls mnt/
rmdir mnt/

2. Balance

We can do it with btrfs balance start. However, running it without any filters, would re-write every data and metadata chunk in the filesystem. Usually, this is not what we want. Instead, we use balance filters to limit what chunks should be balanced.

With the option -dusage=5 we limit balance to compact data blocks that are less than 5% full. This is a good start, and we can increase it to 10-15% or more if needed. The goal here is to make sure there is enough unallocated space on each device in the filesystem to avoid the out-of-space error situations.

# mount the disk used as a storage by incus
mount /dev/nvme1n1 mnt

# balance
btrfs filesystem usage -T mnt/
btrfs balance start -dusage=5 mnt/
btrfs filesystem usage -T mnt/

# unmount
umount mnt
rmdir mnt/

3. Automate

We can automate the commands above with a script:

cat <<'EOF' > /root/misc/btrfs-balance.sh
#!/bin/bash -x

disk=${1:-/dev/nvme1n1}

mnt=$(dirname $0)/mnt/
mkdir -p $mnt

mount -t btrfs $disk $mnt
btrfs balance start -dusage=10 $mnt

umount $mnt
rmdir $mnt
EOF

chmod +x /root/misc/btrfs-balance.sh

Then we can create a cron job to call it periodically:

cat <<EOF > /etc/cron.d/btrfs-balance
0 3 * * 6 root /root/misc/btrfs-balance.sh &>/dev/null
EOF