Proxmox VEVIII · CephCeph performance
Ceph performance and health investigation
What you'll learn
- Investigate slow Ceph I/O using built-in tools
- Interpret ceph daemon perf counters
- Distinguish client-side, network, and OSD-side bottlenecks
- Use latency and throughput metrics to find the constraint
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12
Why this matters in production
“Ceph is slow” is a production ticket. The fix depends on where the slowness is. This lesson teaches the diagnostic flow.
The diagnostic flow
flowchart LR
A[VM reports slow I/O] --> B[Where is latency?]
B --> C[Client side]
B --> D[Network]
B --> E[OSD side]
Step 0: Rule out the four states that are not performance problems
Before measuring anything, establish that the cluster is not doing something that should make it slow. Each of these produces the same ticket and none of them is a tuning question.
# 1. Is a recovery or backfill running?
ceph -s | grep -E 'recovery|backfill|misplaced|degraded'
# 2. Is a deep scrub running right now?
ceph pg dump | awk '$0 ~ /scrubbing/ {print $1, $10}' | head
# 3. Is any OSD near full? Allocation slows sharply as OSDs fill.
ceph osd df | sort -k17 -n -r | head -5
# 4. Are any flags set that change behaviour?
ceph osd statA cluster recovering from a failed OSD is slow because it is doing the work it exists to do. A cluster whose OSDs are at 88% is slow because the allocator is working harder for every write. Both look identical from a guest, and neither is fixed by anything in the rest of this lesson.
Step 1: Confirm the symptom from the client
fio --name=randread --ioengine=libaio --direct=1 --bs=4k \
--rw=randread --numjobs=1 --runtime=10 --size=1G \
--filename=/tmp/fio --group_reporting
rm -f /tmp/fioNote IOPS, latency, and throughput. If guest-side latency is fine, the issue is in the guest.
Step 2: Measure from the host
# create a scratch image rather than testing against a VM disk
POOL=vm-storage
rbd create "$POOL/fio-scratch" --size 10G
DEV=$(rbd map "$POOL/fio-scratch")
fio --name=randread --ioengine=libaio --direct=1 --bs=4k \
--rw=randread --numjobs=1 --runtime=10 --size=1G \
--filename="$DEV" --group_reporting
rbd unmap "$DEV"
rbd rm "$POOL/fio-scratch"If host-side latency is also fine but guest-side was not, the issue is in guest configuration (cache, scheduler, etc.).
Step 3: Check Ceph health
ceph -s && ceph osd perf
ceph osd perf shows per-OSD latency and throughput. A spread of latencies across OSDs
indicates a few hot OSDs; a uniformly elevated latency indicates a cluster-wide issue.
Step 4: Use OSD perf counters
ceph daemon osd.0 perf dump | head -100
Key counters:
| Counter | Meaning |
|---|---|
op_latency_avg | Average latency per OSD op |
op_latency_max | Tail latency |
op_queue_latency_avg | Time ops spend queued |
op_process_latency_avg | Time spent processing |
If op_queue_latency is high, OSDs are saturated. If op_process_latency is high, the
disk is the bottleneck.
Step 5: Check network
for nic in $(ls /sys/class/net | grep -v lo); do echo "=== $nic ==="; ethtool -S $nic 2>/dev/null | grep -E 'rx_errors|tx_errors|dropped' | head -5; doneCommon causes of “Ceph is slow”
| Cause | Indicator | Fix |
|---|---|---|
| OSDs saturated | High op_queue_latency | Add OSDs, add hosts, throttle clients |
| Slow disks | High op_process_latency | Replace disks |
| Network saturation | High NIC utilisation; drop counter increasing | Add bandwidth; isolate networks |
| PG count too low | Few PGs, low recovery throughput | Increase PG count |
| Cache cold | First IO slow, subsequent fast | Expected behaviour |
| Backfill/recovery in progress | ceph -s shows active recovery | Wait or throttle |
Latency breakdown
Ceph I/O latency is the sum of:
Client → librbd → messenger → OSD op queue → OSD op process → reply
Each step adds latency. To find the bottleneck, measure at each step.
| Step | Typical latency |
|---|---|
| Client to messenger | 0.1–1 ms |
| Messenger to OSD | 0.1–1 ms |
| OSD op queue | 0–5 ms (saturated) |
| OSD op process (write) | 1–10 ms (depends on replication) |
| OSD op process (read) | 0.5–5 ms |
A replication=3 write has 3× the OSD op process latency (one per replica) plus network RTT between OSDs.
Production considerations
Common mistakes
- Optimising the wrong layer. Always measure from the symptom outward.
- Tuning before ruling out recovery, deep scrub, nearfull OSDs and set flags. A cluster doing correct work slowly is not a tuning problem.
- Looking for a cluster-wide cause when one OSD is dying slowly.
ceph osd perffinds it in seconds and no aggregate metric will. - Raising
sizeto improve performance. Reads are served by the primary alone, so more replicas cost write latency and buy nothing for reads. - Running
fioagainst a device that holds a live VM disk. Create a scratch RBD image. - Leaving
rados benchobjects behind. They occupy real pool capacity. - Trusting ceph -s HEALTH_OK without checking performance counters.
- Believing latency is “fine” because the average is low. Tail latency is what users feel.
- Adding capacity when the bottleneck is throughput, not capacity.
Key takeaways
- Rule out recovery, scrub, nearfull and flags before measuring anything.
- Measure from the guest inward:
fioin the guest,fioon a scratch RBD image, thenrados benchfor RADOS itself. - A
size 3write waits for the slowest of three OSDs, so one degraded disk slows every PG it touches.ceph osd perfis how you find it. ceph osd perfandceph daemon osd.X perf dumpgive OSD-level detail.- Tail latency matters; monitor p95 and p99.
Knowledge check
Knowledge check · 4 questions
Q1. Which command shows per-OSD latency and throughput?
Q2. Average latency is sufficient to detect performance problems.
Q3. What does a high op_queue_latency counter suggest?
Q4. Users report that every VM on the cluster is slow. ceph -s reports HEALTH_OK, no recovery is running, no OSD is nearfull and no flags are set. Where do you look next?
Passing score: 75%. Answers are checked in this browser.