From 1267e60e8e0b243ce08032edc436e806bd24fccb Mon Sep 17 00:00:00 2001 From: reachableceo Date: Wed, 2 Sep 2026 19:48:45 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20apply=20memory=20shrink=20+=20daemon.js?= =?UTF-8?q?on=20=E2=80=94=20ground=20truth=20update=20[#731]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 11 apps reslidered via API (53.4->29.0GB explicit limits); daemon.json live-restore + nofile defaults applied (log keys banned: unit flag conflict — see #731 incident note). perf/apply-memory-shrink.sh codifies the change. All 159 apps verified running. Meat: https://projects.knownelement.com/issues/731#note-3929 --- README.md | 28 ++++++----- STATUS.md | 12 +++-- perf/apply-memory-shrink.sh | 46 +++++++++++++++++++ ...daemon.json.planned => docker-daemon.json} | 2 - questions-v1.md | 8 +++- 5 files changed, 76 insertions(+), 20 deletions(-) create mode 100755 perf/apply-memory-shrink.sh rename perf/{docker-daemon.json.planned => docker-daemon.json} (59%) diff --git a/README.md b/README.md index 4e75d5d..ad3adfe 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Postgres/MySQL/mail containers, monitored by Uptime Kuma (Cloudron group). | Path | What | |------|------| -| `perf/` | [#685](https://projects.knownelement.com/issues/685) perf work: memory census, pg role-cap SQL, applied sysctl, planned docker daemon.json | +| `perf/` | [#685](https://projects.knownelement.com/issues/685) perf work: memory census, pg role-cap SQL, applied sysctl, applied `daemon.json`, `apply-memory-shrink.sh` | | `monitoring/` | Uptime Kuma Cloudron app coverage — 55-app inventory + sync checker (moved from PFVCluster, #435/#685/#727) | | `ansible/` | Reserved — AWX/Ansible IaC convergence ([#454](https://projects.knownelement.com/issues/454)); playbooks land here as manual changes are codified | | `scripts/` | Rule engine + git hooks (enforcement layer, ADOPTING pattern) | @@ -25,16 +25,22 @@ Postgres/MySQL/mail containers, monitored by Uptime Kuma (Cloudron group). idle-in-transaction timeout 15min. Victim `too many clients` FATALs → zero. - Host sysctl: `vm.swappiness=10` (`perf/sysctl/`). THP already `never`, virtio scheduler `none` — optimal, left alone. -- **`/etc/docker/daemon.json` is still ABSENT on the host** (`live-restore=false`, - containers default to nofile=1024 unless bridged). The postgres fd bridge - (prlimit 262144) is EPHEMERAL — lost on any postgres container recreate. - Applying `perf/docker-daemon.json.planned` needs ONE planned dockerd bounce - (restarts all ~300 containers) in a human-chosen window. -- Memory sliders: shrink table on t/316 (~28GB headroom). Do NOT shrink - `bi.knownelement.com` or platform services (Cloudron auto-sizes those). +- **`/etc/docker/daemon.json` APPLIED 2026-09-02** (#731): `live-restore: true` + (future dockerd restarts are zero-downtime) + `default-ulimits nofile + 262144` (every container start gets real fd limits — the postgres prlimit + bridge is no longer needed; `perf/docker-daemon.json` is the SoR copy). + NOTE: this host's dockerd runs `--log-driver=journald` as a unit flag — + daemon.json must NOT set log-driver/log-opts or dockerd refuses to start + (bit us once, see #731). Container log rotation is journald's job here. +- **Memory shrink table APPLIED 2026-09-02** (#731) via the panel API + (`perf/apply-memory-shrink.sh`): 11 apps resliders, explicit limits + 53.4 GB → 29.0 GB (~24 GB of the ~28 GB headroom reclaimed). Skips: + photos (manifest floor 3.5 GB), `bi.knownelement.com` + platform services + (do NOT shrink). All 159 apps verified running post-change. ## Access -SSH `root@my.knownelement.com` is the only access path — routed through the -KNEL/PFVCluster `tests/remote.sh` chokepoint (see AGENTS.md). Cloudron API -token pending; once issued, refs live in `~/.creds/cloudron.env`, never here. +SSH `root@my.knownelement.com` — routed through the KNEL/PFVCluster +`tests/remote.sh` chokepoint (see AGENTS.md). Panel API token issued +2026-09-02 (`~/.creds/cloudron.env`, refs only in this repo) — prefer the +API over SSH for reads/limits; base image rebuilds (#710) still need SSH. diff --git a/STATUS.md b/STATUS.md index 07b5510..0606765 100644 --- a/STATUS.md +++ b/STATUS.md @@ -7,8 +7,9 @@ Last updated: 2026-09-02 | ticket | state | item | |--------|-------|------| -| #727 | 🔄 | repo bootstrap + perf script capture | -| #685 | ⏳ | founder soak — evening storm window is the real test; caps stay until tymeslot ships a POOL_SIZE env | +| #727 | ✅ | repo bootstrap + perf script capture + API token received (suggest close) | +| #731 | ✅ | memory shrink applied (53.4→29.0GB) + daemon.json live-restore bounce (suggest close after soak) | +| #685 | ⏳ | tonight 20:00-04:00 CDT storm window is the soak test; caps stay till tymeslot POOL_SIZE | | #626 | ⏳ | Cloudron proxying (Grocy multi-user + HA general access) | | #710 | ⏳ | custom Ubuntu 24.04 base image for Cloudron hosts | | #723 | ⏳ | linkwarden save-to function | @@ -16,6 +17,7 @@ Last updated: 2026-09-02 ## Inbox (founder action items) -- Cloudron API token (asks in #685/#727): create in the panel → material value - to `~/.creds/cloudron.env` — unblocks API-first access + future AWX modules. -- docker daemon.json bounce window (see README) — ticket it when scheduled. +- axiosheartstudios.com returns 500 — pre-existing app breakage (founder + confirmed 2026-09-02), unrelated to its memory reslider. Needs an app-level + look when scheduled. +- Tymeslot upstream: ask for a POOL_SIZE env so the role caps can be removed. diff --git a/perf/apply-memory-shrink.sh b/perf/apply-memory-shrink.sh new file mode 100755 index 0000000..c5d18b6 --- /dev/null +++ b/perf/apply-memory-shrink.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# perf/apply-memory-shrink.sh — apply the t/316 memory shrink table via the +# Cloudron API. Applied live 2026-09-02 (Redmine #731). Idempotent: re-running +# sets the same values; 202 = accepted, app restarts. +# +# Requires: curl, jq. Creds: ~/.creds/cloudron.env (CLOUDRON_URL, CLOUDRON_API_TOKEN). +# +# Gotchas learned on this host: +# - Route is POST /api/v1/apps//configure/memory_limit (NOT /memory_limit). +# - Values <= manifest.memoryLimit (package floor) are rejected 400 "too small" +# (e.g. photos/immich floor 3.5GB — left at 4096). +# - This host's dockerd runs --log-driver=journald as a systemd-unit flag; +# daemon.json must NOT set log-driver/log-opts (dockerd refuses to start). + +set -euo pipefail + +# shellcheck disable=SC1090 # creds path is env-overridable by design +source "${CLOUDRON_CREDS:-$HOME/.creds/cloudron.env}" + +# fqdn|target MB (t/316 table; photos skipped — manifest floor 3.5GB) +TABLE=' +axiosheartstudios.com|512 +community.turnsys.com|4096 +nextcloud.knownelement.com|2048 +bookmarks.knownelement.com|2048 +share.knownelement.com|512 +hfnfc.net|512 +notes.knownelement.com|1024 +digital-assets-manager.knownelement.com|512 +finance.turnsys.com|512 +finance.reachableceo.com|512 +' + +APPS=$(curl -s --max-time 20 -H "Authorization: Bearer $CLOUDRON_API_TOKEN" "$CLOUDRON_URL/api/v1/apps") + +echo "$TABLE" | while IFS='|' read -r fqdn mb; do + [ -z "$fqdn" ] && continue + app_id=$(jq -r --arg f "$fqdn" '.apps[] | select(.fqdn==$f) | .id' <<<"$APPS") + [ -z "$app_id" ] && { echo "SKIP (not found): $fqdn"; continue; } + code=$(curl -s --max-time 20 -X POST \ + -H "Authorization: Bearer $CLOUDRON_API_TOKEN" -H "Content-Type: application/json" \ + -d "{\"memoryLimit\": $((mb * 1048576))}" \ + "$CLOUDRON_URL/api/v1/apps/$app_id/configure/memory_limit" -o /dev/null -w '%{http_code}') + echo "$code $fqdn -> ${mb}MB" + sleep 10 +done diff --git a/perf/docker-daemon.json.planned b/perf/docker-daemon.json similarity index 59% rename from perf/docker-daemon.json.planned rename to perf/docker-daemon.json index c0d5780..7980dc5 100644 --- a/perf/docker-daemon.json.planned +++ b/perf/docker-daemon.json @@ -1,6 +1,4 @@ { - "log-driver": "json-file", - "log-opts": { "max-size": "10m", "max-file": "3" }, "default-ulimits": { "nofile": { "Name": "nofile", "Soft": 262144, "Hard": 262144 } }, diff --git a/questions-v1.md b/questions-v1.md index a901634..42c246c 100644 --- a/questions-v1.md +++ b/questions-v1.md @@ -26,5 +26,9 @@ bounces (~300). live-restore is currently false so there is no zero-downtime path. Until then the postgres nofile bridge is ephemeral (lost on postgres container recreate). -- [ ] Pick a window (suggest: pre-storm weekday morning) — agent will ticket - the runbook + execute on your go +## 2. docker daemon.json bounce window (see README "Ground truth") + +- [x] Pick a window — founder chose 2026-09-02 ~19:30 CDT (pre-storm). + APPLIED: live-restore + default-ulimits nofile 262144 now active; first + attempt failed (log-driver flag conflict — unit passes journald), fixed + by dropping log keys; see #731 for the incident record.