Files
felhom.eu/documentation/audits/sizing-2026-10-13/SIZING.md
T
adminandClaude Opus 5.5 2f1f6c8052
gates / gates (push) Successful in 6m34s
Prowlarr evidence (R-1048) + its apps-page card; the stated app count 62 -> 63; R-1057 filed
- documentation/audits/prowlarr-2026-10-15/: bench (update test proven, C3 failed as it must, soak 7.6 %,
  volume-persistence CLEAN, official-image check), 9202 runs 1-3 (run 1's bump emptied the drill .felhom.yml — a
  test-tool bug, fixed; run 3 re-proved the step and backup -> remove -> restore with the full template), tools.
- website: a Prowlarr card on both apps pages; every stated count 62 -> 63, the media group 19 -> 20; sizing.json +
  sizing-data.json carry Prowlarr (41/44 MB, onboarding), the sizing test and SIZING.md table moved to 63 apps.
- OPEN-ITEMS: R-1057 (a restore of a removed gated app returns without the gate, even from a backup made before the
  setup — measured on 9202); R-1036 notes the count and that wger/plant-it are hidden/abandoned.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-15 12:58:18 +02:00

198 lines
14 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.
# What a Felhom box needs — sizing, 2026-10-13
Measured on the N100 demo box (`demo-felhom`: Intel N100, 4 cores, 16 GB). Method, sources and limits: `METHOD.md`.
Data file for the future estimator: `sizing.json` (rebuilt by `tools/build_sizing.py`). Memory is MB (MiB),
docker-style `used`. CPU is % of the whole N100 (4 cores = 100 %).
## 1. The floor — the box with no apps
| Part | Memory | Disk |
|---|---|---|
| Whole host (Proxmox + agent + guest + platform), `MemTotal − MemAvailable` | **2,494 MB** (peak 2,793) | host root 31 GB used of 94 GB, of which 19 GB = 3 local guest dumps |
| — of which inside the guest: platform containers + Docker daemons | ≈ 470 MB | guest system 1.1 GB, Docker volume 1.6 GB (platform images 1.55 GB) |
| traefik (proxy) | 113 MB (anon 15) | — |
| filebrowser (file browser) | 75 MB (anon 18) | — |
| cloudflared (tunnel) | 38 MB (anon 16) | — |
| felhom-controller | 29 MB (anon 28) | — |
| dockerd + containerd + shims | ≈ 220 MB RSS | — |
| CPU at rest, platform only | 0.4 % of the N100 | — |
| Power at rest (CPU package, approximate) | 3.9 W | — |
Samba is not in the floor: it runs only when sharing is switched on. Evidence: `evidence/floor/`.
## 2. Each app
<!-- apps-table: generated by tools/render_table.py -->
| App | Typical MB | Peak MB | Catalog request / limit | CPU class | CPU rest % | Busiest minute % | Source |
|---|---|---|---|---|---|---|---|
| Immich (`immich`) | 1,557 | 3,290 | 2,048 / 4,480 | heavy-in-bursts | 0.74 | 74.2 | onboarding ! |
| Grimmory (`grimmory`) | 451 | 1,095 | 600 / 1,408 | medium | 0.83 | 22.0 | onboarding ! |
| Karakeep (`karakeep`) | 372 | 1,050 | 700 / 2,816 | light | 0.6 | 2.9 | onboarding ! |
| Cal.com (`calcom`) | 800 | 950 | 200 / 1,792 | light | 0.14 | 1.8 | onboarding |
| Dawarich (`dawarich`) | 390 | 890 | 500 / 2,688 | light | 0.48 | 14.4 | onboarding |
| Tandoor Recipes (`tandoor`) | 868 | 868 | 512 / 1,280 | medium | 0.16 | 20.0 | onboarding |
| RomM (`romm`) | 528 | 769 | 300 / 1,280 | medium | 0.71 | 24.2 | telemetry |
| Paperless-ngx (`paperless-ngx`) | 645 | 755 | 500 / 1,664 | medium | 0.37 | 31.3 | telemetry |
| SparkyFitness (`sparkyfitness`) | 316 | 664 | 400 / 1,792 | light | 0.23 | 0.4 | onboarding ! |
| n8n (`n8n`) | 351 | 578 | 512 / 1,536 | light | 0.1 | 0.5 | onboarding ! |
| Nextcloud (`nextcloud`) | 314 | 525 | 256 / 1,664 | light | 0.4 | 12.8 | onboarding ! |
| Komga (`komga`) | 442 | 496 | 200 / 768 | light | 0.15 | 0.5 | onboarding |
| Docmost (`docmost`) | 415 | 495 | 200 / 896 | light | 0.38 | 0.8 | telemetry |
| OnlyOffice (`onlyoffice`) | 364 | 488 | 512 / 2,048 | medium | 0.04 | 31.8 | measured |
| Rallly (`rallly`) | 360 | 479 | 384 / 1,024 | light | 0.19 | 2.9 | onboarding |
| AdventureLog (`adventurelog`) | 361 | 464 | 100 / 896 | medium | 0.45 | 19.3 | telemetry |
| Jellyfin (`jellyfin`) | 191 | 448 | 512 / 2,048 | heavy-in-bursts | 0.06 | 51.3 | measured |
| Grafana (`grafana`) | 269 | 435 | 100 / 512 | light | 0.31 | 1.0 | onboarding ! |
| Home Assistant (`home-assistant`) | 352 | 434 | 256 / 1,024 | light | 0.2 | 2.4 | onboarding |
| Mealie (`mealie`) | 273 | 431 | 200 / 1,000 | light | 0.15 | 1.4 | onboarding ! |
| Calibre-Web Automated (`calibre-web`) | 191 | 413 | 200 / 768 | medium | 0.04 | 26.0 | telemetry ! |
| Papra (`papra`) | 266 | 405 | 256 / 768 | light | 0.07 | 0.2 | onboarding |
| Outline (`outline`) | 282 | 393 | 200 / 1,152 | light | 0.39 | 1.4 | onboarding |
| Gramps Web (`gramps-web`) | 326 | 342 | 384 / 1,024 | light | 0.04 | 0.0 | onboarding |
| MeTube (`metube`) | 62 | 329 | 128 / 768 | light | 0.03 | 0.1 | onboarding ! |
| qBittorrent (`qbittorrent`) | 163 | 318 | 192 / 1,024 | light | 0.04 | 0.1 | onboarding ! |
| wger (`wger`) | 246 | 267 | 256 / 416 | — | – | – | onboarding — hidden |
| Plant-it (`plant-it`) | 50 | 256 | 50 / 256 | — | – | – | unmeasured — abandoned |
| Kimai (`kimai`) | 163 | 250 | 100 / 640 | light | 0.16 | 0.8 | telemetry ! |
| Claper (`claper`) | 248 | 248 | 100 / 640 | light | 0.3 | 0.5 | onboarding |
| Plex (`plex`) | 232 | 234 | 512 / 2,048 | light | 0.22 | 3.4 | measured |
| Uptime Kuma (`uptime-kuma`) | 118 | 227 | 50 / 256 | light | 0.2 | 0.2 | onboarding ! |
| BookStack (`bookstack`) | 159 | 225 | 150 / 768 | light | 0.18 | 0.9 | telemetry ! |
| Zipline (`zipline`) | 196 | 217 | 100 / 768 | light | 0.2 | 0.3 | onboarding |
| Wishlist (`wishlist`) | 118 | 184 | 120 / 512 | light | 0.07 | 0.1 | onboarding ! |
| Jellyseerr (`seerr`) | 166 | 168 | 100 / 384 | light | 0.04 | 0.1 | measured |
| ActualBudget (`actualbudget`) | 44 | 167 | 50 / 256 | light | 0.06 | 0.1 | measured ! |
| Ghost (`ghost`) | 149 | 149 | 150 / 512 | light | 0.08 | 0.2 | onboarding |
| Wanderer (`wanderer`) | 143 | 147 | 350 / 1,024 | light | 0.2 | 0.3 | measured |
| Emby (`emby`) | 142 | 146 | 512 / 2,048 | light | 0.18 | 1.9 | onboarding ! |
| Gitea (`gitea`) | 76 | 136 | 100 / 512 | light | 0.04 | 0.1 | onboarding ! |
| Homebox (`homebox`) | 17 | 118 | 50 / 256 | light | 0.06 | 0.1 | measured ! |
| Termix (`termix`) | 102 | 117 | 30 / 128 | light | 0.04 | 0.1 | measured |
| Grocy (`grocy`) | 33 | 114 | 60 / 384 | light | 0.04 | 0.3 | onboarding ! |
| LubeLogger (`lubelogger`) | 56 | 113 | 80 / 512 | light | 0.08 | 0.3 | onboarding ! |
| Vikunja (`vikunja`) | 20 | 104 | 50 / 256 | light | 0.0 | 0.0 | measured ! |
| Radarr (`radarr`) | 96 | 100 | 150 / 512 | light | 0.09 | 0.3 | measured |
| Vaultwarden (`vaultwarden`) | 44 | 99 | 50 / 256 | light | 0.03 | 0.0 | onboarding ! |
| Crafty Controller (`crafty-controller`) | 79 | 84 | 256 / 2,048 | light | 0.09 | 0.2 | onboarding ! |
| Homepage (`homepage`) | 82 | 83 | 50 / 256 | light | 0.03 | 0.0 | measured |
| Gokapi (`gokapi`) | 7 | 77 | 30 / 128 | light | 0.03 | 0.0 | measured ! |
| Sonarr (`sonarr`) | 77 | 77 | 150 / 512 | light | 0.07 | 0.2 | onboarding |
| Recipe Importer (`recipe-importer`) | 63 | 72 | 30 / 128 | light | 0.09 | 0.1 | measured |
| OpenGist (`opengist`) | 27 | 68 | 30 / 128 | light | 0.03 | 0.8 | telemetry |
| SABnzbd (`sabnzbd`) | 68 | 68 | 192 / 1,024 | light | 0.04 | 0.1 | onboarding |
| PrivateBin (`privatebin`) | 40 | 60 | 30 / 128 | light | 0.04 | 0.0 | telemetry ! |
| Code-Server (`code-server`) | 59 | 59 | 200 / 1,024 | light | 0.03 | 0.0 | measured |
| Audiobookshelf (`audiobookshelf`) | 48 | 54 | 100 / 512 | light | 0.04 | 0.1 | onboarding |
| Radicale (`radicale`) | 27 | 27 | 40 / 128 | light | 0.03 | 0.0 | onboarding |
| Transmission (`transmission`) | 6 | 22 | 64 / 512 | light | 0.05 | 0.1 | onboarding ! |
| Navidrome (`navidrome`) | 14 | 18 | 50 / 256 | light | 0.04 | 0.1 | onboarding |
| BentoPDF (`bentopdf`) | 10 | 15 | 100 / 384 | light | 0.03 | 0.0 | telemetry ! |
| Glance (`glance`) | 6 | 7 | 20 / 128 | light | 0.03 | 0.0 | measured |
| NZBGet (`nzbget`) | 5 | 5 | 64 / 512 | light | 0.04 | 0.1 | onboarding |
<!-- /apps-table -->
`source`: telemetry = hub fleet data ≥ 7 days · onboarding = an earlier session's measured peak · measured = this bench ·
unmeasured = catalog value. `!` = two sources' peaks differ by more than 50 %; the higher is used (all values:
`evidence/apps-table.json`). `—` = not installable today (hidden / abandoned), no bench.
**The five largest peaks:** Immich 3,290 · Grimmory 1,095 · Karakeep 1,050 · Cal.com 950 · Dawarich 890 MB.
**Raised with the HP check (2026-10-14, operator ruling A, R-1009):** 23 figures of 64 apps took the second machine's higher
figure (HP t740, `audits/sizing-check-2026-10-14/hp-figures.json`); none went down. Each raised figure is named in
`sizing.json` → `raised_by_hp` (the figure, what it was, what it is). The table above already holds the raised figures.
**CPU bursts (heavy-in-bursts):** Immich — 74 % for the busiest minute, while it reads new photos; Jellyfin — 51 %,
while it converts a film (CPU only, no GPU passed through). **Medium:** Paperless-ngx 31 % (reading scans),
OnlyOffice 32 %, Calibre-Web 26 %, RomM 24 %, Grimmory 22 %, Tandoor 20 %, AdventureLog 19 % (first start).
Every other app stays under 15 % even at first start.
## 3. Catalog accuracy (§7 C — templates NOT changed; `evidence/catalog-check.json`)
**Near a memory limit (peak > 80 %) — risk: the container is stopped for using too much memory:**
| App | Finding | Source |
|---|---|---|
| docmost | container `docmost` 475 MB = 93 % of its 512 MB limit | telemetry + bench |
| romm | container `romm` 703 MB = 92 % of its 768 MB limit | telemetry + bench |
| termix | 117 MB = 91 % of its 128 MB limit | bench |
| uptime-kuma | 227 MB = 89 % of `mem_limit` 256 MB | telemetry (tester-1) |
| grafana | 435 MB = 85 % of its 512 MB limit | bench |
| immich | container `immich-server` 1,665 MB = 81 % of its 2,048 MB limit (kernel peak incl. cache 4,365 of 4,480 MB for the app) | bench, 200-photo import |
| tandoor | container `tandoor` 827 MB = 81 % of its 1,024 MB limit | bench |
No container was killed for memory during the bench (`oom_kill` = 0 in every container).
**Typical use above `mem_request` (the catalog's figure is too low), 23 apps:** calcom ×4.0 · adventurelog ×3.6 ·
termix ×3.4 · claper ×2.5 · grafana ×2.2 · komga ×2.2 · docmost ×2.1 · recipe-importer ×2.1 · uptime-kuma ×2.1 ·
zipline ×2.0 · romm ×1.8 · seerr ×1.7 · tandoor ×1.7 · homepage ×1.6 · kimai ×1.6 · mealie ×1.4 · outline ×1.4 ·
privatebin ×1.3 · home-assistant ×1.2 · nextcloud ×1.2 · bookstack ×1.1 · paperless-ngx ×1.1 · papra ×1.0.
## 4. Power
Wall figures: the whole N100 box at the socket (Tuya meter plug, tinytuya, 2 s; `audits/poweroff-2026-10-13/`,
measured the same evening). CPU figures: Intel RAPL, CPU package only (this run).
| State | Wall (plug) | CPU package (RAPL) |
|---|---|---|
| Platform at rest | **8.5 W** average, 7.2 W median (10 min) | 3.9 W (15 min) |
| Immich import, 200 photos + all jobs | **20.7 W** average, 24.0 W busiest minute, 25.2 W max (442 s) | 12.5 W average, 14.7 W busiest minute; 70 °C max (411 s) |
| Switched off (host powered off, plug on) | **0.0 W** — below the plug's 0.1 W step (18 min) | — |
| **One real night, the N100** (2026-10-13 22:12 → 06:00, its normal night: dumps, Tier 2, Tier 3, whole-guest backup, OS steps) | **66 Wh in 7.8 h** — 8.5 W average, 7.3 W median, 20.6 W max (the backup window: 19.8 W max); ≈ 0.2 kWh a day | — |
| **HP t740 (demo-hp)**, the second machine, at rest (guest 9201 with 11 apps + Tester 1's VM + a scratch guest) | **24–27 W** (median 24.1 W) | — |
| HP, three heavy apps busy at once (Immich import, Jellyfin transcode, Nextcloud upload) | 71 W average, 81.6 W max | — |
| HP, a start after a power cut (to all apps healthy, ~4 min) | 72–81 W peak, 2.7–3.4 Wh | — |
| HP, switched off with the plug on | 0–3.2 W | — |
Rough yearly figure at rest: 8.5 W × 8,760 h ≈ 75 kWh (the measured night agrees: 66 Wh in 7.8 h). The N100 night and the
HP rows: `audits/night-power-2026-10-13/` (2026-10-14). They are in this table only, not in `sizing.json`: changing that file
changes the checksum the website's data file carries, and the website was not to change that night.
## 5. Proposed minimum machine — a proposal; the operator decides
**Worked from:** floor + the household set (Immich, Jellyfin, Nextcloud, Home Assistant, Vaultwarden, Paperless-ngx).
| | Memory |
|---|---|
| Household set, typical (sum) | 3,103 MB |
| Household set, all at peak at once (sum — they rarely coincide; deliberately pessimistic) | 5,551 MB |
| Guest platform | ≈ 470 MB |
| Guest needs at peak | ≈ 5.9 GB |
| + 50 % headroom | ≈ 8.8 GB guest |
| + the host's share (the installer keeps 4 GB for Proxmox; it used ≈ 2.0 GB here) | **≈ 12.8 GB box** |
**Headroom 50 %, because:** the bench library is small (200 photos; tester-1's real Immich already peaked at 2.9 GB);
the nightly backup runs database dumps and restic next to the apps; an update pulls new images while the old ones run;
and the page cache is what makes a photo library feel fast.
| | Minimum | Recommended |
|---|---|---|
| CPU | x86-64, 4 cores, N100 class or newer (Intel or AMD) | N100 / N150 / N305 or better; Intel Quick Sync helps Jellyfin later (not used today) |
| Memory | **8 GB** | **16 GB** |
| System disk | 256 GB SSD | 512 GB SSD (NVMe or SATA) |
| Data | a separate drive for photos and films | same |
| Out | ARM (Raspberry Pi), Synology / QNAP and other NAS appliances, routers, cloud VPS, 32-bit machines | — |
**Disk, why:** the installer gives the guest a 32 GB system volume and, from the free space, a 70 / 130 / 250 GB data
volume; the host keeps three daily guest dumps (19 GB on this box). Image sizes per app were **not measured** in this run.
**What each size runs — three verdicts, the same rule as the estimator page** (`/gepigeny`, `website/assets/sizing.js`).
*Corrected 2026-10-13:* the lines here first said an 8 GB box runs Immich „on its own” while this section sizes a box at
peak × 1.5 — which Immich alone does not meet on 8 GB ((470 + 3,061) × 1.5 ≈ 5.2 GB > 4 GB), though its peak fits
(3.4 GB). Three verdicts say both honestly. The guest is the installer's rule on MemTotal = label − 612 MiB (the
16 GB N100 reports 15,772 MiB): **8 GB → 4.0 GB, 16 GB → 11.4 GB, 32 GB → 27.4 GB** (the first version said 11.6 / 27.6).
**Fits well:** peak × 1.5 ≤ guest · **fits, but tight:** peak ≤ guest < peak × 1.5 — works, but a large photo import or
a backup at the same time can slow it down · **does not fit:** guest < peak. Peak = 470 MB platform + the apps' peaks summed.
<!-- sizing-verdicts: generated from the rule; scripts/test_build_sizing_web.py checks this table (24 GB column added 2026-10-13, estimator v2) -->
| Selection | Peak | 8 GB | 16 GB | 24 GB | 32 GB |
|---|---|---|---|---|---|
| Household set | 5.9 GB | does not fit | fits well | fits well | fits well |
| Household set without Immich | 2.7 GB | fits, but tight | fits well | fits well | fits well |
| Immich alone | 3.7 GB | fits, but tight | fits well | fits well | fits well |
| All 63 apps | 21.9 GB | does not fit | does not fit | does not fit | fits, but tight |
<!-- /sizing-verdicts -->
- **8 GB:** the household set without Immich fits, but tight; Immich alone fits, but tight; the whole household set does not fit.
- **16 GB:** the whole household set fits well.
- **32 GB:** the household set fits well; all 63 apps at once fit, but tight.
2026-10-14: raised with the HP check (operator ruling A); one verdict became stricter — household set without Immich on 8 GB, by 0.5 MB.