Proxmox VEXVI · MonitoringMonitoring strategy
Monitoring Ceph and PBS
What you'll learn
- Distinguish Ceph metrics that predict trouble from those that merely report it
- Explain why cluster-average capacity is the wrong figure and what to alert on instead
- Monitor PBS job outcomes, which the PBS metric export does not carry
- Set thresholds that fire with time to act rather than at the moment of failure
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
Node monitoring is well understood: CPU, memory, disk, network, and you know roughly what bad looks like. Ceph and PBS are different, and they are different in the same way — both degrade for a long time before they fail, and neither degradation is visible in the metrics an operator habitually watches.
A Ceph cluster that is one disk away from blocking writes has normal CPU, normal memory and normal network. A PBS datastore that has not produced a restorable backup for one guest in six weeks has normal disk usage and a green dashboard. In both cases the useful signal is a specific number that nobody looks at unless somebody told them to, and this lesson is that somebody.
Ceph: the numbers that predict rather than report
HEALTH_OK is not a monitoring strategy. It is a summary of health checks
that have already tripped. The useful monitoring is upstream of it.
# ceph -s cluster:
id: 8f2c1b44-9d3a-4e57-b1c0-2a7e5d9f0c31
health: HEALTH_WARN
1 nearfull osd(s)
Degraded data redundancy: 4127/1893456 objects degraded (0.218%)
services:
mon: 3 daemons, quorum pve-01,pve-02,pve-03 (age 4w)
mgr: pve-01(active, since 4w), standbys: pve-02
osd: 12 osds: 11 up (since 22m), 12 in (since 3w)
data:
pools: 2 pools, 289 pgs
objects: 631.15k objects, 2.4 TiB
usage: 7.3 TiB used, 10.5 TiB / 17.8 TiB avail
pgs: 271 active+clean
18 active+undersized+degraded
io:
client: 14 MiB/s rd, 42 MiB/s wr, 812 op/s rd, 1.902k op/s wr
recovery: 128 MiB/s, 31 objects/sIllustrative output
Reading it line by line:
11 up (since 22m), 12 in. One OSD is down but still markedin, meaning Ceph still expects it to hold data. Oncemon_osd_down_out_intervalelapses (10 minutes by default, unlessnooutis set) it is markedoutand recovery begins in earnest. The gap betweenupandinis where you still have a choice about whether to fix the disk or let the cluster heal around it.active+undersized+degraded. These PGs have fewer replicas than the pool requires. They are still serving I/O.undersizedmeans fewer OSDs thansize;degradedmeans fewer copies than required. Neither is an outage.inactive,incompleteordownin that list would be.usage: 7.3 TiB used, 10.5 TiB avail. This is the number that will mislead you, and the next section is about why.- MON quorum
age 4w. A quorum age that keeps resetting means monitors are flapping, which is a network or clock problem and a much more serious finding than a nearfull OSD.
set -euo pipefail
# Per-OSD utilisation, with the spread visible. The STDDEV line at the
# bottom is a direct measure of how unbalanced the cluster is.
ceph osd df tree
# The single number to alert on.
ceph osd df --format json \
| grep -o '"utilization":[0-9.]*' \
| cut -d: -f2 | sort -g | tail -1
# What the thresholds are set to on this cluster.
ceph config get mon mon_osd_nearfull_ratio
ceph config get mon mon_osd_full_ratio
ceph config get osd osd_backfillfull_ratio
# Per-pool usage and, more usefully, MAX AVAIL - what a pool can still take
# given its replication and the fullest OSD in its CRUSH tree.
ceph df detailMAX AVAIL in ceph df detail is the honest capacity figure. It already
accounts for replication and for the fullest OSD in the pool’s failure
domain, and it is usually a good deal smaller than the number an operator
computes by dividing raw free space by three.
The health checks worth alerting on individually
HEALTH_WARN is too coarse to page on and too important to ignore. Alert per
check:
| Health check | Means | Urgency |
|---|---|---|
OSD_FULL | An OSD hit the full ratio; writes refused | Page immediately |
OSD_BACKFILLFULL | Rebalancing onto an OSD has stopped | Page — the cluster can no longer heal |
PG_AVAILABILITY | PGs are inactive; I/O to them is blocked | Page |
MON_DOWN, MON_CLOCK_SKEW | Monitor quorum is at risk | Page |
PG_DAMAGED | Scrub found an inconsistency | Urgent ticket; this is possible corruption |
SLOW_OPS | Requests taking longer than the complaint threshold | Urgent — usually a dying disk |
OSD_NEARFULL | 85% on at least one OSD | Ticket, with a deadline |
PG_NOT_DEEP_SCRUBBED | Deep scrubs are falling behind | Ticket — you are not detecting bit rot |
OSD_DOWN | An OSD is down | Ticket if one; page if it is the second |
set -euo pipefail
# Per-OSD commit latency. Sort and look at the tail.
ceph osd perf
# Which health checks are currently firing, with detail rather than summary.
ceph health detail
# Deep scrub age - how long since each PG was fully verified. Falling behind
# here means bit rot would not be detected.
ceph pg dump pgs 2>/dev/null | awk 'NR>1 {print $1, $23, $24}' | head -20
# Confirm the suspect disk from the OS side before condemning it.
OSD_DISK=/dev/sdf
smartctl -a "$OSD_DISK" | grep -Ei 'reallocated|pending|uncorrect|error rate'PBS: the metrics do not measure what you care about
PBS supports the same external metric server model as PVE, configured under Configuration → Metric Server. It exports host metrics — memory, network, disk activity — and datastore usage.
It does not export job outcomes.
set -euo pipefail
DATASTORE=pbs-main
MAX_AGE_HOURS=36
# Every backup group and the timestamp of its most recent snapshot.
proxmox-backup-manager datastore list
# Per-group freshness. Anything older than the threshold is a finding,
# whether or not any job ever reported an error.
proxmox-backup-client snapshot list \
--repository "root@pam@localhost:$DATASTORE" \
--output-format json \
| grep -o '"backup-time":[0-9]*' | cut -d: -f2 | sort -n | tail -1 \
| while read -r newest; do
age=$(( ( $(date +%s) - newest ) / 3600 ))
echo "newest snapshot is ${age}h old (threshold ${MAX_AGE_HOURS}h)"
[ "$age" -le "$MAX_AGE_HOURS" ] || echo 'STALE - investigate'
doneWhat to watch on the PBS host
| Signal | Where from | Threshold that gives you time |
|---|---|---|
| Datastore usage | Metrics | Alert at 75%, not 90% — prune and GC need free space to work |
| Garbage collection last run and outcome | proxmox-backup-manager garbage-collection status | Alert if none succeeded in 48 h |
| Verify job outcome | Task list / notifications | Any failure is urgent |
| Oldest snapshot against retention | Snapshot list | Alert if retention is not actually being met |
| Newest snapshot per group | Snapshot list | Alert at 1.5x the expected interval |
| Chunk store filesystem | Host metrics | Inode exhaustion is possible before space exhaustion |
| Task failures | proxmox-backup-manager task list | Any non-zero failure count in 24 h |
set -euo pipefail
DATASTORE=pbs-main
# Datastore usage from the PBS side.
proxmox-backup-manager datastore list
# Did garbage collection run, and did it succeed?
proxmox-backup-manager garbage-collection status "$DATASTORE"
# Recent tasks, including failures. This is where verify and prune outcomes
# are visible from the CLI.
proxmox-backup-manager task list --limit 50
# Bytes and inodes. On ext4 or XFS the second one can run out first.
df -h /mnt/datastore
df -i /mnt/datastore
# The underlying pool, if the datastore is on ZFS.
zpool status -x
zpool list -o name,size,alloc,free,capacity,fragmentationSetting thresholds that give you time
The recurring theme across both subsystems is that the default instinct — alert when the thing is nearly broken — leaves no room to act. Both Ceph and PBS have a remediation step that itself needs resources, and if you alert at the point of exhaustion, the remediation is unavailable.
| Subsystem | Naive threshold | Better threshold | Why |
|---|---|---|---|
| Ceph capacity | Cluster 90% used | Fullest OSD 80%, paging at 85% | Backfill stops at 0.90; you need room to rebalance |
| Ceph OSD down | Any OSD down | Ticket at one, page at two in the same failure domain | One is routine; two is a redundancy decision |
| PBS datastore | 90% used | 75% used | GC and prune need free space |
| Backup freshness | Job failed | No new snapshot in 1.5x interval | A job that stopped running reports nothing |
| Deep scrub | Ignore the warning | Ticket with a deadline | The detection window must stay shorter than backup retention |
Common mistakes
- Alerting on
HEALTH_WARNas a single condition. It is either constantly firing and therefore ignored, or suppressed and therefore useless. Alert per health check. - Using cluster-average capacity. The fullest OSD is the constraint.
- Reading
SLOW_OPSas load. It is usually one dying disk. - Treating
PG_NOT_DEEP_SCRUBBEDas cosmetic. It sets the size of the window between corruption and detection. - Believing a PBS metrics dashboard reports backup success. It reports host and datastore statistics only.
- Monitoring only for job failure, so a job that stopped being scheduled is invisible.
- Monitoring bytes and not inodes on a non-ZFS chunk store.
- Leaving a pool at
size=2, min_size=1and never auditing for it.
Key takeaways
- Ceph’s usable capacity is set by the fullest OSD, not the average, and writes stop cluster-wide at 0.95 on any one OSD.
- The gap between backfillfull (0.90) and full (0.95) is where the cluster loses the ability to heal itself.
- Alert on individual health checks with individual urgencies;
HEALTH_WARNalone is not actionable. SLOW_OPSplusceph osd perffinds the one bad disk behind “everything is slow”.- Deep scrub lag determines whether corruption is caught inside your backup retention window.
- PBS metrics carry host and datastore statistics and no job outcomes. Backup success comes from notifications; backup presence comes from checking snapshot freshness yourself.
- Set thresholds so that the remediation is still possible when they fire.
Knowledge check
Knowledge check · 5 questions
Q1. A Ceph cluster reports 58% used overall. Which figure should the capacity alert actually be based on?
Q2. Which of these can be determined from the PBS external metric export? Select all that apply.
Q3. A pool configured with size=2 and min_size=1 keeps serving I/O after a single OSD failure, so it is a reasonable trade-off for cost-sensitive clusters.
Q4. Users report that every VM on a Ceph-backed cluster feels slow. CPU, memory and network on the nodes all look normal, and Ceph reports SLOW_OPS. What is the most likely cause?
Q5. Why should a PBS datastore alert at around 75% usage rather than the 90% commonly used for other filesystems?
Passing score: 75%. Answers are checked in this browser.