Files
esh-pfi-infrastructure/docs/runbooks/nh3-dev-io-stall.md
T
vh d4aa59a199 fix(backup): enable fleecing so a slow target cannot stall a guest
Operator-approved after last night's stall. Proxmox's own man vzdump names
both the mechanism and the remedy: without fleecing, guest writes block on
the backup target's speed; with it, pre-write copies land in a local image
instead.

Verified by behaviour rather than by config read-back, on a live backup that
was still crawling at the same 1.4 MiB/s that caused the outage:

  io pressure full avg10   96.5%  ->  0.01%
  in-flight writes         45, completing none  ->  0 0
  D-state processes        11  ->  0
  32 MiB dsync write       never completed  ->  0.24s (133 MB/s)

The backup is exactly as slow as before and the guest no longer cares. That
is the point, and also the limit: this contains the blast radius and does
not explain why a cross-site backup that once ran at 941 MiB/s now runs at
1.4, with the link up and pbs-ana answering in 11 ms.

Job config backed up to /root/jobs.cfg.bak-* on nh3-pve before the change.
2026-09-02 22:33:12 -07:00

5.3 KiB

A VM wedges on IO and the disk is innocent

First seen 2026-09-03, nh3-dev (VM 102 on nh3-pve). Reported by hamr-dev as "/dev/sda stalled — writes hang". It was the backup.

The signature

/proc/pressure/io     full avg10=96  (everything is IO-blocked)
/sys/block/sda/inflight        0 45  (45 writes in flight, zero completing)
D state       jbd2/sda1-8, flush-8:0 kworkers, systemd-journald, any writer
dmesg         "blocked for more than 120 seconds", virtio_ring in the trace

Looks exactly like a failing disk or controller. It is not.

What distinguishes it — and the checks are cheap

1. There are no device errors. No SCSI/ATA errors, no I/O error, no bus resets, pool ONLINE, SMART quiet. A failing disk says so. Absence of errors is evidence, not a gap in it.

2. Dirty in /proc/meminfo is ~zero while requests pile up in flight. If writeback were merely slow, dirty pages would be accumulating. Near-zero Dirty plus stuck in-flight requests means the writes were already submitted and are not being completed — the stall is below the block layer, not above it.

3. THE HYPERVISOR IS IDLE. Check it before touching the guest:

ssh root@nh3-pve 'cat /proc/loadavg; cat /proc/pressure/io; zpool iostat rpool 1 2'

Host load 0.63 and io pressure 0.00 while a guest is 96% IO-blocked means the guest's writes are not reaching the host at all. Something between them is holding the queue.

4. Then look at the block layer, which names the culprit outright:

ssh root@nh3-pve 'qm monitor <VMID> <<< "info block"'
drive-scsi0: /dev/zvol/rpool/data/vm-102-disk-0 (copy-before-write)   ← HERE

ssh root@nh3-pve 'qm monitor <VMID> <<< "info block-jobs"'
Type backup, device drive-scsi0: Completed 545 MB of 19.3 GB

copy-before-write is the filter Proxmox interposes during a backup: every guest write must first copy the original block out to the backup target before it may proceed. A slow or stalled target therefore throttles the guest to the target's speed. On 2026-09-03 that was 1.4 MiB/s, cross-site to pbs-ana, for 35 minutes.

Fix

ssh root@nh3-pve 'pvesh delete "/nodes/localhost/tasks/<UPID>"'

Cancels the backup and detaches the filter. Verify: info block shows the disk as (raw) again, info block-jobs says No active jobs, guest inflight returns to 0 0, D-states clear.

No reboot is needed and none helps. Rebooting the guest leaves the filter attached and the next writes stall the same way.

Cancelling is the standing call, per the fleet rule prefer no backup over one that can crash the service. An incremental backup with a dirty bitmap re-runs cheaply; an unusable dev box does not.

Why it can recur, and the real fix

The job is backup-5d8f1221-8f71: daily at 21:00, all 1, storage pbs-ana — every VM on the host, cross-site to Anaheim.

The job has fleecing 0. With fleecing enabled, copy-before-write writes land in a fast local fleecing image instead of going straight to the backup target, which decouples guest IO from target throughput. That is the difference between "the backup is slow tonight" and "the box is unusable tonight."

A previous run of this VM managed 941 MiB/s read, so 1.4 MiB/s is a degradation with its own cause — the cross-site link and pbs-ana were both responsive (11 ms) throughout, so the throughput cause is not yet known and enabling fleecing treats the blast radius, not the disease.

Fleecing was enabled 2026-09-03, and verified by behaviour

pvesh set /cluster/backup/backup-5d8f1221-8f71 \
  --fleecing 'enabled=1,storage=local-zfs'

Proxmox's own man vzdump states the mechanism and the fix outright:

The guest write blocks until this operation is finished so guest IO to not-yet-backed-up sectors will be limited by the speed of the backup target. [...] With backup fleecing, such old data is cached in a fleecing image rather than sent directly to the backup target. This can help guest IO performance and even prevent hangs in certain scenarios, at the cost of requiring more storage space.

Verify by behaviour, not by config. The config read-back only proves the key is set. Run a backup and measure the GUEST while it runs:

# host: topology should show BOTH filters
qm monitor <VMID> <<< "info block"
drive-scsi0           ... (copy-before-write)
drive-scsi0-fleecing  ... /dev/zvol/rpool/data/vm-<VMID>-fleece-0 (raw)   ← this

# guest: must stay responsive
cat /proc/pressure/io ; cat /sys/block/sda/inflight
dd if=/dev/zero of=$(mktemp) bs=1M count=32 oflag=dsync

Measured during a live backup that was still crawling at the same 1.4 MiB/s:

during the stall with fleecing
io pressure full avg10 96.5% 0.01%
in-flight writes 45, completing none 0 0
D-state processes 11 0
32 MiB dsync write never completed 0.24 s (133 MB/s)

The backup is exactly as slow as before. The guest no longer cares — which is the whole point, and the reason this is a blast-radius fix rather than a cure.

Fleecing costs disk. The image holds pre-write copies of changed blocks for the life of the job, on local-zfs (717 G free at the time). A long, slow backup of a busy guest is the case that grows it — watch zfs list rpool/data/vm-<VMID>-fleece-0 if the target stays degraded.