Files
felhom.eu/REPORT.md
T
admin 172584c26f
gates / gates (push) Failing after 14s
REPORT: record CI run 348 by id, and what could not be read
Checklist item "confirm your own push's CI run by run ID". Run 348,
head_sha ebfd0967c, conclusion failure, elapsed 13 s -- the inherited
golden-currency conviction (R-334), not a new fault: CI's only step is the
same repo_gates.py --fast entry point, and 13 s is the honest-failure band
rather than R-265's reap band.

Stated plainly that the run LOG could not be read (runs/348/logs and
tasks/348/logs both 404 authenticated as admin, runs/348/jobs empty, web
endpoint 302), so naming the gate is an inference from the local run plus
gates.yml -- not CI's own words. Standing rule 2: a "no access" claim names
what was tried.

Also notes the [felhom CI] gates FAILED mail run 348 will send, so it is not
read as a second incident alongside this morning's backup alerts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016p1PTCzb8rF5G9Aa1qhpBN
2026-08-18 06:11:29 +02:00

8.2 KiB

REPORT — a listening socket that served nobody (2026-08-18, early)

Trigger: two whole_guest_backup_failed alert mails, 04:30 and 04:32 CEST. Outcome: root cause found on ep0, fixed, both missed backups re-driven and landed. No repo code changed — this was an operational run. Documentation, register and evidence only.


1. What was wrong

Both alerts were the same incident and neither was on a customer box. demo-felhom and demo-hp each failed their felhom-pbs tier with Can't connect to 10.77.0.1:8007 (Connection timed out) — the one thing they share, the Hetzner offsite PBS endpoint.

On ep0, proxmox-backup-proxy was active, held its listening socket, and served nobody:

ss -lnt '( sport = :8007 )'  →  LISTEN  Recv-Q 1025  Send-Q 1024
ls /proc/<proxy>/fd | wc -l  →  1024        # == its soft RLIMIT_NOFILE

Send-Q on a listener is the accept backlog; Recv-Q is the queue depth. At 1025 against 1024 the queue had overflowed, because accept() was returning EMFILE on every call. 1016 of the 1024 descriptors were sockets and 547 connections sat in CLOSE-WAIT — a connection leak, fed by ~85,000 requests/day, that reached the ceiling after 14 days of uptime. Last request served: 2026-08-17 18:15:32 UTC. Offsite DR was therefore down ≈ 9 h 37 m.

The observation that settled it: the daemon was wedged from its own loopback too — curl https://127.0.0.1:8007/ on ep0 timed out. A listener that cannot serve 127.0.0.1 has no network left to blame, and that check is cheap enough to make early.

Full record, including everything that was ruled out first (tunnel, nftables, disk, dead daemon): documentation/audits/INCIDENT-ep0-pbs-fd-exhaustion-2026-08-18.md.

2. What was done

  1. LimitNOFILE=65536 drop-ins for proxmox-backup-proxy.service and proxmox-backup.service, each carrying its reason inline. The API daemon was not implicated (15 fds) and its drop-in says so — a later reader must not mistake it for a second culprit.
  2. Restarted both. After: soft limit 65536, fds back to 18, Recv-Q 0, loopback 200.
  3. Verified from the customer side, not only from ep0 — both boxes got 200 in ~0.1 s and pvesm status read felhom-pbs pbs active.
  4. Re-drove the missed backups through the product pathPOST /backup?target=felhom-pbs on each agent's local API, issued from inside the guest's controller container with the controller's own credentials, i.e. the same call the scheduler makes. Not a hand-run vzdump.

Result: demo-felhomct/9201/2026-08-18T03:57:43Z (4.10 GB, 36.4 s); demo-hpct/9201/2026-08-18T03:58:43Z (4.29 GB, 41.5 s). Both directories carry a full manifest on ep0, and both hosts log the re-run vzdump as OK. And the hub agrees — both boxes' next host reports carry felhom-pbs success=true (04:00:33Z and 04:07:35Z), so the operator view went green on the evidence rather than on the fact that a restart was performed. No data was lost and no backup was skipped — the daily local tier was never affected (it completed on both boxes at 05:00 and 05:02), and the PBS tier is weekly, so the outage window cost exactly one attempt, which was re-driven the same morning.

Evidence copied off ep0 before the restart, per standing rule 5: documentation/audits/evidence-ep0-fd-2026-08-18/ — pre-restart state, post-fix state, access-log tail.

