Ceph MDS_CACHE_OVERSIZED: cache above limit and the cap recall that follows
MDS_CACHE_OVERSIZED fires when an active Metadata Server daemon’s in-memory inode and capability cache crosses mds_cache_memory_limit * mds_health_cache_threshold. It is almost always workload-driven: a client has touched enough files in a short enough window that the MDS is caching more metadata than its budget allows.
The warning itself is benign for a few seconds. What matters is the cascade. To shrink its cache, the MDS revokes client capabilities (caps), one batch per session per second, capped by mds_recall_max_caps (default 30,000). If clients keep acquiring caps on new inodes faster than the MDS can recall them, you see a second warning (MDS_CLIENT_RECALL), the recall throttle counters climb, ceph_mds_mem_rss keeps rising, and at the end of the path sits client eviction.
The terminal state is not the cache warning. It is one or more CephFS clients blacklisted because they failed to return enough caps within session_timeout. From the client side that looks like I/O suddenly failing with EBLACKLISTED. From the operator side it looks like a “slow client” eviction log entry, hours after the original MDS_CACHE_OVERSIZED was dismissed as noise.
What this means
The MDS keeps the hot portion of the CephFS namespace in RAM: inodes, dentries, directory fragments, open file metadata, and the per-client capabilities that allow clients to cache reads and writes safely. Every inode cached on the MDS side typically has matching state on one or more clients, gated by caps. The cache is not an MDS-local structure; it is a distributed cache coordinated through cap messages.
mds_cache_memory_limit (default 4 GiB as of Reef/Squid) is a soft budget. The MDS does not hard-stop at it; it starts trimming. The actual recall trigger is mds_cache_reservation (default 0.05, meaning recall begins at 95% of the limit). When the cache crosses 95%, the MDS starts asking clients to return caps so it can evict the corresponding inodes. mds_health_cache_threshold (default 1.5) is only the warning threshold: if the cache still reaches 150% of the limit, MDS_HEALTH_CACHE_OVERSIZED fires.
That gap, between 95% and 150%, is where most incidents actually live. By the time the warning fires, the recall machinery has already been running flat-out and losing. Two more signals usually accompany it:
MDS_CLIENT_RECALLactive, meaning a client is failing to release caps at the rate the MDS wants.ceph_mds_server_session_recall_throttleandceph_mds_server_global_recall_throttlecounters rising, meaning the MDS is bumping into its own recall throttles (mds_recall_max_capsper session, decay-limited globally).
If the gap is not closed, ceph_mds_server_cap_revoke_eviction ticks upward as the MDS evicts clients that exceed mds_max_caps_per_client (default 1M ) or miss session_timeout (default 60s).
flowchart TD
A["Client scans tree
find, rsync, du"] --> B["Cache crosses
95% of limit"]
B --> C["MDS recalls caps
mds_recall_max_caps per session"]
C --> D{"Recall rate beats
acquisition rate?"}
D -- yes --> E["Cache shrinks
warning clears"]
D -- no --> F["MDS_CACHE_OVERSIZED fires
at 150% of limit"]
F --> G["MDS_CLIENT_RECALL fires
recall_throttle counters rise"]
G --> H["Client crosses
mds_max_caps_per_client or
misses session_timeout"]
H --> I["Client evicted
EBLACKLISTED on client"]The fork at “Recall rate beats acquisition rate?” is the decision point. The rest of the article is about determining which side you are on and what to do about it.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Recursive scan workload | MDS_CACHE_OVERSIZED correlates with a find, rsync, du -sh, or backup job. One client session dominates ceph_mds_caps. | ceph tell mds.<id> session ls for the session with the largest num_caps. |
| Genuine working set larger than cache | Steady-state ceph_mds_mem_rss near the limit even with no scans. MDS_CLIENT_RECALL flickers but does not escalate. Slow but sustained recall throttle rate. | Compare ceph_mds_inodes_with_caps against mds_cache_memory_limit. |
| Slow or stuck client | One or two sessions have many recalled_caps and old recall timestamps. Cache size flat; the MDS is waiting on them. | ceph tell mds.<id> session ls for stale recall timestamps; client-side logs for cap return stalls. |
| Standby-replay false positive | Warning on a standby-replay MDS with “0 inodes in use by clients, 0 stray files” in the health message. No active sessions on that daemon. | ceph fs status and the rank state of the warning daemon. |
mds_cache_memory_limit misconfigured under Rook | Memory request set in the CephFilesystem CRD but no limit; effective cache limit stays at the 4 GiB default. | ceph config get mds mds_cache_memory_limit on the MDS host. |
Quick checks
Run these read-only on the cluster and on the active MDS host. None of them modify state.
# Top-level: is the warning still active, and which daemon is firing it?
ceph health detail | grep -E 'MDS_CACHE_OVERSIZED|MDS_CLIENT_RECALL'
# Cache size and inode pressure for the MDS
ceph tell mds.<id> cache status
ceph tell mds.<id> perf dump | jq '.mds_mem'
# Live sessions, sorted by caps held (look for the dominant client)
ceph tell mds.<id> session ls | jq 'sort_by(-.num_caps) | .[] | {id, num_caps, recalled_caps}'
# Effective config on this daemon (compare against what you think you set)
ceph config get mds mds_cache_memory_limit
ceph config get mds mds_cache_reservation
ceph config get mds mds_recall_max_caps
ceph config get mds mds_max_caps_per_client
# Confirm the daemon rank: is this the active MDS, or standby-replay?
ceph fs status
# Process memory for ground truth (rss in KB)
ps -eo pid,rss,args | grep ceph-mds
If the warning daemon is standby-replay and ceph health detail reports zero inodes in use, skip to the “Standby-replay false positive” subsection. This is a documented false positive on Quincy (17.2.x) and earlier, tracked in Red Hat Bugzilla #1951348 and #1944148 .
How to diagnose it
- Confirm the cache is actually oversized on an active MDS. Cross-check
ceph tell mds.<id> cache statusagainstmds_cache_memory_limit. If the daemon isstandby-replay, treat it as the false-positive case regardless of the cache size value. - Find the dominant session. Sort
session lsbynum_caps. In the scan-driven case, one or two client addresses account for the bulk of the caps. Note those IPs. - Check whether recall is making progress. Pull
ceph_mds_caps,ceph_mds_inodes_with_caps,ceph_mds_server_session_recall_throttle, andceph_mds_server_global_recall_throttleover a few minutes. Ifceph_mds_capsis flat or rising while the throttle counters rise, recall is losing. - Look for the eviction precursor. Check
increase(ceph_mds_server_cap_revoke_eviction[5m]). Any non-zero value means the MDS has already evicted at least one client; expectEBLACKLISTEDerrors on the client side and entries inceph osd blocklist ls. - Identify the workload. The dominant client IP from step 2 should map to a host you can inspect. The usual suspects are
find /, recursiversync, backup jobs, indexing services, and CI artifact pipelines. A shortperf dumpbaseline before and after the job starts shows the cap acquisition rate. - Rule out the misconfiguration cases. Verify
mds_cache_memory_limitis what you expect, especially under Rook, where a pod memoryrequestwithout alimitdoes not propagate to the MDS. The behavior is tracked as rook/rook#8143 .
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
ceph_health_detail{name="MDS_HEALTH_CACHE_OVERSIZED"} | Cache above 150% of the configured limit. MDS is already recalling and losing. | Active for more than a few minutes. |
ceph_health_detail{name="MDS_CLIENT_RECALL"} | A client is failing to return caps fast enough. | Active at all. Correlates with the next eviction. |
ceph_mds_mem_rss | Ground-truth memory consumption of the MDS daemon. | Trending toward host RAM limit; OOM kill is the cliff. |
ceph_mds_caps | Total caps granted to all clients. Proxy for the distributed cache size. | Rising while MDS_CLIENT_RECALL is active. |
ceph_mds_inodes_with_caps | Inodes pinned in the MDS cache by outstanding caps. | Tracking toward the configured limit. |
ceph_mds_server_session_recall_throttle (counter) | Per-session recall batches limited by mds_recall_max_caps. | Monotonic increase during the incident. |
ceph_mds_server_global_recall_throttle (counter) | Global recall rate-limiter hits. | Monotonic increase during the incident. |
ceph_mds_server_cap_revoke_eviction (counter) | Clients evicted for not returning caps. | Any non-zero value. Use increase() over a short window. |
ceph_mds_reply_latency_sum / _count | End-to-end MDS request latency. | Rising while cache pressure rises; client-visible stall. |
ceph_mds_slow_reply (counter) | MDS replies breaching the slow-reply threshold. | Any non-zero value over a 5-minute window. |
Fixes
The fix is one of: raise the limit because the working set genuinely needs it, change the workload so it does not pin the whole namespace, or address the specific edge case (slow client, standby-replay false positive, Rook misconfiguration). Restarting the MDS is almost never the right first move.
Raise mds_cache_memory_limit for a genuine working set
If ceph_mds_inodes_with_caps is consistently near the limit outside of any scan, and MDS_CLIENT_RECALL is steady background noise rather than an escalation, the working set is larger than the budget. Cache-related options are runtime-updatable; no MDS restart is required.
# Inspect current value
ceph config get mds mds_cache_memory_limit
# Raise it (example: 8 GiB). Apply live.
ceph config set mds mds_cache_memory_limit 8589934592
Two cautions. First, the MDS host must have the RAM. ceph_mds_mem_rss should track the new limit within a few minutes; if it does not, the config did not apply (re-check with ceph config get mds ... on the host). Second, under Rook, set the value through the CephFilesystem CRD’s metadataServer.resources.limits.memory, not only via ceph config set, otherwise the next reconcile may reset it.
Throttle the scan, do not throttle the MDS
If a find, rsync, or backup job is the trigger, the right fix is on the workload side. The variable that matters is the metadata-op rate, not CPU or data-IO priority, so nice/ionice alone usually do not help; you need to slow the directory-walk rate itself.
- Add a small
sleepbetween directory descents in scan scripts (find ... -exec sh -c '...' \;with a delay, or a rate-limited walker). This is the only reliable way to drop the cap acquisition rate below the recall rate. - Replace
rsync -a /src/ /dst/over the whole tree with targeted syncs, or use--updateplus a manifest to avoid re-scanning unchanged subtrees. - For backup systems that walk CephFS, prefer CephFS snapshots (
ceph fs snap) as the backup source instead of a live walk. Snapshots do not pin caps the same way. - Schedule scans during low client-count windows so per-client cap pressure is bounded by
mds_max_caps_per_client.
If you must tune the MDS to absorb a known one-time scan, raise mds_recall_max_caps (per-session recall batch size, default 30,000). This lets the MDS recall more caps per session per second, which can close the gap on scan-driven incidents. The tradeoff is more recall traffic on the MDS-to-client link.
# Increase per-session recall batch (example: 50,000)
ceph config set mds mds_recall_max_caps 50000
Tune in small steps and watch ceph_mds_server_session_recall_throttle fall while ceph_mds_caps falls.
Address a slow or stuck client
If session ls shows one or two clients with large recalled_caps counts and old recall timestamps, the MDS is waiting on them. Investigate the client host:
- Kernel CephFS client: check
dmesgfor cap-related stalls, look at client memory pressure, verify the client is not itself under OOM. - FUSE client: check the client process CPU and memory.
- Network: cap recall messages share the same path as data. Packet loss or saturation between the client and the MDS will manifest as slow recall.
If the client is genuinely wedged, manual eviction is a last resort. The client will see EBLACKLISTED; clear the blocklist entry once the client has remounted and the workload is no longer stuck.
Standby-replay false positive
If ceph health detail shows MDS_HEALTH_CACHE_OVERSIZED on a standby-replay daemon with “0 inodes in use by clients, 0 stray files,” this is the documented false positive. The standby-replay daemon replays the active MDS’s journal and accumulates metadata without having any clients to recall caps from, so it cannot trim. Restarting the standby-replay MDS clears the warning with no client impact:
<!-- TODO: verify exact systemd unit name. Under classic deploy this is ceph-mds@<daemon-id>.service; under cephadm it is ceph-<fsid>@mds.<host>.<name>. Adjust to your deployment. -->
# Safe: only the standby-replay daemon is affected. Active MDS continues serving.
systemctl restart ceph-mds@<standby-replay-daemon-id>
If this fires repeatedly, the underlying issue is tracked at tracker.ceph.com/issues/48673 ; consider reducing journal size or filing a bug against your Ceph version.
Prevention
- Size
mds_cache_memory_limitfor the working set, not the average. Measureceph_mds_inodes_with_capsduring peak load and set the limit with at least 30% headroom. The 4 GiB default assumes a modest namespace; large CephFS deployments routinely run at 16 to 32 GiB. - Alert on
ceph_mds_mem_rsstrending toward host RAM, not just onMDS_CACHE_OVERSIZED. The warning fires at 150% of the configured cache limit; OOM fires at host RAM. The gap between those two is your runway. - Track
ceph_mds_server_cap_revoke_evictionas a counter and alert on any increase. Evictions are user-visible incidents; they should never be a surprise. - Gate backup and indexing jobs that walk CephFS. Treat any process that does
find /orrsync /against CephFS the way you would treat a full table scan in a database: require an approval, schedule it, and rate-limit the walk. - If you run multi-active MDS (
max_mds > 1), monitor per-rank cache pressure separately. Subtree partitioning does not guarantee balanced cap distribution; one rank can hitMDS_CACHE_OVERSIZEDwhile others are idle. - Under Rook, set both
requestsandlimitsfor the MDS pod. Verify the effectivemds_cache_memory_limitafter deploy, not just the CRD value. - Review recall throttle settings when the cluster’s client count or namespace grows. Defaults tuned for a small cluster will starve recall on a large one.
How Netdata helps
- The
ceph_health_detailmetric with thenamelabel lets you alert onMDS_HEALTH_CACHE_OVERSIZEDandMDS_CLIENT_RECALLindependently. Both firing on the same MDS rank is the strongest signal that the cascade has started. ceph_mds_capsandceph_mds_inodes_with_capsper MDS daemon show the distributed cache size directly. A rising curve against a flatmds_cache_memory_limitis the leading indicator, before the health check fires.ceph_mds_server_session_recall_throttleandceph_mds_server_global_recall_throttlecounters, viewed as rates, show whether the recall machinery is saturated. Flatceph_mds_capswith rising throttle counters is the “recall is losing” signature.ceph_mds_server_cap_revoke_evictionviewed asincrease(...[5m])is the eviction alarm. Any non-zero value means clients are already seeingEBLACKLISTED.- Correlating
ceph_mds_reply_latencyandceph_mds_slow_replyagainst the cache-pressure metrics separates an MDS that is merely hot from one that is stuck behind its own recall throttle. ceph_mds_mem_rssnext to the host’s available memory gives the OOM runway directly. Per-second resolution catches the sharp ramp that precedes an OOM kill, which the 150%-of-limit health check does not see.
Related guides
- Ceph backfill_toofull: recovery blocked because target OSDs are full
- Ceph blocked ops: client I/O stuck behind a single slow OSD
- Ceph BlueStore RocksDB compaction stalls: periodic latency spikes
- Ceph BLUEFS_SPILLOVER: RocksDB metadata spilling onto the slow device
- Ceph BlueStore allocator fragmentation: rising latency at moderate fullness
- Ceph capacity death spiral: an OSD fails and recovery has nowhere to go
- Ceph client latency vs OSD latency: fast disks, slow clients
- Ceph deep scrub performance impact: I/O saturation that mimics an incident
- Ceph degraded objects: reduced redundancy and the race against a second failure
- Ceph health detail: mapping ceph_health_detail checks to a cause
- Ceph HEALTH_ERR: reading the umbrella status and finding the real fault
- Ceph HEALTH_WARN: which warnings are noise and which are structural






