18 KiB
TASK: Infrastructure FileBrowser + Orphan Stack Handling + Catalog Fixes
Priority order: Task 1 → Task 2 → Task 3 (Task 3 is independent, can be done anytime)
Repositories involved:
deploy-felhom-compose— controller Go code, docker-setup.sh, hdd-setup.shapp-catalog-felhom.eu— template catalog
Task 1: FileBrowser Quantum as Infrastructure Service
Context
FileBrowser Quantum (gtstef/filebrowser:latest) becomes a mandatory infrastructure service
deployed alongside Traefik, Cloudflared, and felhom-controller. It provides the customer with
permanent web-based access to their HDD data — this is critical for the orphan deletion workflow
(Task 2), where users need to retrieve data from deleted apps.
FileBrowser is removed from the app catalog and instead deployed by docker-setup.sh during
initial server setup, just like Traefik.
1.1 — HDD Folder Structure Update (hdd-setup.sh)
Add new user-facing folders to the HDD folder structure arrays in hdd-setup.sh:
Current structure:
${HDD_PATH}/
├── media/
│ ├── downloads/complete/
│ ├── downloads/incomplete/
│ ├── movies/
│ ├── series/
│ ├── music/
│ └── books/
├── storage/
│ ├── immich/
│ ├── nextcloud/
│ ├── filebrowser/ ← REMOVE (filebrowser is now infra, doesn't need storage/)
│ ├── backups/local/
│ └── backups/appdata/
└── appdata/
Updated structure:
${HDD_PATH}/
├── Dokumentumok/ ← NEW: user documents (OnlyOffice, general files)
├── media/
│ ├── downloads/complete/
│ ├── downloads/incomplete/
│ ├── movies/
│ ├── series/
│ ├── music/
│ └── books/
├── storage/
│ ├── immich/
│ ├── nextcloud/
│ ├── backups/local/
│ └── backups/appdata/
└── appdata/
Changes to hdd-setup.sh:
- Remove
"storage/filebrowser"fromSTORAGE_DIRSarray - Add
"Dokumentumok"as a new top-level entry (add a newUSER_DIRSarray, or add to existing) - Ownership: same 1000:1000 as other dirs
1.2 — FileBrowser Docker Compose (Infrastructure)
Create /opt/docker/stacks/filebrowser/docker-compose.yml during docker-setup.sh execution.
Mount strategy — three tiers with different permissions:
| HDD Path | Container Mount | Access | Rationale |
|---|---|---|---|
${HDD_PATH}/storage/ |
/srv/storage |
read-only | App data, prevent accidental deletion |
${HDD_PATH}/media/ |
/srv/media |
read-write | User adds movies, music, books |
${HDD_PATH}/Dokumentumok/ |
/srv/Dokumentumok |
read-write | User documents (docx, xlsx, etc.) |
Docker Compose template:
# FileBrowser Quantum — Infrastructure file manager
# Domain: files.${DOMAIN}
# Deployed by docker-setup.sh — do NOT remove
#
# Mount permissions:
# /srv/storage/ → HDD storage/ (READ-ONLY — app data)
# /srv/media/ → HDD media/ (read-write — user media)
# /srv/Dokumentumok/ → HDD Dokumentumok/ (read-write — user documents)
services:
filebrowser:
image: gtstef/filebrowser:latest
container_name: filebrowser
restart: unless-stopped
environment:
- TZ=Europe/Budapest
volumes:
- filebrowser_data:/home/filebrowser/data
- ${HDD_PATH}/storage:/srv/storage:ro
- ${HDD_PATH}/media:/srv/media
- ${HDD_PATH}/Dokumentumok:/srv/Dokumentumok
networks:
- traefik-public
deploy:
resources:
limits:
memory: 256M
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:80/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
labels:
- "traefik.enable=true"
- "traefik.http.routers.filebrowser.rule=Host(`files.${DOMAIN}`)"
- "traefik.http.routers.filebrowser.entrypoints=websecure"
- "traefik.http.routers.filebrowser.tls=true"
- "traefik.http.routers.filebrowser.tls.certresolver=letsencrypt"
- "traefik.http.services.filebrowser.loadbalancer.server.port=80"
- "traefik.docker.network=traefik-public"
volumes:
filebrowser_data:
networks:
traefik-public:
external: true
Default credentials: admin / admin (user should change on first login).
NOTE: The healthcheck endpoint /health should be verified against FileBrowser Quantum docs.
If it doesn't exist, fall back to wget --spider -q http://localhost:80/.
1.3 — Deploy in docker-setup.sh
Add a new step in docker-setup.sh that deploys FileBrowser after Traefik is running
and after HDD is mounted (HDD_PATH must be known).
Implementation notes:
- The step should come after
install_traefik()and after HDD detection/mount - Requires
HDD_PATHto be set — if no HDD is configured, skip FileBrowser deployment and log a warning: "FileBrowser skipped — no HDD path configured. Deploy manually after HDD setup." - Create the compose file from template (substitute
${DOMAIN}and${HDD_PATH}) - Create a
.envfile in the filebrowser stack dir withDOMAINandHDD_PATH docker compose up -d- Verify container is running
New function: install_filebrowser()
1.4 — Add to Protected Stacks
Update controller.yaml.example to include filebrowser in the protected list:
stacks:
protected:
- "traefik"
- "cloudflared"
- "felhom-controller"
- "filebrowser" # ← ADD
Also update any hardcoded protected stack references in documentation/README.
1.5 — Remove FileBrowser from App Catalog
In app-catalog-felhom.eu repository:
- Delete
templates/filebrowser/directory (docker-compose.yml + .felhom.yml) - Delete
existing-appinfo/filebrowser-appinfo.ymlif it exists - FileBrowser should no longer appear in the "Alkalmazások" catalog on the dashboard
1.6 — Dashboard UI for Infrastructure Services
Currently the dashboard shows catalog apps. Infrastructure services (traefik, cloudflared, controller, filebrowser) are hidden. Consider adding a small "Rendszer" (System) section at the bottom of the sidebar or dashboard that shows infrastructure service status.
This is optional / future work — not blocking. The controller already knows about
protected stacks via IsProtectedStack(). The UI just needs to render them differently
if this section is added.
Task 2: Orphan Stack Detection and Deletion
Context
When an app is removed from the catalog (e.g., Stirling-PDF replaced by BentoPDF), its stack
directory may still exist in /opt/docker/stacks/ with containers still deployed. These
"orphaned" stacks need to be visible on the dashboard with a clear state and deletable by the user.
Because FileBrowser (Task 1) gives users permanent access to their HDD data, the delete flow can safely remove Docker volumes while informing users their HDD files are still accessible.
2.1 — New Stack State: orphaned
Add a new constant in the stacks package:
StateOrphaned ContainerState = "orphaned"
An orphaned stack is defined as:
- Has a
docker-compose.ymlin/opt/docker/stacks/<name>/ - Has
app.yamlwithdeployed: true - Does NOT have a matching template in the synced catalog
2.2 — Orphan Detection in ScanStacks()
After the existing scan loop in ScanStacks(), add orphan detection:
// After scanning all stack dirs, check which deployed stacks have no catalog template
catalogTemplates := m.getCatalogTemplateSlugs() // returns set of slugs from synced catalog
for name, stack := range m.stacks {
if stack.Protected {
continue // infrastructure stacks are never orphaned
}
if !stack.Deployed {
continue // not deployed = just an available template, not orphaned
}
if !catalogTemplates[name] {
stack.Orphaned = true
}
}
Add Orphaned field to Stack struct:
type Stack struct {
Name string `json:"name"`
Meta Metadata `json:"meta"`
ComposePath string `json:"compose_path"`
State ContainerState `json:"state"`
Deployed bool `json:"deployed"`
Protected bool `json:"protected"`
Orphaned bool `json:"orphaned"` // ← ADD
Containers []ContainerInfo `json:"containers"`
AppConfig *AppConfig `json:"app_config,omitempty"`
LastUpdated time.Time `json:"last_updated"`
}
getCatalogTemplateSlugs() needs to read the synced catalog directory and return
a map[string]bool of all template slugs that have a docker-compose.yml. The synced
catalog lives at the path configured in git.local_path or wherever the catalog sync
stores templates after git pull. Check the existing catalogsync package for the exact path.
2.3 — Dashboard UI for Orphaned Stacks
Orphaned stacks appear in the deployed apps list with distinct visual treatment:
Visual styling:
- Left border: amber/yellow (instead of green for running or gray for stopped)
- Badge:
Elavult(Deprecated) — amber background, dark text - App name still shown from
.felhom.ymlmetadata (if available) or directory name - Show current state (running/stopped) alongside the orphan badge
Available actions for orphaned stacks:
- ✅ Start / Stop (normal controls — user may need to run it briefly)
- ✅ View logs
- ✅ Törlés (Delete) button — NEW, only shown for orphaned stacks
- ❌ No "Frissítés" (Update) — no catalog template to update from
- ❌ No "Beállítások" (Settings) — no deploy_fields to configure
2.4 — Delete API Endpoint
Endpoint: DELETE /api/stacks/{name}
Request body:
{
"remove_hdd_data": false
}
Preconditions (return 409 Conflict if violated):
- Stack must be stopped (State != running). Force the user to stop first.
- Stack must be orphaned (for now — catalog apps cannot be deleted, only stopped). In the future this could be relaxed, but for safety, start with orphan-only deletion.
Execution steps:
1. Verify preconditions (stopped + orphaned)
2. Read docker-compose.yml to identify:
a. Named Docker volumes (from `volumes:` top-level section)
b. HDD bind mounts (paths starting with ${HDD_PATH})
3. Run: docker compose down --rmi local --volumes
- This removes containers, local images, AND named Docker volumes (SSD data)
- Named volumes (configs, databases, caches) are always removed — they're useless
without the app and are the #1 cause of "Docker ate my disk space"
4. If remove_hdd_data == true:
a. For each HDD bind mount found in step 2:
- Calculate size: du -sh <path>
- Remove: rm -rf <path>
- Log: "[INFO] Removed HDD data: <path> (<size>)"
b. WARNING: Never rm -rf ${HDD_PATH} itself or ${HDD_PATH}/media/ or
${HDD_PATH}/Dokumentumok/ — only remove app-specific subdirectories
like ${HDD_PATH}/storage/paperless/ or ${HDD_PATH}/storage/immich/
5. Remove stack directory: rm -rf /opt/docker/stacks/<name>/
6. Log the complete delete action with timestamp
7. Trigger ScanStacks() to refresh dashboard
8. Return 200 OK with summary
Response body:
{
"deleted": "stirling-pdf",
"volumes_removed": ["stirling_pdf_data"],
"hdd_paths_removed": [],
"hdd_paths_preserved": ["/mnt/hdd_1/storage/stirling-pdf (245 MB)"]
}
Safety guards:
- Protected stacks can never be deleted (check
IsProtectedStack()) - Running stacks can never be deleted (must stop first)
- Only orphaned stacks can be deleted (for now)
- HDD data deletion is opt-in (default false)
- Never delete top-level HDD directories (media/, storage/, Dokumentumok/)
- Log every delete action with full details
2.5 — HDD Data Discovery for Delete Dialog
The delete confirmation dialog needs to show what HDD data exists and its size.
New endpoint: GET /api/stacks/{name}/hdd-data
Parses the stack's docker-compose.yml to find HDD bind mounts, checks if paths exist
on disk, and returns size info:
{
"stack": "stirling-pdf",
"hdd_paths": [
{
"path": "/mnt/hdd_1/storage/stirling-pdf",
"size_bytes": 256901120,
"size_human": "245 MB",
"exists": true
}
],
"has_hdd_data": true
}
If no HDD bind mounts exist (SSD-only app like Vaultwarden, Mealie), return:
{
"stack": "vaultwarden",
"hdd_paths": [],
"has_hdd_data": false
}
2.6 — Delete Confirmation Dialog (UI)
Full dialog (when HDD data exists):
┌──────────────────────────────────────────────────────┐
│ Stirling-PDF törlése │
│ │
│ ⚠ Ez az alkalmazás már nem érhető el a │
│ katalógusban. │
│ │
│ Az alkalmazás eltávolítása magában foglalja a │
│ konténereket, beállításokat és belső adatbázist. │
│ │
│ ☐ Felhasználói adatok törlése │
│ 📁 /srv/storage/stirling-pdf (245 MB) │
│ ℹ Ha nem törli, a Fájlkezelőben továbbra is │
│ elérheti ezeket a fájlokat. │
│ │
│ [Mégse] [Törlés] │
└──────────────────────────────────────────────────────┘
Notes:
- Show the FileBrowser-relative path (
/srv/storage/...) not the system path — this is what the user sees in FileBrowser - The "Felhasználói adatok törlése" checkbox is unchecked by default
- The info hint reminds users about FileBrowser access (Task 1 must be completed first)
- "Törlés" button should be red/destructive styling
Simple dialog (no HDD data — SSD-only apps):
┌──────────────────────────────────────────────────────┐
│ Vaultwarden törlése │
│ │
│ ⚠ Ez az alkalmazás már nem érhető el a │
│ katalógusban. │
│ │
│ Az alkalmazás és minden adata véglegesen törlődik. │
│ │
│ [Mégse] [Törlés] │
└──────────────────────────────────────────────────────┘
No checkbox needed — there's nothing optional to preserve.
2.7 — Router Registration
Add to the API router:
r.HandleFunc("/api/stacks/{name}/hdd-data", r.getStackHDDData).Methods("GET")
r.HandleFunc("/api/stacks/{name}", r.deleteStack).Methods("DELETE")
Both require authentication (same as existing stack endpoints).
Task 3: App Catalog Fixes
These are independent template fixes in the app-catalog-felhom.eu repository.
3.1 — BentoPDF: Change Subdomain to pdf.*
<should be done already, verify>
3.2 — Calibre-Web → Calibre-Web-Automated
<should be done already, verify>
3.3 — FileBrowser → FileBrowser Quantum (Catalog Removal)
Since FileBrowser is now infrastructure (Task 1), remove it from the catalog entirely:
- Delete
templates/filebrowser/directory - Delete
existing-appinfo/filebrowser-appinfo.yml - The existing
filebrowsercatalog entry in any customer's deployed stacks will become orphaned (Task 2 handles this gracefully)
Note: If a customer already has the old catalog-based FileBrowser deployed, it will show
as orphaned after catalog sync. They can delete it via the orphan workflow. The infrastructure
FileBrowser (Task 1) will already be running at files.${DOMAIN}.
Implementation Checklist
deploy-felhom-compose repository
- hdd-setup.sh: Add
Dokumentumok/to folder structure, removestorage/filebrowser - docker-setup.sh: Add
install_filebrowser()function - controller.yaml.example: Add
filebrowsertostacks.protectedlist - stacks/manager.go (or equivalent):
- Add
Orphanedfield toStackstruct - Add
StateOrphanedconstant - Add orphan detection in
ScanStacks() - Add
getCatalogTemplateSlugs()helper
- Add
- stacks/delete.go (new file or add to manager):
DeleteStack()method with volume + HDD cleanupGetStackHDDData()method for size discovery- HDD path parsing from docker-compose.yml
- Safety guards (protected, running, top-level dir protection)
- api/router.go:
DELETE /api/stacks/{name}endpointGET /api/stacks/{name}/hdd-dataendpoint
- templates/dashboard.html (or relevant UI template):
- Orphan badge styling (amber)
- Delete button for orphaned stacks
- Delete confirmation dialog with HDD data info
- FileBrowser hint in delete dialog
- README.md: Update protected stacks list, document delete flow
app-catalog-felhom.eu repository
- Delete
templates/stirling-pdf/(if exists) - Delete
templates/filebrowser/(moved to infra) - Delete
existing-appinfo/filebrowser-appinfo.yml - Update
templates/bentopdf/— subdomainbento.*→pdf.* - Replace
templates/calibre-web/with calibre-web-automated version - Verify all YAML files parse without errors
- README.md: Update accordingly