3. What I got wrong, and corrected

  • proxmox-backup-manager version prints available then running. It read 4.2.5-1 running version: 4.2.2, which looks exactly like a daemon left behind by a package upgrade. It is not: dpkg -l shows 4.2.2-1 installed, 4.2.5-1 merely available in the repo, and the on-disk binary is dated 2026-06-18. I restarted the API daemon on that mistaken reading; harmless, and its drop-in is a genuine improvement, but it was not needed.
  • I addressed demo-hp's agent at the island address because operations/nodes.md says that box is island-migrated. It is not (R-338), and the resulting timeout was briefly read as a fault.

4. Findings filed — R-336, R-337, R-338

All three are in documentation/backlog/OPEN-ITEMS.md with numbers, per the registers-first rule.

  • R-336 — the poll rate is the real defect. ~1 request/second against a DR endpoint written to weekly. LimitNOFILE raises the ceiling; it does not fix the leak, it converts a fortnightly outage into a multi-year one.
  • R-337 — a status endpoint that trailed its own artifact, then caught up. WATCHING, not a defect. demo-hp's /backup/status was still serving the superseded 03:27:00Z failure at ~04:03Z while the snapshot sat on ep0 and the host logged OK; demo-felhom updated within ~40 s. I filed this as a defect and that was premature — the next host report (04:07:35Z) carried the success and the skew cleared with no intervention. Rewritten as WATCHING, with the explicit instruction not to open a fix until someone establishes whether this is just collection cadence. Recorded at all because during the recovery it read as a second failure, and it was not one.
  • R-338 — demo-hp is not on the R-50 island and nodes.md says it is. No island_bridge keys, guest has no eth1, vmbr9 has zero members, and the agent's local API is bound to the customer LAN — the exposure R-50 existed to remove.

Not done, deliberately: PBS 4.2.5-1 was not applied. Upgrading a production offsite endpoint was outside what this run was authorised to do, and its changelog should be read for the connection- handling leak first.

5. Gates — one pre-existing conviction, and a stated bypass

python3 scripts/repo_gates.py --fastrc=1, CONVICTED: golden-currency. Eight of nine gates pass. The conviction is R-334, inherited and not caused here: newest released controller 0.216.0, newest golden bake 0.214.0, so a new install misses two releases. The gate reads felhom-controller/CHANGELOG.md and documentation/tests/golden-*this session touched neither, and its whole diff is documentation. Baking is possible; vouching is operator-password-gated, and a baked-but-unvouched golden is worse than none, so it is not a one-sided job CC can finish.

This push therefore used git push --no-verify, stated here per .claude/rules/gates.md. R-334 is updated in the register with the new numbers rather than left reading 0.215.0.

CI checked by run ID, as the checklist requires: run 348, head_sha ebfd0967c, conclusion failure, elapsed 13 s (04:09:45→04:09:58Z). Expected and inherited — CI's only step is python3 scripts/repo_gates.py --fast, the same entry point that convicts golden-currency locally, with the sibling repos fetched. The 13 s runtime places it in the workflow's own "honest gate failure" band rather than the R-265 reap band, so the result is the gate speaking, not the runner. I could not read the run log to name the gate from CI's own mouthactions/runs/348/logs and actions/tasks/348/logs both 404, actions/runs/348/jobs returns an empty list, authenticated as admin, and the web log endpoint 302s. So this is an inference from the local run plus the workflow definition, not a direct reading, and it is stated as such.

Expect one [felhom CI] gates FAILED in admin/felhom.eu mail for run 348 — the workflow alarms on failure by design. It is this push, and it is the golden-currency row, not a new fault; the same mails on 12 and 14 August have the same cause.

(Noted for accuracy: the first gate run was piped to tail, which returned rc=0tail's exit code, not the gate's. It was re-run unpiped to read the real rc=1. That is standing rule 1's trap in its smaller form, and the number reported above is the unpiped one.)

6. What to watch

The positive observable is the descriptor count, not the absence of an alert — an empty alert queue is equally consistent with "healthy" and "wedged again":

ssh root@<ep0> 'PID=$(systemctl show proxmox-backup-proxy -p MainPID --value); \
  ls /proc/$PID/fd | wc -l; ss -lnt "( sport = :8007 )"'

Healthy is ~20 fds and Recv-Q 0. A count climbing between restarts means R-336's leak is still live.