Files
app-catalog-felhom.eu/REUSE.md
T
admin 8ddd3c9da5 fix(healthcheck): sweep localhost -> 127.0.0.1 across all 48 templates
BusyBox wget (+ node/python/curl one-shots, incl mealie's socket tuple) resolve
localhost -> IPv6 ::1 with no cross-family fallback; an IPv4-only-binding app
reads docker-unhealthy while serving (vaultwarden, re-run 2026-07-06). Escalates
that instance to the class. Scoped strictly to healthcheck test: lines
(diff-reviewed: no env/config/label changed; .felhom.yml already clean). New
REUSE.md convention row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-06 20:25:54 +02:00

9.4 KiB

REUSE.md — app-catalog-felhom.eu

Before adding or editing an app, check here. Conventions to copy, traps to avoid. Maintenance: update in the SAME commit that changes a catalog-wide convention. Entries cite real files. Line numbers are landmarks only — reconfirm before editing.

1. Canonical helpers

None — this repo is templates/config, not code. See §2/§5.

2. Canonical patterns (copy structure from THE named file)

Pattern Canonical file Key traits
The one canonical example app templates/paperless-ngx/ (both files) Multi-container (app + postgres + redis), HDD + userdata mounts, full deploy_fields spectrum (domain/subdomain/secret/password/text/path/select). Copy this structure for any new app.
.felhom.yml required fields templates/paperless-ngx/.felhom.yml All 53 apps: display_name, description (Hungarian), category, subdomain, slug, resources{mem_request, mem_limit, pi_compatible, needs_hdd}, deploy_fields, app_info{tagline, use_cases, first_steps, ...}, healthcheck. Optional: smtp_mapping (email-capable apps), open_path (non-root landing page, e.g. ghost).
deploy_fields conventions templates/paperless-ngx/.felhom.yml (deploy_fields: block) Every app starts with DOMAIN (type domain) + SUBDOMAIN (type subdomain, locked_after_deploy: true). Secrets: type: secret + generate: — dominant generators password:24 (DB passwords) and hex:32 (app secret keys); password:16 for shown admin passwords (type: password). HDD apps add HDD_PATH (type: path, placeholder /mnt/felhom-drives/hdd_1, locked). Labels/descriptions in Hungarian.
Controller-side health probe templates/vaultwarden/.felhom.yml (healthcheck: block) healthcheck.checks[] with type: http (port only), type: api (port + path + expect.status: 200), or type: tcp (port only — mealie, crafty-controller). Prefer api with a real health path when the app has one.
Docker healthcheck host ALL templates/*/docker-compose.yml (healthcheck.test:) Always 127.0.0.1, never localhost. BusyBox wget (and node/python/curl one-shots) resolve localhost→IPv6 ::1 with NO cross-address-family fallback; an app that binds IPv4-only then reads docker-unhealthy while fully serving (vaultwarden, re-run 2026-07-06 — swept all 48 templates).
Docker healthcheck — BusyBox/wget images templates/vaultwarden/docker-compose.yml (~L49) test: ["CMD", "wget", "--spider", "-q", "http://localhost:<port>/<path>"]. Most common family (~20 apps, e.g. homebox, glance).
Docker healthcheck — curl-capable images templates/paperless-ngx/docker-compose.yml (~L76) test: ["CMD", "curl", "-f", "http://localhost:<port>/<path>"] (~18 apps: jellyfin, immich, sonarr…).
Docker healthcheck — Node images (no wget/curl) templates/rallly/docker-compose.yml (~L49) test: ["CMD", "node", "-e", "require('http').get(...)"] — used when the image lacks wget (that was rallly's actual bug).
Docker healthcheck — Python images templates/mealie/docker-compose.yml (~L47) test: ["CMD-SHELL", "python3 -c \"import socket; s=socket.create_connection(('localhost',<port>),2); s.close()\""] (mealie, crafty-controller). tandoor/wger use urllib.request variants for real HTTP checks.
Docker healthcheck — DB/Redis sidecars templates/paperless-ngx/docker-compose.yml (~L107, L129) postgres: pg_isready -U <user> -d <db>; mariadb: healthcheck.sh --connect --innodb_initialized; redis: redis-cli ping. App container gets depends_on: <db>: condition: service_healthy.
Memory convention templates/paperless-ngx/docker-compose.yml (~L71) + .felhom.yml resources: EVERY service has deploy.resources.limits.memory (compose is the enforcement). NO reservations anywhere. .felhom.yml mem_limit = SUM of all containers' limits (see paperless header comment: 768+256+128=1152M); mem_request = expected steady-state usage, display-only.
Compose file skeleton templates/paperless-ngx/docker-compose.yml (header) Header comment (app, domain, DB type, RAM math, Pi), restart: unless-stopped, TZ=Europe/Budapest, explicit container_name, traefik-public external network + <app>-internal for DBs, Traefik labels with Host(`${SUBDOMAIN}.${DOMAIN}`), named volumes for DB/config (NVMe), ${HDD_PATH}/appdata/<app>/... for bulk data, ${USERDATA_PATH}/... for customer-browsable content.
App-email (SMTP shim) opt-in templates/vaultwarden/.felhom.yml (smtp_mapping:) + README.md §smtp_mapping smtp_mapping maps shim host/port/security/from to the app's own env names; compose MUST reference the mapped ${VAR:-} keys with empty defaults. STARTTLS if the app can accept self-signed certs, else security_value: "NONE" plaintext (or the :2526 plaintext listener for STARTTLS-insistent clients — see calcom/nextcloud). TRAP: an image that treats defined-but-EMPTY mail vars as "set" (vaultwarden — campaign F1 2026-07-06) needs its own enable-flag gated false in compose and flipped "true" via smtp_mapping.extra; boot-prove a fresh email-off deploy for every new smtp-mapped app.
Probe-container naming templates/vaultwarden/docker-compose.yml (container_name: vaultwarden) + templates/sparkyfitness/ The controller-side healthcheck.checks[] probe dials the container whose name equals the stack (directory) name exactly; fallback = the FIRST running prefix-match, which in a multi-container stack can be the DB (verified: felhom-controller/internal/stacks/healthprobe.go findProbeContainer). So the Traefik-exposed service's container_name must be exactly the stack name; sidecars <app>-db, <app>-redis, ….

3. Dangerous lookalikes — do NOT copy

Trap Why it bites Use instead
templates/gokapi/docker-compose.yml custom entrypoint seeding config.json + --deployment-password One-off hack because Gokapi has no env-var headless setup (pinned to ConfigVersion 21 / v1.9.6). Copying this entrypoint pattern to another app will break on image updates. Normal env-var config via deploy_fields; entrypoint-seeding only as last resort.
templates.json + scripts/generate-customer.sh LEGACY Portainer-only mechanism (marked so in README.md). New apps do NOT need entries here; the controller syncs templates/<app>/ directly. Just templates/<app>/{docker-compose.yml,.felhom.yml}.
templates/uptime-kuma/docker-compose.yml test: ["CMD", "extra/healthcheck"] Image-provided binary, unique to this image — not a portable pattern. Pick the wget/curl/node/python family matching your image (§2).

4. Seams & interfaces (cross-repo)

  • Controller pulls this repo (default 15m) and copies templates/<app>/docker-compose.yml + .felhom.yml to /opt/docker/stacks/<app>/; it NEVER overwrites deployed app.yaml / .env; SHA-256 change detection. Contract description: README.md §"How It Works".
  • .felhom.yml is the contract surface consumed by felhom-controller (repo felhom-controller/): deploy_fields drive the deploy wizard, resources the deploy screen hints, healthcheck.checks the controller-side probe, smtp_mapping the app-email injection at deploy/redeploy.
  • Assets are NOT in this repo: logo/screenshots resolve from felhom.eu via slug ({assets.base_url}/assets/{slug}-logo.webp — see comment block in templates/paperless-ngx/.felhom.yml).
  • Commit+push to main IS the deploy: the controller picks changes up on next sync.

5. Extension points (adding a new app)

  1. templates/<app>/docker-compose.yml — copy templates/paperless-ngx/docker-compose.yml skeleton; every service needs container_name, restart: unless-stopped, TZ=Europe/Budapest, deploy.resources.limits.memory, a healthcheck (family per §2), Traefik labels on the web service, traefik-public external + <app>-internal network if it has a DB.
  2. templates/<app>/.felhom.yml — copy templates/paperless-ngx/.felhom.yml; required keys per §2; DOMAIN + SUBDOMAIN fields always; mem_limit = sum of compose limits; Hungarian user-facing text; healthcheck.checks probe.
  3. Update README.md App Catalog + Variable-types tables (convention — every existing app is listed).
  4. Skip templates.json / generate-customer.sh (legacy, §3).
  5. Email-capable app: add smtp_mapping + matching ${VAR:-} compose lines (§2 last row).

6. Known inconsistencies (observed — NOT fixed)

  • README.md lists templates/filebrowser/ in the tree, but no such directory exists; conversely templates/recipe-importer/ exists but is absent from README's tree and both catalog tables.
  • README.md field-type table (§".felhom.yml Format") omits subdomain (used by all 53 apps) and secret_input (templates/romm/.felhom.yml), and lists boolean which no descriptor uses.
  • README.md says smtp_mapping is "Currently mapped: Vaultwarden, Mealie" — six descriptors now carry it (calcom, gitea, mealie, nextcloud, rallly, vaultwarden).
  • Healthcheck URL style drifts: localhost vs 127.0.0.1, trailing-slash vs none, curl -f vs curl -sf, and two distinct Node one-liner styles (compact rallly vs verbose docmost).
  • One unquoted generator value (generate: password:24) among otherwise-quoted generate: "..." values.
  • The in-file comment block in .felhom.yml headers (e.g. templates/paperless-ngx/.felhom.yml "Generator types") omits base64key:N, which README.md documents and one app uses.