BTRFS-SCRUB(8)

<h1>πŸ¦‹ BTRFS-SCRUB πŸ’…</h1>

okay so bestie, welcome to BTRFS-SCRUB(8) β€” that's the btrfs-scrub page, section 8 of the btrfs manual, and it's literally all about scrubbing your filesystem and verifying block checksums. pull up a chair, we're doing this together 🫢


✨ NAME ✨

btrfs-scrub β€” scrub btrfs filesystem, verify block checksums πŸ’–


✨ SYNOPSIS ✨

btrfs scrub <subcommand> <args>

✨ DESCRIPTION ✨

OK so scrub is a validation pass over all your filesystem data and metadata. It's the vibe check your disk has been needing. What does it catch? πŸ‘€

  • data checksum errors
  • basic super block errors
  • basic metadata block header errors
  • disk read errors

Scrub happens on a per-device basis. If you hand a device to btrfs scrub start, then bestie, only that one device gets scrubbed. That said, btrfs will still try to read another device to hunt down a good copy, in case the mirror on the device you specified failed to be read or failed verification. It's giving "I'll find a backup bestie" energy 😭

And if you pass a path of btrfs to btrfs scrub start? Then btrfs scrubs all devices in parallel. slay. πŸƒβ€β™€οΈπŸƒβ€β™€οΈ

On filesystems that use replicated block group profiles (think RAID1 and friends), a read-write scrub will automatically repair any damage by copying verified good data from one of the other replicas. no cap, it heals itself πŸ’…

That same automatic repair also happens when reading metadata or data from a read-write mounted filesystem. iconic behaviour fr fr ✨

⚠️ Warning: As currently implemented, setting the NOCOW file attribute (with chattr +C) on a file implicitly enables NODATASUM. What does that mean for you? It means that while metadata for these files keeps on getting validated and corrected by scrub, the actual file data is not. πŸ™ˆ Furthermore, btrfs does not currently mark missing or failed disks as unreliable, so it will keep right on load-balancing reads to potentially damaged replicas. Normally this isn't a problem because damage gets detected by checksum validation β€” but because NOCOW files aren't protected by checksums, btrfs has literally no idea which mirror is the good one, so it can hand the bad contents right back to the user space tool. πŸ’€ Detecting and recovering from such failure requires manual intervention. Notably, systemd sets +C on journals by default β€” https://github.com/systemd/systemd/commit/11689d2a021d95a8447d938180e0962cd9439763 β€” and libvirt β‰₯ 6.6 sets +C on storage pool directories by default β€” https://www.libvirt.org/news.html#v6-6-0-2020-08-02. Other applications or distributions may also set +C to try to improve performance.
πŸ“Œ Note: Scrub is not a filesystem checker (fsck, btrfs-check(8)). It can only detect filesystem damage using checksum validation, and it can only repair filesystem damage by copying from other known good replicas. btrfs-check(8) performs more exhaustive checking, and can sometimes be used β€” with expert guidance β€” to rebuild certain corrupted filesystem structures when there's no good replica left. but like, expert guidance. for real. πŸ‘‘
πŸ“Œ Note: Read-only scrub on a read-write filesystem will still cause some writes into the filesystem. This is due to a design limitation meant to prevent a race between marking a block group read-only and writing back block group items. So if you want zero writes from scrub, you gotta run read-only scrub on a read-only filesystem. that's the deal πŸŽ€
πŸ“Œ Note: After the v6.19 kernel, scrub can be interrupted by various events, including but not limited to power management suspend/hibernate, filesystem freezing, cgroup freezing (utilized by systemd for slice freezing) and pending signals. The running scrub will be cancelled after such an interruption, and you can resume it with the btrfs scrub resume command. no stress, it remembers where it was 🧚

The user is supposed to run it manually or via a periodic system service. The recommended period is a month, but it could be less. The estimated device bandwidth utilization is about 80% on an idle filesystem. yes bestie, 80% β€” she's hungry πŸ“

The scrubbing status is recorded in /var/lib/btrfs/ in textual files named scrub.status.UUID for a filesystem identified by the given UUID. (Progress state is communicated through a named pipe in the file scrub.progress.UUID in the same directory.) The status file is updated every 5 seconds. A resumed scrub will continue from the last saved position. obsessed with the continuity honestly πŸ’œ

Scrub can be started only on a mounted filesystem, though it's possible to scrub only a selected device. See btrfs scrub start for more β€” that's a whole other section, iykyk.


🎧 Bandwidth and IO limiting 🎧

πŸ“Œ Note: The ionice(1) β€” https://man7.org/linux/man-pages/man1/ionice.1.html β€” may not be generally supported by all IO schedulers, and the options to btrfs scrub start may not work as expected. just so you know πŸ’…

