# felhom-controller **Central management container for Felhom home servers.** Replaces Portainer + scattered systemd scripts with a single, lightweight container that provides: - Hungarian-language web dashboard for customers - Docker Compose stack management (start/stop/update) - Backup orchestration (DB dumps + restic snapshots) - System health monitoring with Healthchecks pings - Git-based stack synchronization with update management - Self-update with automatic rollback on failure ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ Customer Hardware (N100 mini PC / Raspberry Pi) │ │ │ │ ┌──────────┐ ┌────────────────────────────────────────────┐ │ │ │ Traefik │ │ felhom-controller │ │ │ │ (reverse │──▶│ │ │ │ │ proxy) │ │ ┌──────────┐ ┌─────────────────────────┐│ │ │ └──────────┘ │ │ Web UI │ │ Stack Manager ││ │ │ │ │ (HU dash │ │ (compose up/down/pull, ││ │ │ ┌──────────┐ │ │ board) │ │ git sync, update mgmt) ││ │ │ │cloudflared│ │ └──────────┘ └─────────────────────────┘│ │ │ │ (tunnel) │ │ ┌──────────┐ ┌─────────────────────────┐│ │ │ └──────────┘ │ │ Backup │ │ Monitor & Pinger ││ │ │ │ │ (db dump │ │ (healthchecks pings, ││ │ │ ┌──────────┐ │ │ restic) │ │ system metrics) ││ │ │ │ App │ │ └──────────┘ └─────────────────────────┘│ │ │ │ stacks │ │ ┌──────────┐ ┌─────────────────────────┐│ │ │ │ (docker │ │ │Scheduler │ │ REST API ││ │ │ │ compose) │ │ │(cron-like│ │ (for UI + remote mgmt) ││ │ │ └──────────┘ │ │ jobs) │ └─────────────────────────┘│ │ │ │ └──────────┘ │ │ │ └────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ pings │ git pull ▼ ▼ status.felhom.eu gitea.dooplex.hu (Healthchecks on k3s) (stack definitions) ``` ## Module Overview | Module | Path | Responsibility | |--------|------|----------------| | **Config** | `internal/config/` | Load & validate controller.yaml | | **Stacks** | `internal/stacks/` | Docker Compose operations, catalog, container status | | **Backup** | `internal/backup/` | DB dumps, restic snapshots, restore | | **Monitor** | `internal/monitor/` | Health checks, Healthchecks pings, system metrics | | **Scheduler** | `internal/scheduler/` | Cron-like job runner for all periodic tasks | | **API** | `internal/api/` | REST API endpoints (consumed by web UI + remote mgmt) | | **Web** | `internal/web/` | Dashboard UI, static files, server-side templates | ## Stack Management ### How stacks get onto the machine 1. During initial setup, `deploy-felhom-compose.sh` clones the app catalog 2. Compose files + `.felhom.yml` metadata land in `/opt/docker/stacks//` 3. The controller periodically pulls from Git to detect changes ### First deployment flow (via dashboard) 1. Customer sees app card with "🚀 Telepítés" (Deploy) button 2. Clicks → deploy page shows: - **Auto-filled**: DOMAIN (from controller config), read-only - **Auto-generated**: DB passwords, secret keys (shown as "✓ Generated") - **User input**: HDD path, admin password, language, etc. - **"🎲 Generálás"** button next to password fields 3. Clicks "Telepítés" → controller: - Generates all secrets - Validates required fields (checks path exists, etc.) - Saves `app.yaml` (env vars + locked fields list) - Runs `docker compose up -d` with env vars injected 4. Post-deploy: locked fields (DB_PASSWORD, etc.) become read-only ### Update strategy Stack updates are classified in the Git repository via markers: | Marker | Behavior | |--------|----------| | No marker | Optional update — shown on dashboard, customer clicks "Update" | | `UPDATE_REQUIRED=true` | Mandatory — auto-applied during next update window | | `UPDATE_SECURITY=true` | Critical — applied immediately (within minutes) | The update window is configurable per customer (default: 03:00-05:00 local time). ### Protected stacks The following stacks cannot be stopped from the customer UI: - `traefik` (reverse proxy) - `cloudflared` (tunnel) - `felhom-controller` (this container) ## Backup Strategy The controller replaces Backrest and manages backups directly: 1. **DB dumps** (default 02:30): Discovers running database containers, dumps via pg_dump/mysqldump 2. **Restic snapshots** (default 03:00): Backs up `/opt/docker/stacks/` data + DB dumps 3. **Verification**: Periodically checks snapshot integrity 4. **Pruning**: Configurable retention (default: 7 daily, 4 weekly, 6 monthly) Backup status is displayed on the dashboard and reported to Healthchecks. ## Self-Update Mechanism 1. Controller checks for new image versions periodically 2. Before updating: creates a restic snapshot of its own config 3. Pulls new image, recreates container 4. Health check timeout (60s) — if new container doesn't become healthy → rollback 5. Rollback: restores previous image tag, restarts with old config ## Configuration ### Controller config (infrastructure only) Single YAML file per customer: `/opt/docker/felhom-controller/controller.yaml` Contains customer identity, infrastructure secrets, backup/monitoring settings. Does **not** contain app-specific config (HDD paths, DB passwords, etc.). See `configs/controller.yaml.example` for the full reference. ### Per-app config (created during deployment) Each deployed app gets an `app.yaml` in its stack directory: ```yaml # /opt/docker/stacks/paperless-ngx/app.yaml # Auto-generated by felhom-controller — do not edit locked fields manually deployed: true deployed_at: "2026-02-13T14:30:00Z" env: DOMAIN: "demo-felhom.eu" DB_PASSWORD: "a7f2b9c1e4d..." # locked PAPERLESS_SECRET_KEY: "8b3e..." # locked PAPERLESS_ADMIN_USER: "admin" # editable HDD_PATH: "/mnt/hdd_1" # locked locked_fields: - DB_PASSWORD - PAPERLESS_SECRET_KEY - DOMAIN - HDD_PATH ``` Fields are defined in each stack's `.felhom.yml` metadata file. See `configs/example-felhom-metadata.yml` for the full format. ### App assets (logos, screenshots, descriptions) Baked into the container image at build time — no external dependencies at runtime. Assets are synced from the felhom.eu website repo before building: ```bash make sync-assets # copies from ../felhom.eu/website/assets/ make sync-assets WEBSITE_ASSETS_DIR=/path # or specify custom path ``` Served locally at `/static/assets/`. Naming convention matches the website: | Asset | File pattern | Served at | |-------|-------------|-----------| | Logo (SVG) | `assets/{slug}-logo.svg` | `/static/assets/{slug}-logo.svg` | | Logo (PNG fallback) | `assets/{slug}-logo.png` | `/static/assets/{slug}-logo.png` | | Screenshot | `assets/{slug}-screenshot-{n}.webp` | `/static/assets/{slug}-screenshot-{n}.webp` | ## Build & Deploy ```bash # Build for both architectures make build-all # Build Docker image make docker-build # Push to registry make docker-push # Build for specific arch make build-amd64 make build-arm64 ``` ## Development ```bash # Run locally (needs Docker socket) go run ./cmd/controller/ --config configs/controller.yaml.example # Run tests go test ./... # Lint golangci-lint run ``` ## Repository Layout ``` felhom-controller/ ├── cmd/controller/ # Entry point │ └── main.go ├── internal/ │ ├── config/ # Configuration loading │ │ └── config.go │ ├── stacks/ # Docker Compose stack management │ │ ├── manager.go # Core: scan, start, stop, restart, update, logs │ │ ├── metadata.go # Parse .felhom.yml app metadata │ │ └── deploy.go # First-deploy flow: secret gen, app.yaml, compose up │ ├── backup/ # DB dumps + restic operations (Phase 3) │ ├── monitor/ # Health checks + metrics (Phase 2) │ ├── scheduler/ # Periodic job runner (Phase 2) │ ├── api/ # REST API │ │ └── router.go │ └── web/ # Dashboard UI │ ├── server.go # HTTP server, auth, page handlers │ └── templates.go # Embedded HTML templates + CSS (Hungarian) ├── configs/ # Example config files │ ├── controller.yaml.example │ └── example-felhom-metadata.yml ├── docs/ │ └── BUILDING.md # Container image build & registry guide ├── scripts/ │ └── hashpass.go # Password hash generator ├── Dockerfile # Multi-stage build (Go + debian-slim) ├── docker-compose.yml # Controller's own compose definition ├── Makefile # Build targets (amd64, arm64, docker) ├── go.mod └── README.md ``` ## Status & Roadmap ### Phase 1 — Stack Manager + Deploy Flow (current) - [x] Project skeleton & config format - [x] .felhom.yml app metadata format with deploy fields - [x] Per-app config persistence (app.yaml) - [x] Secret generation engine (password, hex, static) - [x] Stack catalog (read compose files + metadata from disk) - [x] Docker Compose operations (up/down/pull/ps/logs) - [x] Deploy flow with interactive field input - [x] Basic web dashboard with start/stop/deploy buttons - [x] REST API for stack + deploy operations - [x] Simple web authentication (bcrypt sessions) - [x] App logos + screenshots loaded from felhom.eu - [x] Container image build pipeline (Dockerfile + Makefile) - [ ] First build & test on N100 hardware - [ ] End-to-end test: deploy an app through dashboard ### Phase 2 — Monitoring & Health - [ ] System metrics collection (CPU, RAM, disk, temperature) - [ ] Healthchecks.io ping integration - [ ] Dashboard system health panel - [ ] Customer notifications (email/Telegram) ### Phase 3 — Backups - [ ] DB dump engine (PostgreSQL, MariaDB/MySQL, SQLite) - [ ] Restic integration (snapshot, prune, check) - [ ] Backup status on dashboard - [ ] Manual backup trigger from UI - [ ] Restore workflow ### Phase 4 — Git Sync & Updates - [ ] Periodic git pull for stack definitions - [ ] Update classification (optional/required/security) - [ ] Update window enforcement - [ ] Dashboard update notifications with "Update" button ### Phase 5 — Self-Update & Resilience - [ ] Self-update check & execution - [ ] Pre-update config backup - [ ] Health-based rollback mechanism - [ ] Config export/import ### Phase 6 — Central Management (future) - [ ] API authentication for remote management - [ ] Central dashboard on k3s querying all customer controllers - [ ] Fleet-wide update management