diff --git a/CHANGELOG.md b/CHANGELOG.md index 53ad216..3721d39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## 2026-10-08 (day) — wger runs on its real web server: gunicorn with 2 workers (R-762) + +**What runs on a box changed:** nothing today. wger stays `lifecycle: hidden`, and no box runs it (hub read 2026-10-08 +09:46 CEST: wger is in no box's deployed list, containers or app telemetry; paperless-ngx control present on 4 of 4). + +- `templates/wger/docker-compose.yml`: `WGER_USE_GUNICORN=True` and `WEB_CONCURRENCY=2`, with a comment. Before this, + the image ran Django's development server (`manage.py runserver`). The image's entrypoint runs + `gunicorn ... --preload` only on that switch and passes no `-w`, so gunicorn reads its worker count from + `WEB_CONCURRENCY`. The header's RAM line now gives the measured figure. +- Proven on the bench (9401, harness v5 verdict `proven`, 606 s watch: anon peak 170 MiB = 44 % of 384M) and on scratch + 9202 (a fresh install from the drill catalog through the product's deploy endpoint): 2 × „Booting worker" in the + log; login page 200; its three CSS links 200 through `wger-files`; a photo uploaded 201 and read back 200 (179 B, + `image/png`); an unknown `/static/` file 404 (control). 9202's 600 s watch (10,944 requests): anon peak 246 MiB = + 64.1 % of 384M, flat for 1,200 more page loads after; oom_kill 0, restarts 0. +- **No ladder entry:** the change moves no image (from == to). `ladder.check_entry` refuses a re-test with the same + digest („tests nothing new"), and `09` §5.4 renders the catalog template when the images equal the pinned ones. So the + change reaches an installed wger at its next `up -d`. +- `onboarding/wger.md`: row 1.5 done (gunicorn); row 1.7's „stays unset" sentence corrected; row 9.3's open-row count + (8) and register rows corrected. +- Evidence: `felhom.eu/documentation/audits/day-2026-10-08/r762/` (`bench/`, `9202/`). Gates: `catalog_gates.py --fast` + green. The runtime gates (image-resolvable, volume-persistence) were not run: they need a container runtime, and + DooPlex gets no Docker command. + ## Unreleased (2026-10-07) — the shared rule file; no code change - `.claude/rules/unprompted-work.md`: rule 11 (every helper prompt carries the brief's fences in full, `09` §3 decision 160) and the §1 line „no hub image build or hub deploy in a session the operator does not attend" (decision 162). Identical in all five copies. diff --git a/REPORT.md b/REPORT.md index d7ada18..a70d0ef 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,4 +1,30 @@ -# REPORT — no code change (2026-10-07 day) +# REPORT — wger runs on gunicorn with 2 workers (R-762, 2026-10-08 day) -Only the shared rule file changed (`09` §3 decision 162: no hub image build or deploy in a session the operator does not -attend; and rule 11, every helper prompt carries the brief's fences in full). Controller 0.302.0 stays on the three boxes. +**Change:** `templates/wger/docker-compose.yml` sets `WGER_USE_GUNICORN=True` and `WEB_CONCURRENCY=2`. Before this, wger +ran Django's development server. wger stays `lifecycle: hidden`. No box runs wger (hub read 2026-10-08 09:46 CEST). + +| venue | workers | anon peak (of 384M) | oom_kill | restarts | login | CSS (3 links) | photo read back | +|---|---|---|---|---|---|---|---| +| bench 9401 (harness v5, 606 s) | 2 | 170 MiB, 44 % | 0 | 0 | 200 | 200 | 200 | +| scratch 9202 (drill catalog, product deploy, 600 s) | 2 | 246 MiB, 64.1 % | 0 | 0 | 200 | 200 | 200 (179 B, image/png) | + +On 9202 the anon figure stayed at 246 MiB for 1,200 more page loads after the watch. Control: an unknown `/static/` file +answered 404. + +**No ladder entry.** The images do not move (from == to). `ladder.check_entry` refuses a same-digest re-test, and +`09` §5.4 renders the catalog template when the images equal the pinned ones. + +**Also corrected:** `onboarding/wger.md` rows 1.5, 1.7 and 9.3. + +**Not changed:** `mem_request: "100M"` in `.felhom.yml`. The measured figure is about 250 MiB, but the controller uses +`mem_request` in its install memory check (`deploy.go` L305, `update.go` L489). A change there changes which boxes can +install wger, so it is for the operator. + +**Gates:** `catalog_gates.py --fast` green. The runtime gates were not run, because DooPlex gets no Docker command. + +**Teardown on 9202:** wger was removed through the product. Afterwards there was no container and no volume, and the +stack directory held only the synced catalog template, as before the test. 9202's catalog setting was put back +(`repo_url` = the live catalog; `controller.yaml` sha256 identical to before). The drill commit was reverted (drill +`95876a3`). + +Evidence: `felhom.eu/documentation/audits/day-2026-10-08/r762/`. diff --git a/onboarding/wger.md b/onboarding/wger.md index 71063a0..fb57f71 100644 --- a/onboarding/wger.md +++ b/onboarding/wger.md @@ -26,9 +26,9 @@ Measured on 9202 2026-10-01 from the drill catalog (e9f50b5, wger template ident 1.2 | n/a | wger keeps SQLite on its own volume, no database sidecar 1.3 | n/a | no MariaDB or PostgreSQL service in this template 1.4 | done | felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C2-static-reads.txt ; felhom.eu/documentation/audits/more-night-apps-2026-09-30/box/wger/step.txt — `DJANGO_PERFORM_MIGRATIONS=True`; the 2.6 -> 2.7 step migrated and read back on the box -1.5 | open | the container runs `manage.py runserver` — `WGER_USE_GUNICORN` is not set (R-755; C2) +1.5 | done | gunicorn with 2 workers since 2026-10-08 (`WGER_USE_GUNICORN=True`, `WEB_CONCURRENCY=2`, R-762): 2 × „Booting worker" in the log; login page, CSS and a photo 200; 10-min watch 0 oom_kill, 0 restarts — bench 9401 and 9202 (`felhom.eu/documentation/audits/day-2026-10-08/r762/`) 1.6 | done | felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C2-static-reads.txt ; felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C3-logins.txt — of 15 key-like env reads: SECRET_KEY generated, JWT pair made at start (phone route 200), DB password unused by SQLite; the rest belong to features off here (S3, mail, reCAPTCHA, OIDC, PowerSync) -1.7 | done | `DJANGO_DEBUG=False` (R-762, 2026-10-06): `collectstatic` runs at start; the static files are served by `wger-files` (nginx) — the login page's own CSS links 200 on the bench and on 9202 (`felhom.eu/documentation/audits/design-build-2026-10-06/F/`). `WGER_USE_GUNICORN` stays unset (R-755, closed) +1.7 | done | `DJANGO_DEBUG=False` (R-762, 2026-10-06): `collectstatic` runs at start; the static files are served by `wger-files` (nginx) — the login page's own CSS links 200 on the bench and on 9202 (`felhom.eu/documentation/audits/design-build-2026-10-06/F/`). the web server is gunicorn since 2026-10-08 (row 1.5) 1.8 | done | felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C2-static-reads.txt ; felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C3-logins.txt — DEBUG False; an unknown page is a plain 404, no debug page 1.9 | n/a | SECRET_KEY signs sessions and reset links only; the JWT pair lives on the data volume and is backed up with it 2.1 | open | re-sweep 2026-10-02 with the fixed gate (felhom.eu/documentation/audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md): UNDETERMINED — /home/wger/media stays empty after the seed (no upload in the seed); nothing written outside a preserved folder (R-807). The August CLEAN came from start-time writes alone @@ -71,5 +71,5 @@ Measured on 9202 2026-10-01 from the drill catalog (e9f50b5, wger template ident 8.5 | n/a | wger is an existing app; the count does not change 9.1 | open | the runtime volume-persistence gate was not re-run today (last CLEAN 2026-08-02); the other gates exit 0 (B3) (R-759) 9.2 | done | felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C1-install.txt ; felhom.eu/documentation/audits/new-app-checklist-2026-10-01/C/C8-signup-guest-media-static.txt — fresh installs from the drill catalog, as household and stranger -9.3 | open | 10 other rows stay open; each is a register row (R-755, R-759, R-762, R-763, R-764) +9.3 | open | 8 other rows stay open; each is a register row (R-759, R-763, R-764, R-807) 9.4 | n/a | wger is exempt: published 2026-02-15, before the checklist diff --git a/templates/wger/docker-compose.yml b/templates/wger/docker-compose.yml index 47a9fc6..c157443 100644 --- a/templates/wger/docker-compose.yml +++ b/templates/wger/docker-compose.yml @@ -1,7 +1,7 @@ # wger - Edzésnapló és fitnesz tervező # Domain: ${SUBDOMAIN}.${DOMAIN} # Database: None (file-based) -# RAM: ~100M (mem_limit: 384M) | Pi-compatible: Yes +# RAM: ~250M with 2 gunicorn workers (measured 2026-10-08; mem_limit: 384M + wger-files 32M) | Pi-compatible: Yes # # Environment variables: # DOMAIN - Your domain (e.g., demo-felhom.eu) @@ -83,6 +83,14 @@ services: # /home/wger/static, and Django stops serving /static and /media itself (it never did in production — upstream # puts nginx in front). wger-files below serves both from the shared volumes. - DJANGO_DEBUG=False + # R-762 (2026-10-08): the real web server. The image's entrypoint.sh runs `gunicorn wger.wsgi:application --preload + # --bind 0.0.0.0:$PORT` when WGER_USE_GUNICORN is "True" (else Django's development server, `manage.py runserver`). + # It passes no -w and the image has no gunicorn.conf.py, so the worker count is gunicorn's own WEB_CONCURRENCY + # (unset = 1). Two workers: measured on the bench and on 9202 (2 x "Booting worker" in the log). anon peak under + # load: 170 MiB (44 %) on the bench, 246 MiB (64 %) on 9202 after a photo upload, flat for 1,200 more page loads; + # 0 oom_kill, 0 restarts. --preload: the workers share the app's pages with the master. + - WGER_USE_GUNICORN=True + - WEB_CONCURRENCY=2 volumes: - wger_data:/home/wger/db - wger_media:/home/wger/media