CephCVI · RBD BackupRBD Backup
Meeting RBD recovery targets at scale
What you'll learn
- Compute achievable RPO and RTO for a fleet
- Identify the binding constraint
- Choose mechanisms that meet the targets
- Verify the targets are met
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
Why this matters in production
Per-image targets are easy. A fleet of hundreds of images sharing one cluster, one network, and one backup target is where they stop being met.
Computing achievable targets
# Substitute your own values before running:
POOL=rbd-vms
# the fleet's actual footprint
rbd ls "$POOL" --format json | python3 -c '
import sys,json,subprocess
pool = sys.argv[1]
tot = 0; n = 0
for img in json.load(sys.stdin):
d = json.loads(subprocess.check_output(
["rbd","du",pool+"/"+img,"--format","json"]))
u = sum(i.get("used_size",0) for i in d.get("images", []))
tot += u; n += 1
print("images:", n, " used: %.1f TiB" % (tot/1024**4))' "$POOL"
Achievable RPO = time to complete one full backup cycle.
Achievable RTO = time to restore the images an incident affects,
not the time to restore one.
| Fleet quantity | Effect |
|---|---|
| Total used size | full backup duration |
| Daily change rate | incremental duration |
| Backup target write throughput | often the binding constraint |
| Restore concurrency | how many images restore in parallel |
| Images affected per incident | what RTO must actually cover |
# daily change rate, measured from consecutive diffs
ls -l /backups/*.diff | awk '{s+=$5} END {printf "daily diff total: %.1f GiB\n", s/1024^3}'
The binding constraint
# Substitute your own values before running:
POOL=rbd-vms
IMAGE=vm-101-disk-0
SNAP=nightly-2026-08-18
BACKUP_HOST=backup.example.com
# time a representative export to find the real rate
time rbd export "$POOL/$IMAGE@$SNAP" - | ssh "$BACKUP_HOST" 'cat > /dev/null'
| Constraint | Symptom | Response |
|---|---|---|
| Cluster read | OSD latency rises during backup | stagger; throttle concurrency |
| Network | rate plateaus below both ends’ capability | more bandwidth, or compress |
| Backup target write | target disk saturated | faster target, or more targets |
| Serial execution | cluster and network idle | parallelise |
| Backup window | everything else fits, the clock does not | incrementals, or a longer window |
# during a backup run
POOL=rbd-vms
ceph osd pool stats ${POOL}
iostat -x 5 3
Choosing mechanisms
| RPO target | Mechanism | Fleet implication |
|---|---|---|
| 24 hours | nightly incrementals | fits most fleets |
| 4 hours | incrementals every 4 hours | 6× the diff volume |
| 1 hour | snapshot-based mirroring | needs a peer cluster |
| Minutes | mirroring on a short schedule | needs bandwidth for the change rate |
# Substitute your own values before running:
POOL=rbd-vms
IMAGE=vm-101-disk-0
rbd mirror pool enable "$POOL" image
rbd mirror image enable "$POOL/$IMAGE" snapshot
rbd mirror snapshot schedule add --pool "$POOL" 1h
rbd mirror pool status "$POOL" --verbose | head -20
Mirroring moves the RPO problem from the backup window to the network:
the change rate must fit the link continuously rather than fitting a
nightly window.
Verifying
# actual RPO across the fleet: age of the newest backup per image
POOL=rbd-vms
rbd ls ${POOL} | while read img; do
newest=$(rbd snap ls "${POOL}/$img" --format json 2>/dev/null | python3 -c '
import sys,json
s = json.load(sys.stdin)
print(s[-1]["name"] if s else "NONE")')
printf '%-28s %s\n' "$img" "$newest"
done | grep -c NONE
# Substitute your own values before running:
POOL=rbd-vms
# actual RTO: restore a representative image and time the whole sequence
time ( rbd import /backups/rep.raw "$POOL/rto-test" && \
rbd map "$POOL/rto-test" && \
mount "/dev/rbd/$POOL/rto-test" /mnt/rto && \
ls /mnt/rto >/dev/null )
umount /mnt/rto; rbd unmap "/dev/rbd/$POOL/rto-test"; rbd rm "$POOL/rto-test"
Quiz
Knowledge check · 4 questions
Q1. How should fleet RTO be stated?
Q2. A fleet whose daily change fits a nightly backup window will also mirror comfortably.
Q3. Assess whether recovery targets are achievable.
A pool holds 200 RBD images, 40 TiB used. The stated targets are a 1-hour RPO and a 2-hour RTO for a host failure affecting 30 images.
Q4. What happens to RPO when mirroring falls behind?
Passing score: 75%. Answers are checked in this browser.
Production discipline
State fleet RTO against a realistic incident scope and measure the parallel restore rate, not the single-image one. Put mirror lag on a dashboard; when mirroring falls behind, RPO degrades silently to whatever the link delivers.
Cross-course references
- Kubernetes: recovery targets stated per-pod mislead when a node fails
- Linux: continuous replication is sized by peak rate, batch backup by window