Skip to main content
RunBook Academy

CephCXII · OSD Host LossOSD Host Loss

Controlling how fast recovery runs

Advanced⏱ ~18 minceph

What you'll learn

  • Identify the active OSD scheduler
  • Change recovery aggressiveness through the right lever
  • Estimate when recovery will finish
  • Detect an OSD whose measured capacity is wrong

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

Recovery competes with client I/O for the same devices, and the settings most operators reach for have had no effect since the scheduler changed.

Which scheduler is running

ceph config get osd osd_op_queue          # mclock_scheduler
ceph config get osd osd_mclock_profile    # balanced
ceph config get osd osd_mclock_override_recovery_settings   # false
ProfileClient I/ORecovery
high_client_opsprioritisedslowest
balanceddefault splitdefault
high_recovery_opsreducedfastest
customoperator-defined reservations and limitsoperator-defined
ceph config set osd osd_mclock_profile high_recovery_ops
ceph config set osd osd_mclock_profile balanced      # back again

The settings that no longer apply

# these are overridden by the profile unless the override is enabled
ceph config get osd osd_max_backfills
ceph config get osd osd_recovery_max_active
ceph config get osd osd_recovery_sleep
ceph config set osd osd_mclock_override_recovery_settings true
ceph config set osd osd_max_backfills 4
Setting `osd_max_backfills` under the default scheduler and observing no
change is the single most common wasted hour in a recovery. Switch the
profile instead.

Estimating completion

ceph -s | grep -A2 'io:'
  io:
    client:   412 MiB/s rd, 88 MiB/s wr, 3.10k op/s rd, 1.90k op/s wr
    recovery: 1.9 GiB/s, 640 objects/s
ceph -s --format json | python3 -c '
import sys,json
d = json.load(sys.stdin)["pgmap"]
deg, rate = d.get("degraded_objects",0), d.get("recovering_objects_per_sec",0)
print("degraded: %d  rate: %d/s  eta: %s" %
      (deg, rate, "%.1f h" % (deg/rate/3600) if rate else "stalled"))'
ceph progress

Removing the competition

ceph osd set noscrub
ceph osd set nodeep-scrub
Deep scrub reads every object on the OSD and competes for exactly the
device time recovery needs. Suspending it for the duration is usually
worth more than any profile change, and both flags must be cleared
afterwards or the cluster silently stops verifying itself.

Finding the OSD that is holding it up

ceph osd perf | sort -k2 -n -r | head
ceph config show osd.7 osd_mclock_max_capacity_iops_ssd
Each OSD benchmarks its device at first start and stores the result. A
benchmark taken while the device was busy records a capacity far below
the truth, and mClock then throttles that OSD to match.
ceph config rm osd.7 osd_mclock_max_capacity_iops_ssd
ceph orch daemon restart osd.7

Quiz

Knowledge check · 4 questions

  1. Q1. Recovery is too slow and `osd_max_backfills` has been raised with no observable change. What is happening?

  2. Q2. Changing the mClock profile requires restarting the OSDs.

  3. Q3. Speed up a recovery that will otherwise run into business hours.

    A host loss left 3.7 million objects degraded. Recovery is running at 180 objects per second and the cluster is idle overnight.

  4. Q4. How can one OSD end up throttling an entire recovery under mClock?

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

Production discipline

Change osd_mclock_profile rather than osd_max_backfills — under the default scheduler the classic throttles are supplied by the profile and ignored. Check ceph osd perf and the stored mClock capacity for a single outlier before concluding the whole cluster is slow.

Cross-course references

  • Kubernetes: resource requests allocate shares of measured capacity, not counts of pods
  • Linux: an I/O scheduler calibrated against a bad benchmark throttles for the life of the device