Why this matters beyond the homelab: A dashboard that shows what’s running is only useful if it stays accurate without becoming its own maintenance burden. This rebuild was as much about how the config gets deployed as it was about what the dashboard shows.
What changed
The old Homepage instance lived inside a shared apps compose stack alongside n8n and Vaultwarden, which meant it went down or restarted any time something unrelated in that stack needed attention. It’s now split out into its own standalone stack, its own compose file, deployed independently. The apps stack now holds only what actually needs to share that lifecycle: n8n and Vaultwarden.
The dashboard itself went from a flat list to three tabs, Operations, Services, and SSH, styled to match the site’s own visual theme rather than Homepage’s defaults. Thirty services are represented, 17 of them with live widgets rather than static bookmarks, plus per-guest Proxmox stats and one-click SSH copy strips for the boxes accessed most often.
The secrets problem
Homepage’s config format wants API keys and tokens written directly into YAML files. That’s fine for a lab with one or two integrations. At 30 services it’s a spreadsheet of plaintext credentials sitting in a config file, which is exactly the pattern the fleet had spent the last several sprints migrating everything else away from.
The fix was a deploy script that pulls all 15 required variables from Infisical at deploy time using a scoped machine identity, injects them into the running config, and never writes the plaintext values to the compose file or the repo. The config in version control has no secrets in it at all. Deploying is running one script, not manually copying values into a YAML file and hoping nobody commits it by accident.
What’s still not working
Two integrations aren’t playing along yet. The UniFi widget can’t authenticate because the controller’s Network Integrations API key feature isn’t available on the current firmware, that’s a vendor limitation, not a config problem. The AdGuard widget fails silently: the environment variable confirmed injected correctly, no errors in any log, and it just doesn’t render. Worth flagging honestly rather than pretending the dashboard is fully wired up. Seventeen of nineteen integrations working cleanly is a good outcome, not a perfect one.
The lesson
A dashboard covering 30 services is itself a piece of infrastructure with its own failure modes, not just a read-only window into everything else. Splitting it out of a shared stack so unrelated restarts don’t take it down, and treating its own credentials with the same discipline as everything it’s monitoring, are both decisions that only matter once the dashboard has enough surface area for either one to actually break something.
Related: Beszel: Host-Level Monitoring Without Running Another Stack