Files
felhom-controller/REPORT.md
T
2026-06-13 16:44:02 +02:00

4.3 KiB

REPORT — felhom-controller v0.58.0 (infra-protection prevention layer for the OS/Docker-data split)

Phase 2 of the OS/Docker-data storage-split slice (Phase 1 = felhom-agent v0.29.0: golden + provision). The controller guest's OS rootfs and Docker data are now split onto separate local-lvm volumes for resilience; infra (controller/traefik/cloudflared/filebrowser) shares the one Docker data-root and is protected by prevention, not placement. Built, deployed, and live-validated on a freshly re-provisioned guest 9201.

What shipped (v0.58.0)

  • Reserved-buffer headroom guard (internal/system/dockervol.go): GetDockerVolumeHeadroom() measures the Docker-data volume via statfs("/") — the controller container's root overlay is the upperdir on the guest's /var/lib/docker volume (true with the overlay2 driver; see the agent report), so / reports the data volume. Reserve floor DockerVolumeReserveGB = max(5 GB, 10%). Fail-open on a measurement error.
  • Deploy-time hard gate (internal/api/router.go deployStack): a new deploy is refused (HTTP 507 + Hungarian message) when free space on the Docker-data volume is at/under the reserved buffer.
  • Deploy-page surfacing (deploy.html): a new deploy below the buffer shows a Hungarian warning and disables the "Telepítés indítása" button; the API gate is the hard backstop.
  • Runtime monitoring (monitor/healthcheck.go): confirmed DiskPercent watches the Docker-data volume (statfs /); warn 80% / crit 90% trip ABOVE the 10%-free buffer, so the customer is warned before the gate engages. Clarifying comment added.
  • Log rotation baked into the golden's daemon.json (agent side; max-size 10m, max-file 3).
  • Tests: DockerVolumeReserveGB floor/scale.

Live validation (guest 9201, freshly re-provisioned from the split golden)

9201 was destroyed and re-provisioned from the new split golden (32 GB OS rootfs + 256 GB Docker-data volume, backup=1), via felhom-agent --selftest=provision + a reboot. The controller bootstrapped from baked images (no pull), pulled its config from the hub (catalog synced — 55 app defs — hub HTTP 200, CF token configured, hub report pushed). Then:

  • Split layout: controller image 0.58.0, Storage Driver overlay2, Docker Root Dir /var/lib/docker; images on the data volume (/var/lib/docker/overlay2 1.7 GB), /var/lib/containerd idle (380 K); df: / 935 MB/32 GB (4%, lean OS rootfs), /var/lib/docker 256 GB.
  • Prevention gate (the headline): with ample space the deploy page shows no gate banner; after fallocate-filling the data volume to 99% (3.2 GB free < 25.6 GB reserve), a POST /api/stacks/.../deploy returned HTTP 507 with the Hungarian "Nincs elég szabad tárhely" message — proven on the real 256 GB data volume.
  • Regression: /, /stacks, /backups, /monitoring, /stacks/{n}/deploy, /stacks/{n}/backup all HTTP 200; A1 host-storage list still ordered + friendly-labelled (felhom-usb → local-lvm → local → felhom-pbs); A2 Tier-2 panel route serves.
  • Deploy path + DB-on-data-volume (step 3): deployed ActualBudget (HTTP 200, container up); its named volume landed at /var/lib/docker/volumes/actualbudget_actualbudget_data = the data volume.
  • External access: via Cloudflare the controller returns HTTP 200 for vmid 9201 (tunnel + traefik route healthy). (A local-DNS override on the dev machine points the hostname at a stale LAN IP — a red herring; the real public path works.)

OS isolation (resilience — the reason for the split), proven on the provisioned guest

Filling the Docker-data volume to 100% (239 GB) left the OS rootfs at 4% and fully writable, the guest healthy throughout — the data volume cannot starve the OS.

Outstanding (demo restoration, not slice validation)

  • RomM (HDD app) + USB re-enroll: RomM's data is safe on the host USB (/mnt/felhom-usb/felhom-data, untouched by the re-provision). Restoring it is the slice-10 enroll flow (assign → guest-attach → reboot to activate the bind → register storage → deploy). With the split, the USB binds to a free slot (mp1+) since mp0 is now the Docker-data volume — no collision. Documented as the final restore step; not required for slice validation (ActualBudget covered the deploy path; the USB bind was not touched).