Upstream stopped publishing v-prefixed tags on ghcr.io/sysadminsmedia/homebox. Neither the new v0.26.2 NOR the previously shipped v0.16.3 resolves any more -- so homebox was already undeployable from the catalog before this campaign; the deploy is accepted and then no container is ever created because the pull fails. Pinned to the un-prefixed 0.26.2, which resolves. Campaign 7 catalog sweep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nn3VgQk9iwEGgyx6QJ2NvE
Felhom App Catalog
Central repository for Felhom customer application templates.
Architecture
app-catalog-felhom.eu/ <- This repo (source of truth)
├── templates.json # Portainer App Templates index (LEGACY — Portainer-only setups)
├── templates/ # Docker Compose templates with ${VAR} env var syntax
│ ├── actualbudget/
│ │ ├── docker-compose.yml
│ │ └── .felhom.yml # App metadata for felhom-controller
│ ├── adventurelog/
│ ├── audiobookshelf/
│ ├── bentopdf/
│ ├── bookstack/
│ ├── calcom/
│ ├── calibre-web/
│ ├── claper/
│ ├── code-server/
│ ├── crafty-controller/
│ ├── docmost/
│ ├── emby/
│ ├── filebrowser/
│ ├── ghost/
│ ├── gitea/
│ ├── glance/
│ ├── gokapi/
│ ├── grafana/
│ ├── gramps-web/
│ ├── home-assistant/
│ ├── homebox/
│ ├── homepage/
│ ├── immich/
│ ├── jellyfin/
│ ├── kimai/
│ ├── komga/
│ ├── mealie/
│ ├── n8n/
│ ├── navidrome/
│ ├── nextcloud/
│ ├── onlyoffice/
│ ├── opengist/
│ ├── outline/
│ ├── paperless-ngx/
│ ├── papra/
│ ├── plant-it/
│ ├── plex/
│ ├── privatebin/
│ ├── radarr/
│ ├── rallly/
│ ├── romm/
│ ├── seerr/
│ ├── sonarr/
│ ├── sparkyfitness/
│ ├── tandoor/
│ ├── termix/
│ ├── uptime-kuma/
│ ├── vaultwarden/
│ ├── vikunja/
│ ├── wanderer/
│ ├── wger/
│ ├── wishlist/
│ └── zipline/
└── scripts/
└── generate-customer.sh # LEGACY — generates customer-specific templates for Portainer
How It Works (Controller-based deployments)
The felhom-controller syncs directly from this repo:
- Controller periodically pulls this repo (configurable interval, default 15m)
- Copies
docker-compose.ymland.felhom.ymlfromtemplates/<app>/to/opt/docker/stacks/<app>/ - Never overwrites
app.yaml(deployed config/secrets) or.envfiles - Only copies files if content has actually changed (SHA-256 comparison)
- After sync, triggers a stack rescan so new/updated apps appear on the dashboard
- Manual sync available via "Sablonok frissítése" button on the Alkalmazások page
Controller git config (in controller.yaml)
git:
repo_url: "https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git"
branch: "main"
sync_interval: "15m"
username: "" # Optional, for private repos
token: "" # Optional, for private repos
.felhom.yml Format
Each app template has a .felhom.yml metadata file that the controller uses for:
- Display info on the dashboard (name, description, category, subdomain)
- Resource hints (memory request/limit, Pi compatibility, HDD requirement)
- Deploy fields (what the user fills in during first deployment)
Field types
| Type | Description |
|---|---|
domain |
Auto-filled from controller config, read-only |
secret |
Auto-generated, hidden from user (user sees "Generated ✓") |
password |
Auto-generated but shown, user can override |
path |
Filesystem path (validated for existence) |
text |
Free text input |
select |
Dropdown with predefined options |
boolean |
Toggle switch |
Generator types (for secret/password fields)
| Generator | Description |
|---|---|
password:N |
N chars alphanumeric |
hex:N |
N bytes hex-encoded |
base64key:N |
base64: + N random bytes base64-encoded (Laravel APP_KEY format) |
static:VAL |
Fixed value |
Example .felhom.yml
display_name: "Paperless-ngx"
description: "Dokumentumok digitalizálása és rendszerezése"
category: "productivity"
subdomain: "paperless"
slug: "paperless-ngx"
resources:
mem_request: "500M"
mem_limit: "1152M"
pi_compatible: true
needs_hdd: true
deploy_fields:
- env_var: DOMAIN
label: "Domain"
type: domain
locked_after_deploy: true
- env_var: DB_PASSWORD
label: "Adatbázis jelszó"
type: secret
generate: "password:24"
locked_after_deploy: true
- env_var: HDD_PATH
label: "Adattárolási útvonal"
type: path
required: true
placeholder: "/mnt/felhom-drives/hdd_1"
locked_after_deploy: true
App-email mapping (smtp_mapping)
Apps that can send outbound email (password resets, invites, confirmations) get it through
one managed path: app → in-controller SMTP shim → hub → Resend. The Resend key stays
hub-side; nothing app-specific lives on the box. An app opts in by declaring smtp_mapping,
which renames the generic relay settings to that app's own env-var names.
When app-email is on (the household's global toggle AND the app's per-app toggle),
the controller injects at deploy/redeploy: host = the on-box shim, port = 2525,
from = <from_local>@felhom.eu, the security value, the optional display name, and any
fixed extra vars. The values are derived from settings on every compose — never written to
app.yaml — so a toggle change applies on the next redeploy without touching secrets. The
shim accepts no-auth on the Docker network, so leave any SMTP_USERNAME/SMTP_PASSWORD
unset.
The compose file must reference the mapped ${VAR} keys (with a harmless default) so the
injected values reach the container, e.g. - SMTP_HOST=${SMTP_HOST:-}. An empty SMTP_HOST
keeps the app's mail disabled when the toggle is off.
| Field | Meaning |
|---|---|
host_var |
env key receiving the shim host (required) |
port_var |
env key receiving the port 2525 (required) |
security_var |
env key receiving the TLS mode (optional) |
security_value |
the app's term for the chosen mode — starttls, TLS, NONE, … |
from_var |
env key receiving the From address (required) |
from_name_var |
env key receiving the From display name (optional) |
from_local |
local-part of the From address (defaults to the app slug) → <local>@felhom.eu |
extra |
fixed extra env (e.g. accept-invalid-cert flags) |
# Vaultwarden — STARTTLS to the shim (it accepts the self-signed cert):
smtp_mapping:
host_var: SMTP_HOST
port_var: SMTP_PORT
security_var: SMTP_SECURITY
security_value: starttls
from_var: SMTP_FROM
from_name_var: SMTP_FROM_NAME
from_local: vaultwarden
extra:
SMTP_ACCEPT_INVALID_CERTS: "true"
SMTP_ACCEPT_INVALID_HOSTNAMES: "true"
TLS choice per app. Prefer STARTTLS to the shim for apps that can accept a self-signed cert (an "accept invalid certs" option). For apps that can't (e.g. Mealie), use plaintext (
security_value: "NONE") on2525— the shim offers it on the Docker network and it was spike-validated. Never publish the shim off-box, so plaintext there is safe.
Currently mapped: Vaultwarden, Mealie (the two apps proven in
SPIKE-smtp-app-relay-2026-06-28). Adding email to another app is just its smtp_mapping
block + the matching compose ${VAR} lines.
App Catalog
| App | DB Type | RAM (request / limit) | Pi | HDD Data | Subdomain |
|---|---|---|---|---|---|
| ActualBudget | None (file) | 50M / 256M | yes | -- | budget.* |
| AdventureLog | PostgreSQL | 100M / 384M | yes | -- | travel.* |
| Audiobookshelf | None (file) | 100M / 512M | yes | ${HDD_PATH}/media/audiobooks/ |
audiobooks.* |
| BentoPDF | None (file) | 100M / 384M | yes | -- | pdf.* |
| BookStack | MariaDB | 150M / 512M | yes | -- | wiki.* |
| Cal.com | PostgreSQL | 200M / 768M | no | -- | cal.* |
| Calibre-Web Automated | None (file) | 200M / 768M | no | ${HDD_PATH}/media/books/ |
books.* |
| Claper | PostgreSQL | 100M / 384M | yes | -- | present.* |
| Code-Server | None (file) | 200M / 1024M | no | -- | code.* |
| Crafty Controller | None (file) | 256M / 2048M | no | -- | minecraft.* |
| Docmost | PostgreSQL + Redis | 200M / 768M | no | -- | docs.* |
| Emby | None (file) | 512M / 2048M | no | ${HDD_PATH}/media/ |
emby.* |
| FileBrowser Quantum | None (file) | 50M / 256M | yes | ${HDD_PATH}/storage/filebrowser/ |
files.* |
| Ghost | SQLite | 150M / 512M | no | -- | blog.* |
| Gitea | SQLite | 100M / 512M | yes | -- | git.* |
| Glance | None (file) | 20M / 128M | yes | -- | dashboard.* |
| Gokapi | None (file) | 30M / 128M | yes | -- | share.* |
| Grafana | None (file) | 100M / 512M | yes | -- | grafana.* |
| Gramps Web | None (file) | 100M / 384M | yes | -- | family.* |
| Home Assistant | None (file) | 256M / 1024M | yes | -- | ha.* |
| Homebox | None (SQLite) | 50M / 256M | yes | -- | inventory.* |
| Homepage | None (file) | 50M / 256M | yes | -- | home.* |
| Immich | PostgreSQL + Redis | 2048M / 4096M | no | ${HDD_PATH}/storage/immich/ |
photos.* |
| Jellyfin | None (file) | 512M / 2048M | no | ${HDD_PATH}/media/ |
media.* |
| Kimai | MariaDB | 100M / 384M | yes | -- | time.* |
| Komga | None (file) | 200M / 512M | yes | ${HDD_PATH}/media/comics/ |
comics.* |
| Mealie | None (SQLite) | 200M / 1000M | yes | -- | recipes.* |
| n8n | None (file) | 150M / 512M | no | -- | auto.* |
| Navidrome | None (file) | 50M / 256M | yes | ${HDD_PATH}/media/music/ |
music.* |
| Nextcloud | MariaDB + Redis | 256M / 1024M | no | ${HDD_PATH}/storage/nextcloud/ |
cloud.* |
| OnlyOffice | None (file) | 512M / 2048M | no | -- | office.* |
| OpenGist | None (file) | 30M / 128M | yes | -- | gist.* |
| Outline | PostgreSQL + Redis | 200M / 768M | no | -- | kb.* |
| Paperless-ngx | PostgreSQL + Redis | 500M / 1152M | yes | ${HDD_PATH}/storage/paperless/ |
paperless.* |
| Papra | None (file) | 50M / 256M | yes | -- | papra.* |
| Plant-it | None (file) | 50M / 256M | yes | -- | plants.* |
| Plex | None (file) | 512M / 2048M | no | ${HDD_PATH}/media/ |
plex.* |
| PrivateBin | None (file) | 30M / 128M | yes | -- | paste.* |
| Radarr | None (file) | 150M / 512M | yes | ${HDD_PATH}/media/ |
radarr.* |
| Rallly | PostgreSQL | 50M / 256M | yes | -- | poll.* |
| RomM | MariaDB + Redis | 300M / 1024M | no | ${HDD_PATH}/storage/romm/ |
arcade.* |
| Jellyseerr | None (file) | 100M / 384M | yes | -- | requests.* |
| Sonarr | None (file) | 150M / 512M | yes | ${HDD_PATH}/media/ |
sonarr.* |
| SparkyFitness | PostgreSQL | 400M / 1792M | no | -- | sparky.* |
| Tandoor Recipes | PostgreSQL | 150M / 512M | yes | -- | recipes.* |
| Termix | None (file) | 30M / 128M | yes | -- | terminal.* |
| Uptime Kuma | None (file) | 50M / 256M | yes | -- | status.* |
| Vaultwarden | None (SQLite) | 50M / 256M | yes | -- | vault.* |
| Vikunja | None (file) | 50M / 256M | yes | -- | tasks.* |
| Wanderer | None (file) | 100M / 384M | yes | -- | hike.* |
| wger | SQLite | 100M / 384M | yes | -- | fitness.* |
| Wishlist | None (file) | 30M / 128M | yes | -- | wishes.* |
| Zipline | PostgreSQL | 100M / 512M | no | -- | img.* |
Variable types per app
| App | DOMAIN | HDD_PATH | Secrets |
|---|---|---|---|
| ActualBudget | yes | -- | -- |
| AdventureLog | yes | -- | SECRET_KEY, DB_PASSWORD |
| Audiobookshelf | yes | yes | -- |
| BentoPDF | yes | -- | -- |
| BookStack | yes | -- | APP_KEY, DB_PASSWORD |
| Cal.com | yes | -- | NEXTAUTH_SECRET, CALENDSO_ENCRYPTION_KEY, DB_PASSWORD |
| Calibre-Web Automated | yes | yes | -- |
| Claper | yes | -- | SECRET_KEY_BASE, DB_PASSWORD |
| Code-Server | yes | -- | PASSWORD |
| Crafty Controller | yes | -- | -- |
| Docmost | yes | -- | APP_SECRET, DB_PASSWORD |
| Emby | yes | yes | -- |
| FileBrowser Quantum | yes | yes | -- |
| Ghost | yes | -- | -- |
| Gitea | yes | -- | -- |
| Glance | yes | -- | -- |
| Gokapi | yes | -- | -- |
| Grafana | yes | -- | GF_SECURITY_ADMIN_PASSWORD |
| Gramps Web | yes | -- | GRAMPSWEB_SECRET_KEY |
| Home Assistant | yes | -- | -- |
| Homebox | yes | -- | -- |
| Homepage | yes | -- | -- |
| Immich | yes | yes | DB_PASSWORD |
| Jellyfin | yes | yes | -- |
| Kimai | yes | -- | DB_PASSWORD, ADMIN_EMAIL, ADMIN_PASSWORD |
| Komga | yes | yes | -- |
| Mealie | yes | -- | -- |
| n8n | yes | -- | N8N_ENCRYPTION_KEY |
| Navidrome | yes | yes | -- |
| Nextcloud | yes | yes | DB_PASSWORD, MYSQL_ROOT_PASSWORD, NEXTCLOUD_ADMIN_USER, NEXTCLOUD_ADMIN_PASSWORD |
| OnlyOffice | yes | -- | JWT_SECRET |
| OpenGist | yes | -- | -- |
| Outline | yes | -- | SECRET_KEY, UTILS_SECRET, DB_PASSWORD |
| Paperless-ngx | yes | yes | PAPERLESS_SECRET_KEY, DB_PASSWORD, PAPERLESS_ADMIN_USER, PAPERLESS_ADMIN_PASSWORD |
| Papra | yes | -- | -- |
| Plant-it | yes | -- | JWT_SECRET |
| Plex | yes | yes | PLEX_CLAIM |
| PrivateBin | yes | -- | -- |
| Radarr | yes | yes | -- |
| Rallly | yes | -- | SECRET_PASSWORD, DB_PASSWORD |
| RomM | yes | yes | DB_PASSWORD, MYSQL_ROOT_PASSWORD, ROMM_AUTH_SECRET_KEY |
| Jellyseerr | yes | -- | -- |
| Sonarr | yes | yes | -- |
| SparkyFitness | yes | -- | DB_PASSWORD, APP_DB_PASSWORD, API_ENCRYPTION_KEY, BETTER_AUTH_SECRET |
| Tandoor Recipes | yes | -- | SECRET_KEY, DB_PASSWORD |
| Termix | yes | -- | -- |
| Uptime Kuma | yes | -- | -- |
| Vaultwarden | yes | -- | ADMIN_TOKEN |
| Vikunja | yes | -- | VIKUNJA_SERVICE_JWTSECRET |
| Wanderer | yes | -- | MEILI_MASTER_KEY |
| wger | yes | -- | SECRET_KEY |
| Wishlist | yes | -- | -- |
| Zipline | yes | -- | CORE_SECRET, DB_PASSWORD |
Storage strategy
- HDD host paths (
${HDD_PATH}/storage/...): Large user data — photos, documents, ROMs - Named Docker volumes (on internal SSD): Databases, app config, caches — need fast I/O
- Templates without
${HDD_PATH}work without an external HDD (e.g., ActualBudget, Mealie)
Docker Compose template standards
All templates follow these standards (enforced via audit):
${DOMAIN}variable syntax for all domain references (not hardcoded)deploy.resources.limits.memoryon every service container- Healthchecks on every service (appropriate for service type)
restart: unless-stoppedon every serviceTZ=Europe/Budapeston every servicetraefik-publicexternal network + internal network for DB services- Explicit
container_name:on every service - Header comment block with: app name, domain, DB type, RAM, Pi compatibility
depends_onwithcondition: service_healthyfor DB/Redis dependencies
Legacy: Portainer-based deployments
The generate-customer.sh script and templates.json are kept for Portainer-only setups
where the felhom-controller is not used. For controller-based deployments, these are not needed.
Legacy workflow
# Generate customer templates (on your workstation)
./scripts/generate-customer.sh --customer demo-felhom \
--domain demo-felhom.eu --hdd-path /mnt/felhom-drives/hdd_1 --push
Adding a New App
- Create
templates/<appname>/docker-compose.ymlfollowing the template standards above - Create
templates/<appname>/.felhom.ymlfollowing the metadata format - Commit and push — the controller will pick it up on next sync
- (Legacy) If also needed for Portainer: add entry to
templates.jsonandgenerate-customer.sh
Related Repositories
| Repository | Purpose |
|---|---|
| app-catalog-felhom.eu | This repo — templates + metadata |
| felhom-controller | felhom-controller + deploy scripts |
| felhom.eu | Website + k3s manifests |