CephCIV · Multi-Tenancy in PracticeMulti-Tenancy in Practice
What happens when a tenant hits their limit
What you'll learn
- Describe the behaviour at each quota layer
- Alert before the limit rather than at it
- Respond to a tenant at their limit
- Choose limits that fail gracefully
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
Quotas behave very differently by layer, and the RADOS pool quota is the one that fails hardest.
Behaviour at each layer
| Layer | At the limit | Client sees |
|---|---|---|
Pool quota (max_bytes) | writes blocked | RBD I/O hangs; RADOS writes block |
| CephFS subvolume quota | writes refused | EDQUOT — disk quota exceeded |
| RGW user or bucket quota | writes refused | HTTP 403 QuotaExceeded |
| RBD image size | writes beyond the end refused | the filesystem sees a full device |
ceph osd pool get-quota tenant-acme
ceph df detail | grep -A2 tenant-acme
The pool quota is the harsh one: for RBD, a blocked write is a hung
I/O, and a guest filesystem in uninterruptible wait does not report
"quota exceeded" — it stops.
ceph -s
ceph health detail | grep -i -E 'quota|full'
Alerting before the limit
The alert that matters fires at a threshold below the limit, not at it.
- alert: TenantPoolQuotaApproaching
expr: |
(ceph_pool_bytes_used / on(pool_id) ceph_pool_quota_bytes) > 0.80
and on(pool_id) ceph_pool_quota_bytes > 0
for: 30m
labels: { severity: warning }
annotations:
summary: "Pool {{ $labels.name }} is above 80% of its quota"
- alert: TenantPoolQuotaCritical
expr: |
(ceph_pool_bytes_used / on(pool_id) ceph_pool_quota_bytes) > 0.95
and on(pool_id) ceph_pool_quota_bytes > 0
for: 10m
labels: { severity: critical }
# a manual sweep of every quota'd pool
ceph df detail --format json | python3 -c '
import sys,json
for p in json.load(sys.stdin)["pools"]:
q = p.get("stats", {}).get("quota_bytes") or 0
u = p.get("stats", {}).get("stored", 0)
if q:
print("%-24s %6.1f%% %8.1f / %8.1f TiB" %
(p["name"], 100.0*u/q, u/1024**4, q/1024**4))'
Responding to a tenant at their limit
ceph osd pool get-quota tenant-acme
ceph df detail | grep tenant-acme
| Situation | Response |
|---|---|
| Growth was expected | raise the quota; capacity exists |
| Growth was not expected | investigate before raising |
| The cluster has no headroom | raising the quota moves the problem |
| The tenant can delete data | let them; the block clears |
| The tenant is stuck in hung I/O | raise the quota to unblock, then investigate |
ceph osd pool set-quota tenant-acme max_bytes $((15 * 1024**4))
Raising a pool quota takes effect immediately and unblocks hung writes,
which makes it the fastest way to end an incident — and the reason to
have an approval path agreed in advance rather than during one.
Choosing limits that fail gracefully
Prefer a limit whose breach produces an error over one that produces a
hang.
| Preference | Why |
|---|---|
| RBD image size as the tenant limit | the guest filesystem reports full |
| CephFS subvolume quota | EDQUOT is a normal error applications handle |
| RGW quota | 403 is a normal S3 response |
| Pool quota as a backstop only | it hangs I/O; use it above the per-tenant limits |
Set the pool quota as a cluster-protecting ceiling and enforce the
tenant's actual limit at the layer that returns an error.
Quiz
Knowledge check · 4 questions
Q1. What does a guest filesystem experience when its RBD pool reaches its quota?
Q2. A tenant limit belongs above the pool layer, because that is where exhaustion produces an error a client can act on.
Q3. Design tenant limits that fail gracefully.
Tenants receive RBD storage. The current design enforces each tenant's capacity with a pool quota on their pool.
Q4. Why should a quota alert fire below the limit rather than at it?
Passing score: 75%. Answers are checked in this browser.
Production discipline
Set the tenant’s effective limit at a layer that returns an error — RBD image size, subvolume quota, or RGW quota — and keep the pool quota as a cluster-protecting ceiling above it. A pool at quota hangs RBD I/O rather than erroring, and guests cannot handle that.
Cross-course references
- Kubernetes: a limit that OOM-kills differs from one that rejects admission
- Linux: a limit that blocks is operationally worse than one that errors