Back in the day, when the CFQ IO scheduler β€” https://en.wikipedia.org/wiki/Completely_fair_queueing β€” was the generally used one, the ionice(1) β€” https://man7.org/linux/man-pages/man1/ionice.1.html β€” syscalls set the priority to idle, so the IO wouldn't interfere with regular IO. But since kernel 5.0, CFQ is not available. gone. rest in peace πŸͺ¦

The IO scheduler known to support that is BFQ β€” https://docs.kernel.org/block/bfq-iosched.html β€” but first read the documentation before using it! bestie please.

For other commonly used schedulers like mq-deadline β€” https://docs.kernel.org/block/blk-mq.html β€” it's recommended to use the cgroup2 IO controller, which could be managed by e.g. systemd (documented in systemd.resource-control). However, starting scrub like that is not yet completely straightforward πŸ™ƒ The IO controller must know the physical device of the filesystem and create a slice so all processes started from that belong to the same accounting group.

$ systemd-run -p "IOReadBandwidthMax=/dev/sdx 10M" btrfs scrub start -B /

Since linux 5.14 it's possible to set per-device bandwidth limits in a BTRFS-specific way, using the files /sys/fs/btrfs/FSID/devinfo/DEVID/scrub_speed_max ✨ This setting is not persistent β€” it lasts until the filesystem is unmounted. Currently set limits can be displayed by the command btrfs scrub limit.

$ echo 100m > /sys/fs/btrfs/9b5fd16e-1b64-4f9b-904a-74e74c0bbadc/devinfo/1/scrub_speed_max
$ btrfs scrub limit /
UUID: 9b5fd16e-1b64-4f9b-904a-74e74c0bbadc
Id      Limit      Path
--  ---------  --------
 1  100.00MiB  /dev/sdx

that's part 1 bestie, more is coming so don't go anywhere 🌈

πŸ’… SUBCOMMAND


cancel <path>|<device>

If a scrub is currently running on the filesystem that path or device points at β€” girl, it's getting cancelled. Periodt.

If you hand it a device instead, it tracks down the filesystem that device belongs to and btrfs scrub cancel acts exactly like you'd called it on that filesystem. And good news: the progress gets saved into the status file, so later btrfs scrub resume can pick up right where it left off. No cap, your place is saved. πŸŽ€

limit [options] <path>

Shows or sets the scrub limits on the devices of the filesystem you gave it. Vibe check on your throughput, bestie.

Options

  • -d, --devid DEVID β€” pick the device by DEVID to apply the limit to
  • -l, --limit SIZE β€” sets that device's limit to SIZE (size units with suffix work here), or 0 to reset it back to unlimited
  • -a, --all β€” apply the limit to every device, all of them, the whole squad
  • --raw β€” prints all numbers as raw values in bytes, no B suffix
  • --human-readable β€” prints human friendly numbers, base 1024, and this is the default
  • --iec β€” selects the 1024 base for the following options, per the IEC standard
  • --si β€” selects the 1000 base for the following options, per the SI standard
  • --kbytes β€” shows sizes in KiB, or kB with --si
  • --mbytes β€” shows sizes in MiB, or MB with --si
  • --gbytes β€” shows sizes in GiB, or GB with --si
  • --tbytes β€” shows sizes in TiB, or TB with --si

resume [-BdqrR] <path>|<device>

Resumes a cancelled or interrupted scrub on the filesystem identified by path, or on a given device. The starting point is read from the status file if it exists. It's giving "we were on a break" but make it a filesystem. πŸ’–

Important though: this does not start a new scrub if the last scrub finished successfully. It's a resume, not a reboot of the whole thing.

Options

see scrub start.

start [options] <path>|<device>

Starts a scrub on all devices of the mounted filesystem identified by path, or on a single device. If a scrub is already running, the new one will not start β€” it's strictly one main character at a time. Also, a device of an unmounted filesystem cannot be scrubbed this way. 🫢

Without options, scrub starts as a background process. The automatic repairs of damaged copies are performed by default for block group profiles with redundancy. You can turn off the repairing with option -r. πŸ”₯

Options

  • -B β€” do not background, and print scrub statistics when finished
  • -d β€” print separate statistics for each device of the filesystem (-B only), at the end
  • -r β€” run in read-only mode, do not attempt to correct anything, and it can be run on a read-only filesystem
Okay so listen: a read-only scrub on a read-write filesystem can still cause writes into the filesystem, because of some internal limitations. Only a read-only scrub on a read-only filesystem can truly avoid writes from scrub. That's the tea. 😭
  • -R β€” raw print mode, print full data instead of a summary
  • --limit <limit> β€” set the scrub throughput limit for each device

