Files
felhom.eu/documentation/audits/sizing-2026-10-13/METHOD.md
T
admin c885b5acf4 Audit: what a Felhom box needs — floor, 64 apps' memory and CPU, power (N100 bench); R-993..R-995; R-231 due check read green
Measured on demo-felhom (N100, 16 GB): 61 apps deployed and removed through the
product, sampled every 5 s; heavy apps with one real action (Immich 200 photos,
Jellyfin transcode, Paperless OCR, Nextcloud 500 MB, ...). sizing.json for the
estimator (tools/build_sizing.py), SIZING.md with the proposed minimum machine,
METHOD.md. Power is RAPL CPU-package only (no meter). R-993 asks the operator to
approve the minimum machine; R-994 lists catalog limits/requests that are too low;
R-995 records leftover unnamed volumes after removal. R-231's dated check measured:
dooplex-backup.service success, journal green.
2026-10-13 13:23:51 +02:00

88 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Method — sizing bench, 2026-10-13
**Bench:** `demo-felhom` — Intel N100 (4 cores), 16 GB RAM, 512 GB SATA SSD + 1 TB HDD, Proxmox VE; guest 9201
(3 cores, 12 GiB) running controller 0.309.0. Tier 0 (`runbooks/target-selection.md`). Baselines: felhom.eu
`c2a6cf7a58`, app-catalog `8ee687e544`, felhom-controller `1b2f74c52f`.
## Sources, in the spec's order
| # | Source | What it is | Where |
|---|---|---|---|
| 1 | telemetry | hub App Telemetry, last 30 days, one row per 15-min report; app = sum of its containers. Used when one box has ≥ 7 days | `evidence/telemetry-30d.json` (from a hub DB copy with `-wal`, freshness proven: newest host report 2 min old) |
| 2 | onboarding | measured peaks from earlier onboarding/bench sessions; per run, the app's containers summed, anon basis only | `evidence/onboarding-peaks.json` (collected by a read-only helper; every figure cites its file:line) |
| 3 | measured | this run, on the N100 | `evidence/<batch>/` |
| 4 | unmeasured | catalog `mem_request` / `mem_limit` | `.felhom.yml` |
Telemetry under 7 days is used only when nothing better exists (`short-telemetry`). When peaks from two sources differ by
more than 50 %, both are kept (`evidence/apps-table.json`) and the higher goes into `sizing.json`.
**Telemetry is mostly demo and test boxes at rest** (demo-hp, tester-1, demo-felhom): it shows idle use, not household
use. Its `peak` is the highest of 1-minute `docker stats` samples, not the true peak. Its CPU is docker's per-core
percent; divided by 4 here to approximate the N100 scale (other CPUs, approximate).
## The bench measurement
- **Deploy** through the product: the endpoints the dashboard calls (`GET /api/stacks/<n>/deploy-fields`,
`POST /api/stacks/<n>/deploy`), the box's own login + CSRF. The data location: the box has no registered drive, and the
deploy page then says „Nincs regisztrált adattároló — add meg kézzel az útvonalat”; we typed `/mnt/sizing-bench`
(the guest system disk — the controller container sees only `/mnt`). **Removed** through the product
(`stop`, `remove` with data and backups); the product keeps data it cannot resolve (R-442), so the folder was then
deleted by name. Every removal checked: no stack, no container, no volume (`evidence/*/run.log`).
- **Sampling every 5 s** from each container's cgroup v2 files in the guest (`tools/guest_sampler.sh`), and on the host
(`tools/host_sampler.sh`): memory, RAPL package energy, CPU temperature.
- **Memory, two numbers:** `used` = `memory.current − inactive_file` (what `docker stats` and the fleet telemetry show)
and `anon` (the app's own heap). `sizing.json` uses `used`. The kernel's own high-water mark (`memory.peak`,
includes page cache) is recorded too — it is the figure closest to what a container limit is measured against.
- **Windows:** *work* = from the app's deploy to the end of its rest; *rest* = the last 15 minutes, nothing deploying.
Typical = average of rest; peak = highest 5-s sample of work.
- **Batches:** heavy apps alone (deploy → one real action → 15 min rest). Light apps 6–7 per batch, deployed one after
another, then 15 min rest together. Per-container numbers are exact either way; host power in a batch is shared.
- **CPU:** `usage_usec` deltas, summed per app, as **% of the whole N100 (4 cores = 100 %)**. `cpu_peak_1m` = the busiest
60 seconds. Classes: **light** — rest < 1 % and busiest minute < 15 %; **heavy-in-bursts** — busiest minute ≥ 50 %
(two cores fully busy for a minute); **medium** — between.
- **The floor:** 15 min with only the platform running. One app (opengist, ~23 MB) was already deployed there and was
left alone; it is excluded from the platform figures.
## Actions (content: generated or CC0; no personal data — `tools/make_content.py`)
| App | Action |
|---|---|
| Immich | first user; upload 200 photos (140 generated 12 MP, 60 CC0 portrait paintings from The Met — faces without living people); wait until every job queue is empty |
| Jellyfin | startup wizard; library of three generated 1080p H.264 films; scan; one forced transcode (H.264 3 Mbit/s, CPU — no GPU passed through) |
| Paperless-ngx | 20 image-only A4 scans at 300 dpi through the API; wait until all are OCR'd |
| Nextcloud | first start; 50 × 10 MiB over WebDAV |
| Home Assistant | first start; onboarding (first user) |
| Calibre-Web Automated | 20 generated EPUBs into the ingest folder |
| RomM | 15 dummy files copied into the library; **the scan was not triggered** (it needs a websocket client) |
| Mealie, Docmost, AdventureLog | first start (inside their batch) |
## Power
RAPL package energy on the host throughout (`evidence/*/host.tsv`): **CPU package only — not wall power.** The wall
reading is the operator's STOP (§ power in `SIZING.md`).
## Limits
- One box, one run per app, 15 min at rest — a household's long tail (a nightly backup, a library of 50,000 photos,
several users) is not in these numbers. The Immich import is 200 photos, not a family archive.
- The batch neighbours share CPU during first start; a burst can be slower (and lower) than alone.
- Apps that need an account or key to do work (Usenet, the *arr set, Plex sign-in) were measured at rest only.
- The bench path is on the guest system disk; on a real box the data sits on a separate drive — no effect on memory.
## Mixing bases — a known softness
Onboarding peaks are **anon** (the app's own heap); telemetry and this bench are **used** (anon + active file cache).
`used` is usually the larger. Where onboarding is the chosen source, its peak can read low — the >50 % rule then lifts it
to the bench figure (e.g. Immich, Grimmory, Grafana); within 50 % it does not.
## Teardown — three layers
- **Machine (guest 9201):** all 61 bench deploys removed through the product, each checked (no stack, container or
labelled volume). After the queue the guest still held **8 unnamed Docker volumes** made by the bench (4–72 KB:
Immich 1, RomM 2, PrivateBin 3, OnlyOffice 2) and **3 empty import folders** the deploys created (`import/paperless`,
`import/calibre`, `import/grimmory` on the system drive). Recorded (`evidence/leftover-volumes-after-bench.txt`), then
removed by name — no prune. `/root/sizing` and the 1.8 GB of content deleted. End state = start state: 5 containers
(platform + opengist), 3 volumes, 12 images, opengist's own backup folder only.
- **Host (felhom-pve):** samplers stopped, `/root/sizing` deleted. Nothing else was touched.
- **Hub:** read only (a DB copy). The box reported its own telemetry for the bench apps, as any box does; no hub write.