ba78eb060f49277c95acb96bdcda193f86e8b4fa
HA rejects proxied requests (400 Bad Request) without trusted_proxies in configuration.yaml. Uses entrypoint wrapper pattern (like romm) to ensure the http config block exists before HA starts. Handles both fresh deploys (creates full configuration.yaml) and existing installs (appends http block if missing). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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/
│ ├── 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/hdd_1"
locked_after_deploy: true
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.* |
| 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 | -- |
| 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/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 |
| deploy-felhom-compose | felhom-controller + deploy scripts |
| felhom.eu | Website + k3s manifests |
Description
Languages
Shell
100%