# Ceph Pool-Full Diagnosis & Recovery — 2026-07-04 ## Symptoms After adding CT 137's 20 GB RBD to `media_ec`, **6 pools simultaneously showed 100% full** (`MAX AVAIL = 0 B`) despite the HDD class having 6.3 TB free. ## Root Cause A single OSD (osd.10, 982 GB) reached exactly **95% utilization** = `full_ratio`. Ceph blocks ALL writes to PGs mapped to a full OSD. Since nearly every pool has PGs on osd.10, all pools became unwritable — even though osd.6 (2.8 TB) had 2.5 TB free. ### Why osd.10 Filled First CRUSH distributes data by OSD weight. osd.10 and osd.7 are ~1 TB disks; osd.6 and osd.8 are ~2.8 TB disks. The smaller OSDs filled faster because data grew unevenly. The `VAR` column in `ceph osd df` showed osd.10 at 2.13x average utilization vs osd.6 at 0.20x. ## Diagnosis Commands ```bash # Per-OSD utilization (look for %USE > 90% and high VAR) ceph osd df # Pool availability (MAX AVAIL = 0 means full) ceph df # Cluster health (look for "full osd" and "pool(s) full") ceph -s # Check full ratio threshold ceph osd dump | grep full_ratio # Default: full_ratio 0.95, nearfull_ratio 0.93, backfillfull_ratio 0.95 ``` ## Recovery Procedure ### Step 1: Raise full_ratio temporarily ```bash ceph osd set-full-ratio 0.97 ``` This unblocks writes immediately. Pools regain `MAX AVAIL > 0`. Safe as a temporary measure while rebalancing. ### Step 2: Drain overloaded OSDs ```bash # Automatic: reweight all overloaded OSDs at once ceph osd reweight-by-utilization # Moves PGs away from OSDs above average utilization threshold # Manual: target specific OSDs for aggressive draining ceph osd reweight 10 0.5 # drain to 50% of CRUSH weight ceph osd reweight 7 0.8 # moderate drain ``` ### Step 3: Speed up backfill (optional, moderate) ```bash # Allow 2 backfills per OSD (default: 1) ceph config set osd osd_max_backfills 2 # Or inject directly: ceph tell osd. injectargs "--osd_max_backfills=2" # Reduce recovery sleep (default: 0.1s) ceph config set osd osd_recovery_sleep 0.05 ``` **Tradeoffs of increasing backfill speed:** - Higher client I/O latency (VMs feel slower) - More "slow ops" warnings (we already had 4) - Higher CPU/IO load on stressed OSDs - May worsen BlueFS spillover **Recommendation:** Only double `osd_max_backfills` (1→2) and halve `recovery_sleep` (0.1→0.05). Don't go aggressive on production clusters. ### Step 4: Verify recovery ```bash # Watch recovery progress ceph -s | grep -A2 "io:" # recovery: 32 MiB/s, 8 objects/s # Track misplaced objects ceph -s | grep "misplaced" # 353960/1858594 objects misplaced (19%) # Estimate time: misplaced_objects / objects_per_second # Or: misplaced_data_bytes / recovery_rate_bytes_per_second ``` ### Step 5: Restore full_ratio after rebalance ```bash # Once OSDs are balanced (< 80% on all), restore default ceph osd set-full-ratio 0.95 ``` ## Backfill Blocking Issues ### backfill_toofull If a target OSD is too full to accept backfill data, PGs stay in `backfill_toofull` state. Fix: lower the reweight of the full OSD further, or raise `backfillfull_ratio`: ```bash ceph osd set-backfillfull-ratio 0.97 ``` ### injectargs not sticking `ceph tell osd.N injectargs` may not reliably set all parameters. `osd_recovery_sleep` in particular gets reset to 0. Use `ceph config set osd` for persistent values, and verify with `ceph tell osd.N config get `. ### CRITICAL: `ceph config set osd osd_max_backfills` does NOT apply at runtime Setting `ceph config set osd osd_max_backfills 3` updates the MON config store, but **already-running OSD daemons keep using their old value (1)**. The change only takes effect on next OSD restart. Two ways to apply at runtime: **Method A — `ceph tell osd.N injectargs` (runtime, no restart):** ```bash # Apply to each HDD OSD individually ceph tell osd.7 injectargs "--osd_max_backfills 3" ceph tell osd.1 injectargs "--osd_max_backfills 3" ceph tell osd.10 injectargs "--osd_max_backfills 3" # For OSDs on other nodes, SSH there first or use ceph tell remotely # ⚠️ Output is CONFUSING — prints all related config vars with empty values like: # osd_max_backfills = '' osd_recovery_max_active = '' ... # But the change DID take effect. Verify with: ceph daemon osd.7 config get osd_max_backfills # Should return: {"osd_max_backfills": "3"} ``` **Method B — Restart OSD daemon (pick up MON config):** ```bash # ⚠️ MUST restart on the correct node! Use ceph osd find to locate: ceph osd find 7 # Returns JSON with "host": "proxmox7" and IP # Then SSH to THAT node and restart: ssh proxmox7 "systemctl restart ceph-osd@7" # ❌ WRONG: restarting on a node that doesn't host the OSD → # "OSD data directory /var/lib/ceph/osd/ceph-7 does not exist; bailing out" ``` **Verification — check runtime value on each OSD:** ```bash ceph daemon osd.N config get osd_max_backfills # Returns actual runtime value. If still "1", the config hasn't been applied. ``` ### `osd_recovery_max_active` cannot be effectively changed `ceph config set osd osd_recovery_max_active 3` accepts the value but OSDs report 0 (meaning auto/default). `injectargs` also shows empty. This appears to be a Ceph limitation — the parameter is managed internally. Do not waste time trying to force it. ### Also raise `nearfull_ratio` alongside `backfillfull_ratio` When an OSD is above 93%, it triggers `nearfull` warnings which add additional pool-level flags (`nearfull` on pools). If you only raise `backfillfull_ratio` to 0.97 but leave `nearfull_ratio` at 0.93, pools still show warnings and recovery may be partially blocked. Raise both together: ```bash ceph osd set-backfillfull-ratio 0.97 ceph osd set-nearfull-ratio 0.97 # Verify: ceph osd dump | grep -E "nearfull_ratio|backfillfull_ratio" ``` ### Recovery speed progression After applying all tweaks, recovery speed increases progressively as OSDs pick up new config: | Stage | Recovery Speed | Notes | |-------|---------------|-------| | Before intervention | 0 MiB/s (stuck) | backfillfull blocks all backfill | | After ratio increase + reweight | 22-23 MiB/s, 5 obj/s | 1-2 PGs backfilling | | After osd_max_backfills=3 via injectargs | 34 MiB/s, 8 obj/s | Still only 2-3 PGs simultaneous | Even with `osd_max_backfills=3`, expect only 2-3 PGs backfilling simultaneously — Ceph schedules conservatively. The throughput improvement comes from larger transfer windows, not more parallel PGs. ### Recovery monitoring cron pattern Set up a recurring check to track progress and get alerted when complete: ``` Schedule: every 30 minutes Command: ssh to monitor node, run `ceph status`, report: - PGs remapped/backfilling count - % misplaced objects - OSD 7 utilization - Recovery speed - Alert if any OSD down or health ERR - Announce "COMPLETE" when 0 remapped PGs ``` ## Estimating Backfill Duration | Metric | Formula | Example | |--------|---------|---------| | By objects | misplaced_objects ÷ objs/sec | 353,960 ÷ 8 = ~12h | | By bytes | misplaced_bytes ÷ bytes/sec | 418 GB ÷ 32 MiB/s = ~3.6h | | Realistic | Take the higher estimate, factor in PG scheduling | 4-8h | Only a few PGs backfill simultaneously (controlled by `osd_max_backfills`). 192 PGs in `backfill_wait` won't all start at once. --- # OSD Preparation on Proxmox Nodes — 2026-07-04 ## Hardware Assessment Pattern Before adding HDD OSDs to a node, verify: 1. **Available SATA/SAS ports:** ```bash lspci | grep -i "sas\|sata\|nvme\|raid\|hba" ls /sys/class/ata_port/ dmesg | grep -i "ahci\|ata[0-9]" ``` ⚠️ AHCI controllers may show N ports but only implement a subset. Check dmesg for `"ports implemented (port mask 0xN)"`. An OptiPlex 3070 showed 5 SATA ports but only port 0x1 (1 port) was implemented. 2. **Solution for limited ports:** Install a PCIe HBA card (LSI 9211-8i / 9207-8i in IT mode) to connect additional SATA/SAS drives. 3. **Existing OSD inventory:** ```bash ceph osd tree | grep -A20 "host " ceph-volume lvm list ``` ## WAL/DB Space Preparation ### Identifying available flash space ```bash # LVM VG free space on system disk vgs pve lvs pve # Check if thin pool is unused (safe to remove) pvesm list local-lvm # if storage doesn't exist, thin pool is orphaned lvdisplay pve/data # check allocation % ``` ### Repurposing unused thin pool for WAL/DB If `pve/data` thin pool exists but is 0% allocated and not registered as a PVE storage, it can be removed to free NVMe space: ```bash # Remove unused thin pool lvremove pve/data lvremove pve/data_tmeta lvremove pve/data_tdata # Or simply: lvremove pve/data (removes associated meta) # Now VG has ~141 GB free for WAL/DB volumes # Create DB LVs for new OSDs lvcreate -L 30G -n osd-db- pve ``` ### Rule: No loopback files for DB/WAL Per user policy: Ceph DB/WAL must use real partitions or LVs, never loopback files. Options: - Repurpose swap partition (if RAM is adequate) - Shrink root LV (risky) - Remove unused thin pools - Add a physical SSD/NVMe ## Node Checklist for Adding OSDs 1. ☐ Verify physical SATA/SAS ports available (or install HBA) 2. ☐ Verify disks are SMART-clean (`smartctl -a /dev/sdX`) 3. ☐ Prepare WAL/DB space on flash (real partition/LV, no loopback) 4. ☐ Create DB LVs: `lvcreate -L 30G -n osd-db-N pve` 5. ☐ Create OSDs: `ceph-volume lvm create --data /dev/sdX --block.db pve/osd-db-N` 6. ☐ Verify OSD joins cluster: `ceph osd tree` 7. ☐ Check rebalance: `ceph -s` 8. ☐ Set `osd_max_backfills` back to 1 after rebalance completes