If the scrub is for the whole filesystem, it's the same as btrfs scrub limit --all --limit <value>. If the scrub is for a single device, it's the same as btrfs scrub limit --devid <devid> -l <value>.

The value is bytes per second, and it accepts the usual KMGT suffixes. After the scrub is finished, the throughput limit will be reset to the old value of each device. So it's not permanent, bestie, don't stress. ✨

  • -f β€” force starting a new scrub even if a scrub is already running. This can be useful when the scrub status file is damaged and reports a running scrub although it is not, but it should not normally be necessary. Lowkey a last-resort move.

Deprecated options

The priority settings work only with certain schedulers β€” in particular they don't work with the most common one, mq-deadline. So, yeah, ghosts of options past. πŸ’€

  • -c <ioprio_class> β€” set IO priority class (see the ionice(1) manual page) if the IO scheduler configured for the device supports ionice. This is only supported by BFQ or Kyber but is not supported by mq-deadline. Please read the section about IO limiting.
  • -n <ioprio_classdata> β€” set IO priority classdata (see the ionice(1) manual page)
  • -q β€” (deprecated) alias for the global -q option

status [options] <path>|<device>

Shows the status of a running scrub for the filesystem identified by path, or for the specified device.

If no scrub is running, it shows the statistics of the last finished or cancelled scrub for that filesystem or device. The status is read from the file located in /var/lib/btrfs. πŸ¦‹

Options

  • -d β€” print separate statistics for each device of the filesystem
  • -R β€” print all raw statistics without postprocessing, exactly as returned by the status ioctl
  • --raw β€” print all numbers as raw values in bytes without the B suffix
  • --human-readable β€” print human friendly numbers, base 1024, this is the default
  • --iec β€” select the 1024 base for the following options, according to the IEC standard
  • --si β€” select the 1000 base for the following options, according to the SI standard
  • --kbytes β€” show sizes in KiB, or kB with --si
  • --mbytes β€” show sizes in MiB, or MB with --si
  • --gbytes β€” show sizes in GiB, or GB with --si
  • --tbytes β€” show sizes in TiB, or TB with --si

A status on a filesystem without any error looks like the following β€” clean, flawless, no notes ✨:

# btrfs scrub start /
# btrfs scrub status /
UUID:             76fac721-2294-4f89-a1af-620cde7a1980
Scrub started:    Wed Apr 10 12:34:56 2023
Status:           running
Duration:         0:00:05
Time left:        0:00:05
ETA:              Wed Apr 10 12:35:01 2023
Total to scrub:   28.32GiB
Bytes scrubbed:   13.76GiB  (48.59%)
Rate:             2.75GiB/s
Error summary:    no errors found

With some errors found, it's giving less slay but very informative:

Error summary:    csum=72
  Corrected:      2
  Uncorrectable:  72
  Unverified:     0
  • Corrected β€” number of bad blocks that were repaired from another copy
  • Uncorrectable β€” errors detected at read time but not possible to repair from another copy. Ouch. πŸ’€
  • Unverified β€” transient errors, first read failed but a retry succeeded, and these may be affected by lower layers that group or split IO requests
  • Error summary β€” followed by a more detailed list of errors found
    • csum β€” checksum mismatch
    • super β€” super block errors; unless the error is fixed immediately, the next commit will overwrite the superblock
    • verify β€” metadata block header errors
    • read β€” blocks can't be read due to IO errors

It's possible to set a per-device limit via the file sysfs/fs/btrfs/FSID/devinfo/scrub_speed_max. In that case the limit is printed on the Rate: line if option -d is specified, or without it on a single-device filesystem. Read more about that in the section about scrub IO limiting. 🌸

Rate:             989.0MiB/s (limit 1.0G/s)

On a multi-device filesystem with at least one device limit, the overall stats cannot print the limit without -d, so there's a note that some limits are set:

Rate:             36.37MiB/s (some device limits set)

🌈 EXIT STATUS

btrfs scrub returns a zero exit status if it succeeds. Non zero is returned in case of failure:

  1. scrub couldn't be performed
  2. there is nothing to resume
  3. scrub found uncorrectable errors

πŸ‘‘ AVAILABILITY

btrfs is part of btrfs-progs. Please refer to the documentation at https://btrfs.readthedocs.io.

πŸŽ€ SEE ALSO

mkfs.btrfs(8)


And that's a wrap, bestie β€” this page was version 7.3, dated Oct 24, 2026, and it's from BTRFS-SCRUB(8). Go forth and scrub responsibly, you gorgeous nerd. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from