Ceph — Optimization and Capacity Planning¶
The problem¶
Ceph starts with reasonable defaults and a small cluster runs fine for months. Then the database on RBD arrives and tail latencies show up, or usage grows and one day an OSD hits 95% and the cluster stops accepting writes. Neither is a Ceph failure: they are design decisions (replicas, DB on NVMe, network, capacity reserves) that were left at their defaults.
This page gathers the settings that weigh most in a self-hosted cluster with block and database workloads, and how to check with commands whether what you changed helped. Measure first, then touch.
What this covers and what it does not
This is tuning and capacity planning. Installation and architecture are in Ceph — Base Guide; incident diagnosis, in Ceph Troubleshooting; and usage with PostgreSQL, in PostgreSQL on Ceph.
Options change between versions
The names, defaults and behavior of many options (BlueStore cache, autoscaler, bluestore_compression_mode) vary between Ceph versions. Before applying a setting, check with ceph config help <option> what it means in your version and do not copy values from another cluster without measuring.
📋 Table of Contents¶
- Measure before touching
- Pools and placement groups
- BlueStore: WAL/DB and memory
- Network
- RBD and client
- Testing with fio
- Capacity planning
- Recovery and rebalancing
- Monitoring and alerts
- Troubleshooting
- Best Practices
- References
Measure before touching¶
Three commands give the baseline picture. Keep their output before and after each change to compare.
# Global state: health, OSDs, PGs, client I/O
ceph -s
# Usage per OSD and per class; look for imbalances
ceph osd df
# What the PG autoscaler does with each pool
ceph osd pool autoscale-status
In ceph osd df, look at the usage column (%USE) and the spread between OSDs: if one OSD is well above the average, the cluster's usable capacity is set by it, not by the average. In ceph -s, any slow ops or any PG that is not active+clean must be resolved before tuning anything.
Pools and placement groups¶
- Replicas:
size=3andmin_size=2for data that matters. Withsize=2ormin_size=1you gain capacity, but a double failure can lose data or accept writes without a redundant copy. - Erasure coding: saves capacity, but every small write costs more. Avoid it for OLTP; use it for cold objects or backups.
- One pool per workload type: one pool for databases, another for VM disks, another for cold data, each with its own CRUSH rule (for example, restricted to a device class).
- PGs: leave the autoscaler
onand tell Ceph how much each pool should take withtarget_size_ratio; that way it distributes PGs from the start and does not reshuffle as the pool grows.
# Replicas for the database pool
ceph osd pool set db-rbd size 3
ceph osd pool set db-rbd min_size 2
# Active autoscaler and expected share of the cluster
ceph osd pool set db-rbd pg_autoscale_mode on
ceph osd pool set db-rbd target_size_ratio 0.3
# Tag the pool for RBD
ceph osd pool application enable db-rbd rbd
Review after every PG change
A change in the number of PGs triggers data movement. Do it during off-peak hours and watch ceph -s until everything is back to active+clean.
BlueStore: WAL/DB and memory¶
BlueStore stores data on the main device and the metadata (RocksDB) and write-ahead log (WAL) wherever you tell it. On spinning disks, moving DB/WAL to NVMe or SSD is the change with the most impact on latency.
- DB/WAL on a fast device: they are split when creating the OSD (with
cephadm, through an OSD service specification that assigns data and DB devices). Size the DB with headroom: if it fills up, RocksDB spills over to the slow disk and latency collapses. - Memory per OSD:
osd_memory_targetsets how much memory each OSD tries to use; the BlueStore cache auto-tunes around that value. The total across all OSDs on a node plus the other services must not exceed physical RAM, or the node starts swapping. - Compression: enabled per pool or per OSD. It helps with compressible data and costs CPU; measure it with your real data.
# Target memory per OSD (value in bytes; 4 GiB)
ceph config set osd osd_memory_target 4294967296
# BlueStore cache autotuning
ceph config set osd bluestore_cache_autotune true
# See the effective value of an option
ceph config get osd osd_memory_target
# Options changed from the default
ceph config dump
The practical calculation: node RAM, minus what the OS and other services use (monitor, manager, containers), divided by the number of OSDs. If the result falls below your version's default, it is better to reduce OSDs per node than to force a minimal value.
Compression and version
bluestore_compression_mode accepts several modes and its default behavior depends on the version. On databases with already compressed or encrypted pages it adds nothing and burns CPU. Try it first on a test pool.
Network¶
- Bandwidth: 25 GbE or more is reasonable for NVMe. With 1 GbE, the network is the bottleneck before the disks.
- Public and cluster network: separating replication traffic from client traffic reduces interference. In a small cluster, a single network with enough bandwidth is simpler to operate.
- MTU 9000: only if the whole path (NICs, switches, VLAN) supports it. A single 1500 hop in the middle causes intermittent failures that are hard to diagnose.
- IPv6: if you do not use it, do not bind it (
ms_bind_ipv6=false); if you do, use it consistently across the whole cluster. - Latency between nodes: Ceph writes to all replicas before acknowledging, so network latency adds to every write.
RBD and client¶
- Image features:
exclusive-lock,object-map,fast-diffanddeep-flattenspeed up snapshots, clones and diffs. Enable them on production images and check compatibility with the client kernel or library. - RBD cache (librbd): on databases,
rbd_cacheenabled with safe writes (rbd_cache_writethrough_until_flush) usually fits; adjust sizes with measurements, not intuition. - QEMU/libvirt:
cache=none,io=nativeanddiscard=on, withvirtio-scsiand multiple queues. Align the guest partitions to 4K. - Guest filesystem:
ext4orxfswithnoatime; do not disable barriers. - Returning space:
discardin the guest and periodicfstrimlet Ceph free blocks; without them, the space of thin images only grows.
# Features of an image
rbd info db-rbd/pg-data
# Enable features on an existing image
rbd feature enable db-rbd/pg-data exclusive-lock object-map fast-diff
# Real space used by the image
rbd du db-rbd/pg-data
Feature order
object-map and fast-diff depend on exclusive-lock. On images already in use, after enabling them you may need to rebuild the object map (rbd object-map rebuild). Do it with the image idle.
Testing with fio¶
Always measure with a pattern close to the real one. For an OLTP database: small blocks, mixed read/write and moderate concurrency. Run it on a test image, not on real data.
fio --name=db-randrw --filename=/dev/rbd0 --ioengine=libaio --direct=1 \
--bs=8k --rw=randrw --rwmixread=70 --iodepth=32 --numjobs=4 --time_based \
--runtime=120 --group_reporting
Look at the tail latency (high percentiles), not just average IOPS. A good average with a bad tail shows up in the database as sporadic stalls. Repeat the test after each change, one at a time, and again with the cluster busy (recovery in progress) to learn the worst case.
Capacity planning¶
Ceph is not an array: usable capacity is lower than the sum of the disks.
- 3x replication: usable capacity is a third of raw. With erasure coding it is higher, at the cost of performance.
- Failure reserve: the cluster must be able to rebuild the loss of a node (or a failure domain) with the space left. Plan so that, after losing what your design tolerates, usage stays below the thresholds.
- Thresholds: Ceph warns at
nearfull, stops rebalancing atbackfillfulland blocks writes atfull. The defaults are conservative; do not raise them to gain space, add capacity. - Imbalance between OSDs: the fullest OSD sets the limit. The
balancermodule redistributes PGs to even out usage; check it is active. - Growth: adding OSDs triggers rebalancing. Do it in batches and with recovery limits if there are sensitive workloads.
# Usage of each OSD and deviation
ceph osd df tree
# Balancer module state
ceph balancer status
# Usage per pool
ceph df
Recovery and rebalancing¶
Recovery and rebalancing compete with clients for disk and network. Two levers:
- Recovery limit: lowering backfill and recovery priority protects client latency at the cost of taking longer to return to full redundancy. Scheduler option names change between versions; look them up with
ceph config help. mon_osd_down_out_interval: time the monitor waits before marking a down OSDoutand starting to rebuild. A higher value avoids rebalancing on short restarts, and a lower one shortens the window without redundancy.
# Wait 10 minutes before marking a down OSD out
ceph config set mon mon_osd_down_out_interval 600
# Before a planned node shutdown
ceph osd set noout
# ... maintenance ...
ceph osd unset noout
If there is frequent rebalancing without cluster changes, check the CRUSH weights, the network between nodes and OSDs that flap.
Monitoring and alerts¶
- Metrics: the manager's
prometheusmodule exports metrics to Prometheus. In Grafana: OSD read and write latency, client I/O, NIC saturation and per-OSD usage. - Useful alerts: PGs degraded or in backfill for a long time,
nearfull,slow ops, down OSDs and clock skew between monitors. - Conservative thresholds: alert on usage with enough margin to rebuild a node, not when it is almost full.
Troubleshooting¶
| Symptom | Probable cause | Fix |
|---|---|---|
| High write latency on HDD | DB/WAL on the same spinning disk | Move DB/WAL to NVMe/SSD when recreating the OSD |
Repeated slow ops |
Saturated disk or network, misbehaving OSD | ceph -s, locate the affected OSD and check the node's disk and network |
| One OSD much fuller than the rest | Uneven PG distribution | Check ceph balancer status; enable the balancer or adjust weights |
nearfull warning |
Capacity near the limit | Add OSDs, delete data or reduce replicas only if the design allows it |
| Node swapping, slow OSDs | Sum of osd_memory_target exceeds RAM |
Lower osd_memory_target or reduce OSDs per node |
| Slow clients during recovery | Backfill competes with client traffic | Lower recovery priority and schedule changes off-peak |
| Rebalancing after rebooting a node | down_out_interval too low |
Raise it, or use noout for planned maintenance |
| Image space does not drop after deleting data | The guest does not send discard |
Enable discard and run fstrim |
Best Practices¶
- Measure before and after each change, one at a time, and keep the outputs.
- One pool per workload type, with
size=3andmin_size=2for what matters. - DB/WAL on NVMe when the main storage is spinning, with headroom in size.
- Calculate
osd_memory_targetfrom the node's real RAM, not from another cluster's figure. - Do not raise the
fullthresholds to gain space: add capacity before reaching them. - Use
nooutfor planned maintenance and remove it afterwards. - Check
ceph config help <option>on your version before copying settings from tutorials. - Test recovery on a lab cluster before you need it in production.