Skip to main content
RunBook Academy

CephLXI · ScrubbingScrubbing

How Ceph schedules scrubs

Intermediate⏱ ~17 minceph

What you'll learn

  • Explain the scrub scheduling algorithm
  • Read the scrub stamps to see the schedule working
  • Recognise a schedule falling behind
  • Intervene only where intervention is warranted

Prerequisites

None — start here.

Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18

Not yet marked complete on this device.

Why this matters in production

Ceph schedules its own scrubs and mostly does it well. Knowing the algorithm distinguishes a schedule that is working from one that is silently falling behind.

The intervals

ceph config get osd osd_scrub_min_interval
ceph config get osd osd_scrub_max_interval
ceph config get osd osd_deep_scrub_interval
ceph config get osd osd_scrub_interval_randomize_ratio
SettingMeaning
osd_scrub_min_intervaldo not scrub a PG more often than this
osd_scrub_max_intervalscrub regardless of load after this
osd_deep_scrub_intervaltarget interval for deep scrub
osd_scrub_interval_randomize_ratiospread to avoid synchronisation

The randomisation matters: without it, all PGs created at the same time would become due at the same time, producing a scrub storm every week rather than a steady trickle.

The load check

ceph config get osd osd_scrub_load_threshold
ceph config get osd osd_scrub_begin_hour
ceph config get osd osd_scrub_end_hour

Between min_interval and max_interval the OSD scrubs only when the system load average is below the threshold and the current hour falls in the permitted window. Past max_interval it scrubs regardless — which is the safety valve that prevents an always-busy cluster from never scrubbing.

flowchart TD
  A[PG due for scrub?] --> B{since last < min_interval?}
  B -->|yes| C[skip]
  B -->|no| D{since last > max_interval?}
  D -->|yes| E[scrub now]
  D -->|no| F{load below threshold<br/>and in time window?}
  F -->|yes| E
  F -->|no| C

Reading the schedule

ceph pg dump pgs 2>/dev/null | awk 'NR>1 {print $21}' | sort | head -3
ceph pg dump pgs 2>/dev/null | awk 'NR>1 {print $23}' | sort | head -3

The oldest stamps show the PGs most overdue. A healthy cluster’s oldest deep scrub stamp is within roughly the deep scrub interval; one from months ago means the schedule is not keeping up.

Recognising a schedule falling behind

ceph health detail | grep -E 'PG_NOT_SCRUBBED|PG_NOT_DEEP_SCRUBBED'

Ceph raises these warnings when PGs exceed their intervals by a configured factor. Causes, in order of likelihood:

CauseCheck
Load threshold never metuptime on the OSD hosts
Time window too narrowosd_scrub_begin_hour / end_hour
noscrub flag setceph osd dump | grep flags
Too many PGs for the intervalPG count versus scrub throughput
Recovery continuously runningceph -s

Quiz

Knowledge check · 4 questions

  1. Q1. What does `osd_scrub_max_interval` guarantee?

  2. Q2. Setting `osd_scrub_interval_randomize_ratio` to zero makes scrub load more predictable.

  3. Q3. Investigate PG_NOT_DEEP_SCRUBBED warnings.

    A cluster reports PG_NOT_DEEP_SCRUBBED for 340 PGs. The scrub window is set to 01:00–05:00 and the load threshold to 0.5. The cluster runs a batch job overnight.

  4. Q4. Why is a very old last_deep_scrub_stamp a problem even though the cluster is healthy?

Passing score: 75%. Answers are checked in this browser.

Production discipline

Monitor PG_NOT_DEEP_SCRUBBED and the oldest last_deep_scrub_stamp rather than assuming the schedule is keeping up; a narrow window plus a low load threshold can make the scrub condition unreachable. Leave the randomize ratio alone — it is what prevents synchronised scrub storms.

Cross-course references

  • Kubernetes: CronJob jitter serves the same anti-synchronisation purpose
  • Linux: anacron and systemd timer RandomizedDelaySec exist for identical reasons