From af23488df1094d6f70b569998c36fa00e7f491d2 Mon Sep 17 00:00:00 2001 From: librelad Date: Tue, 26 May 2026 14:43:28 +0100 Subject: [PATCH] tidy: docs + Nextcloud APCu + container-side file-capture rollout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three closeouts in one pass: 1. DEVELOPMENT.md — consolidated hook-conventions table covering all 8 per-app hook types (tools / update-specifics / compose-tags / webui-refresh / the two traefik markers / the two network-provider hooks). One place to look instead of inferring from the codebase. 2. Nextcloud APCu wired alongside Redis: appUpdateSpecifics_nextcloud now sets memcache.local=\OC\Memcache\APCu too (was deferred from the fpm switch). APCu = cheap in-process cache; the fpm-alpine image ships the extension. CLI mode may emit a harmless "no memory cache" notice on `occ` runs — Nextcloud is graceful, the FPM worker still uses APCu fine. 3. Container-side file-capture rollout to 3 confident cases: - bookstack: lscr.io/linuxserver/bookstack with PUID=1000 → /config (1000:1000) - gitea: gitea/gitea with USER_UID=1000 → /data (1000:1000) - owncloud: owncloud/server (Apache/PHP) → /mnt/data (33:33, www-data) Snapshots are now complete for these (the dir's excluded from the raw restic pass and captured live through the container as a tar → libreportal-owned staging, same proven pattern as Nextcloud). Less-evidenced candidates left for live verification: linkding, mastodon, jellyfin, trilium, focalboard, invidious, vaultwarden, headscale-service — each needs its in-container uid confirmed before labeling (wrong uid won't break backup, but restore would chown to the wrong owner). Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- containers/bookstack/docker-compose.yml | 1 + containers/gitea/docker-compose.yml | 1 + .../scripts/nextcloud_update_specifics.sh | 11 +++++++--- containers/owncloud/docker-compose.yml | 1 + docs/DEVELOPMENT.md | 21 +++++++++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/containers/bookstack/docker-compose.yml b/containers/bookstack/docker-compose.yml index 1c93a4d..8f0f880 100755 --- a/containers/bookstack/docker-compose.yml +++ b/containers/bookstack/docker-compose.yml @@ -33,6 +33,7 @@ services: libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA libreportal.backup.db: "mariadb:bookstack_db:db:" + libreportal.backup.files: "bookstack:/config:data:1000:1000" traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA # TRAEFIK_PORT_1_BEGIN traefik.http.routers.bookstack-service.entrypoints: web,websecure diff --git a/containers/gitea/docker-compose.yml b/containers/gitea/docker-compose.yml index 2bc7861..b7c1fa8 100755 --- a/containers/gitea/docker-compose.yml +++ b/containers/gitea/docker-compose.yml @@ -65,6 +65,7 @@ services: libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA libreportal.backup.db: "sqlite:::data/gitea/gitea/gitea.db" + libreportal.backup.files: "gitea-service:/data:data/gitea:1000:1000" traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA # TRAEFIK_PORT_1_BEGIN traefik.http.routers.gitea-service.entrypoints: web,websecure diff --git a/containers/nextcloud/scripts/nextcloud_update_specifics.sh b/containers/nextcloud/scripts/nextcloud_update_specifics.sh index 2501bb8..bf4984c 100644 --- a/containers/nextcloud/scripts/nextcloud_update_specifics.sh +++ b/containers/nextcloud/scripts/nextcloud_update_specifics.sh @@ -24,16 +24,21 @@ appUpdateSpecifics_nextcloud() { return 0 fi - isNotice "Wiring Redis caching into Nextcloud..." + isNotice "Wiring Redis caching + APCu into Nextcloud..." local rc=0 + # APCu = in-process local cache (cheap, fast). The fpm image ships with the + # apcu PHP extension; under CLI mode it isn't enabled by default which makes + # `occ` emit a harmless "memory cache not available" notice — Nextcloud's + # graceful, the FPM worker still uses APCu fine. + runFileOp docker exec -u www-data nextcloud-service php occ config:system:set memcache.local --value '\OC\Memcache\APCu' >/dev/null 2>&1 || rc=1 runFileOp docker exec -u www-data nextcloud-service php occ config:system:set memcache.distributed --value '\OC\Memcache\Redis' >/dev/null 2>&1 || rc=1 runFileOp docker exec -u www-data nextcloud-service php occ config:system:set memcache.locking --value '\OC\Memcache\Redis' >/dev/null 2>&1 || rc=1 runFileOp docker exec -u www-data nextcloud-service php occ config:system:set redis host --value nextcloud-redis >/dev/null 2>&1 || rc=1 runFileOp docker exec -u www-data nextcloud-service php occ config:system:set redis port --value 6379 --type=integer >/dev/null 2>&1 || rc=1 runFileOp docker exec -u www-data nextcloud-service php occ config:system:set filelocking.enabled --value true --type=boolean >/dev/null 2>&1 || rc=1 if [[ $rc -eq 0 ]]; then - isSuccessful "Nextcloud → Redis caching + file-locking wired." + isSuccessful "Nextcloud → APCu (local) + Redis (distributed + file-locking) wired." else - isNotice "Redis wiring had errors (non-fatal; Nextcloud still functions without it)" + isNotice "Cache wiring had errors (non-fatal; Nextcloud still functions without it)" fi } diff --git a/containers/owncloud/docker-compose.yml b/containers/owncloud/docker-compose.yml index 0f7fa55..43eb06c 100755 --- a/containers/owncloud/docker-compose.yml +++ b/containers/owncloud/docker-compose.yml @@ -33,6 +33,7 @@ services: libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA libreportal.backup.db: "mariadb:owncloud-mariadb:mysql:" + libreportal.backup.files: "owncloud-service:/mnt/data:files:33:33" traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA # TRAEFIK_PORT_1_BEGIN traefik.http.routers.owncloud-service.entrypoints: web,websecure diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 741c3a4..ed8e921 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -192,6 +192,27 @@ is a dev-only escape hatch. `./config`, …) or nest them under `resources/`. (The scan is `maxdepth 3`, so one sourced subfolder level is the cap — keep `scripts/` flat; name files `_.sh` instead of nesting.) +- **Per-app hook conventions.** Every per-app extension is a conventionally-named + function the central code dispatches via `declare -F` — define it, the + framework picks it up; don't, you're a clean no-op. All hooks live in + `containers//scripts/_*.sh` unless noted. The hooks the framework + currently dispatches: + + | Hook function | When it runs | Notes | + |---|---|---| + | `app` | a Tools-tab button is clicked | declared in `tools/.tools.json`; function in `tools/_.sh`. Dispatched by `dockerAppRunTool`. | + | `appUpdateSpecifics_($app)` | end of every install / update | post-install fixups, side-effects. May set `shouldrestart=true` to request a restart. | + | `appSetupComposeTags_($file)` | during compose templating | fill computed (non-CFG) compose tags. CFG_``_* tags are filled generically — only needed for tags that require computation. | + | `appWebuiRefresh_` | every `webuiLibrePortalUpdate` while the app is installed | data that needs ongoing refresh (e.g. gluetun's provider list snapshot). | + | `appTraefikSkipsDefaultMiddleware_` | router setup | **marker only** — function presence means "opt out of `default@file` middleware." | + | `appTraefikExtraMiddlewares_` | router setup | echo extra middleware entries (one per line) to append to this app's chain. | + | `appNetworkApplyMode_($file)` | templating, when an app sets `CFG__NETWORK=` | switch the routed app's compose to the provider's networking (e.g. toggle the `GLUETUN_OFF`/`ON` regions). The `` is the provider's app slug; the hook lives in `containers//scripts/`. | + | `appNetworkRegisterPorts_` | templating + uninstall, for any provider with a hook | refresh the provider's forwarded-port wiring (so a routed app's ports appear / removed apps drop out). Self-skip when the provider isn't installed. | + + None of these need to be wired anywhere — the container scan live-sources + `containers//scripts/*.sh` (depth 3, only `resources/` pruned), and the + central dispatchers find the hook by name. So new apps adopting any of these + is just "create the file + define the function." - **What gets backed up** (so you know what's safe to regenerate vs. preserve): per-app backups capture only the live data dir `//` — the deployed compose, the live `.config` (settings + secrets), and the mounted