# Ceph CRUSH Weight Optimization for Mixed OSD Sizes — 2026-07-13

## Problem

When a cluster has OSDs of different sizes (1 TB and 3 TB), CRUSH weights
should be proportional to device size. But in practice, operators sometimes
manually lower CRUSH weights on small/full OSDs to reduce incoming data —
which creates a vicious cycle:

1. Small OSD fills up → operator lowers CRUSH weight
2. Lower weight → fewer new PGs assigned → old data stays
3. OSD stays full → operator lowers weight further
4. Large OSDs (especially new ones) remain underutilized

## Diagnosis

```bash
# Show all HDD OSDs with weight, reweight, utilization, PG count
ceph osd df tree | grep hdd

# Key columns to compare:
# WEIGHT  — CRUSH weight (should ≈ size_in_TB)
# REWEIGHT — runtime reweight (should be 1.0 normally)
# %USE    — utilization (should be roughly equal across OSDs)
# PGS     — PG count (should be proportional to weight)
```

### Symptoms of misaligned weights

| Signal | Meaning |
|--------|---------|
| WEIGHT << SIZE (e.g. 0.3 for 1 TB) | Artificially suppressed weight |
| %USE varies wildly (27% vs 94%) | Data not distributed proportionally |
| PGS count disproportional (150 vs 450) | CRUSH assigns PGs by weight, not size |
| New large OSD barely fills (2% after hours) | Weight correct but recovery slow |

## Solution

### Step 1: Identify correct CRUSH weights

Set CRUSH weight = device size in TiB (Ceph convention):

| Device Size | Correct CRUSH Weight |
|-------------|---------------------|
| 1 TB (982 GiB) | ~0.96 |
| 2 TB | ~1.82 |
| 3 TB (2.8 TiB) | ~2.73 |
| 3.6 TiB | ~3.64 |

```bash
# Check current weights
ceph osd tree | grep hdd
# Compare WEIGHT column to SIZE in: ceph osd df
```

### Step 2: Reset artificial weights (after recovery!)

⚠️ **Timing matters**: If a rebalance is already running, changing CRUSH
weights triggers ADDITIONAL remapping. Either:
- Wait for current rebalance to finish, THEN adjust weights, OR
- Adjust weights now and accept a longer combined rebalance

```bash
# Reset CRUSH weight to match device size
ceph osd crush reweight osd.7 0.96   # was 0.50, should be ~0.96 for 1 TB
ceph osd crush reweight osd.10 0.96  # was 0.30, should be ~0.96 for 1 TB

# Also reset runtime reweight to 1.0 (if it was lowered for emergency drain)
ceph osd reweight 7 1.0
ceph osd reweight 10 1.0
```

### Step 3: Let the upmap balancer handle fine-tuning

The `upmap` balancer module optimizes PG placement without full remapping:

```bash
# Check balancer status
ceph balancer status

# If "no_optimization_needed": false, the balancer is actively working
# If "optimize_result" mentions "too many objects misplaced": wait for
# current recovery to drop below 5% misplaced, then balancer kicks in
```

The balancer cannot run while >5% objects are misplaced (it skips to avoid
compounding recovery load). Once the initial rebalance from weight changes
completes, the balancer will fine-tune PG distribution automatically.

### Step 4: Optional — `reweight-by-utilization` for dynamic balancing

```bash
# Automatically reweight OSDs based on utilization (temporary reweights)
ceph osd reweight-by-utilization
# Or with custom threshold:
ceph osd test-reweight-by-utilization 120  # dry run, shows what would change
ceph osd reweight-by-utilization 120       # apply (120 = 1.2x average = overfull)
```

⚠️ Use cautiously — this changes runtime reweights, not CRUSH weights.
The effect is temporary and can interact with ongoing recovery.

## Mixed-Size Cluster Example

Real-world cluster with 1 TB and 3 TB HDDs:

| OSD | Size | Old Weight | New Weight | Old %Used | Expected %Used |
|-----|------|-----------|------------|-----------|----------------|
| osd.1 | 3.6 TiB | 3.64 | 3.64 (OK) | 33% | ~33% |
| osd.6 | 2.8 TiB | 2.76 | 2.76 (OK) | 27% | ~27% |
| osd.8 | 2.8 TiB | 2.73 | 2.73 (OK) | 27% | ~27% |
| osd.11 | 2.8 TiB | 2.76 | 2.76 (OK) | 2% | ~27% (filling) |
| osd.7 | 982 GiB | **0.50** | **0.96** | 94% | ~33% |
| osd.10 | 982 GiB | **0.30** | **0.96** | 57% | ~33% |

After resetting osd.7 and osd.10 to their true weights, CRUSH will assign
them proportionally more PGs, and data will distribute evenly across all
HDDs regardless of size. The 3 TB OSDs will naturally hold ~3× the data
of 1 TB OSDs.

## Pitfalls

### Don't lower weight to "protect" a full OSD

Lowering CRUSH weight on a full OSD prevents new PGs from landing there,
but does NOT move existing data away. The OSD stays full. Meanwhile, the
reduced weight means the OSD contributes less to the cluster's apparent
capacity, causing `nearfull`/`backfillfull` warnings on remaining OSDs.

Instead: use `ceph osd reweight` (runtime, temporary) for emergency drain,
then fix the root cause (add capacity, redistribute, or accept the OSD
size and set proper CRUSH weight).

### Balancer won't help during active recovery

The `upmap` balancer skips when >5% objects are misplaced. Don't expect
it to optimize placement while a major rebalance is in progress. Wait for
recovery to complete, then let the balancer run.

### CRUSH weight changes trigger remapping

Every `ceph osd crush reweight` causes CRUSH to recompute PG placements.
On a cluster with existing data, this triggers backfill. Schedule weight
changes during maintenance windows or combine with planned capacity additions.
