Homelab Dashboard#
What Was Established#
A Node.js + Express dashboard is deployed at https://status.nbkelley.com, serving homelab monitoring data. All API calls are server-side — no internal IPs, credentials, or raw API responses reach the browser.
Deployment#
| Detail | Value |
|---|---|
| Public URL | https://status.nbkelley.com |
| Host | proxy VM (192.168.1.222) |
| Port | 3002 |
| Runtime | Node.js + Express, Docker container |
| compose.yaml location | /home/iluvatar/compose.yaml on proxy VM |
| App directory | /opt/homelab-dashboard/ on proxy VM |
| Routing | Cloudflare Tunnel → 127.0.0.1:3002 |
File Structure#
/opt/homelab-dashboard/
server.js ← Express app, all API logic
package.json
package-lock.json
dockerfile
start.sh
node_modules/
public/
index.html
styles.css
app.js ← frontend render enginecompose.yaml Entry#
homelab-dashboard:
build: /opt/homelab-dashboard
container_name: homelab-dashboard
restart: unless-stopped
network_mode: host
environment:
- PORT=3002network_mode: host means the app binds directly to host port 3002. The ports: mapping is ignored when using host networking.
Cloudflare Tunnel Routing#
Added as a Public Hostname in Cloudflare Zero Trust → Networks → Tunnels:
- Subdomain:
status - Domain:
nbkelley.com - Type: HTTP
- URL:
127.0.0.1:3002
NPM SSL (Let’s Encrypt) was attempted but failed. Cloudflare tunnel handles TLS termination instead — no NPM proxy host needed for this service.
API Endpoints#
| Endpoint | Description |
|---|---|
| GET /api/health | {"ok":true} — liveness check |
| GET /api/summary | Latest row from homelab_analysis Postgres table. 5-minute cache TTL. |
| GET /api/live | Live data fetched in parallel from Prometheus, Uptime Kuma, UniFi, and Synology. Returns sections, hosts, monitors, network, nas. 2-minute cache TTL. |
All data sources are fetched server-side and served as clean JSON. Both /api/summary and /api/live use an in-memory cache. On startup, caches are populated immediately and setInterval keeps them warm (lines 317–319).
Server-Side Caching#
| Cache | TTL | Source |
|---|---|---|
| summary | 5 minutes | Postgres homelab_analysis table |
| live | 2 minutes | Prometheus, Uptime Kuma, UniFi, Synology (produces sections, hosts, monitors, network, nas) |
On startup, both caches are populated immediately. setInterval keeps them warm in the background.
SECTIONS System#
server.js has a SECTIONS config (lines 34–61) that organizes all hosts and monitors into a hierarchical structure. Each section represents a top-level infrastructure box on the dashboard, with optional sub-groups that cluster related hosts and monitors.
All pattern matching is case-insensitive substring.
Current structure#
| Section | Section hosts | Group | Host patterns | Monitor patterns |
|---|---|---|---|---|
| Proxmox | proxmox |
Media | servarr, docker |
jellyfin, hinterflix, sonarr, radarr, bazarr, prowlarr, lidarr, readarr |
| Infra | proxy, cloudflared, prometheus, uptime, n8n, postgresql |
npm, cloudflare, prometheus, postgres, n8n, uptime kuma |
||
| Websites | hugoboss |
nbkelley, hugoboss, mbta, mbtadash, homelab dashboard, homelab-dashboard |
||
| AI | celebrimbor, elrond, wiki-llm, wiki |
ollama, open webui, openwebui, webui, wiki-llm |
The applySections() function (lines 63–101) processes raw hosts and monitors through this config. Each section’s own hosts (matched by sectionHostPatterns) are shown as section-level metrics. Each group’s matched hosts and monitors are claimed and excluded from the ungrouped host/service lists. Groups with no matches are skipped.
Status rollup#
- A group’s status is
upif all items are up,downif all are down, otherwisedegraded. - A section’s status is
upif all its groups are up,downif any group is down, otherwisedegraded.
Frontend (public/app.js)#
The frontend fetches /api/summary and /api/live in parallel on load and every 2 minutes. It renders:
- Overall summary card — AI-generated text from
homelab_analysis, with time-ago timestamp - Monitor strip — vertical stack of infra-box cards, one per section:
- Section header shows section name, aggregate status (up/degraded/down), and CPU/RAM/disk bars for the section’s own host
- Each section’s sub-groups render as a 2-column grid of subgroup cards, each with a header (name + status badge) and a sorted list of items:
- Host items — status dot, name, instance, and CPU/RAM/disk progress bars (color-coded: green/yellow/red)
- Monitor items — status dot, name, 24h uptime %, and ping ms
- Down items are visually dimmed with a red left-edge indicator
- Section cards — Hosts, Services, Network, NAS; each shows an AI sub-summary and a status badge (ok / warn / err)
Thresholds used for color coding:
| Metric | warn | err |
|---|---|---|
| CPU | 70% | 85% |
| RAM | 80% | 90% |
| Disk | 35% | 60% |
| NAS disk temp | — | >45°C |
| NAS volume | >70% | — |
Styling#
Minimal monospace design. Font: IBM Plex Mono (300/400/500 weights, loaded from Google Fonts). Color palette uses CSS variables (--bg, --surface, --border, --green, --yellow, --red, etc.). No sharp border-radius — all cards and badges are square. Responsive: single-column layout below 640px.
Proxy VM Context#
| Service | Port | Notes |
|---|---|---|
| NPM admin | 81 | http://192.168.1.222:81 |
| NPM HTTP | 8000 | |
| NPM HTTPS | 9443 | |
| MBTA dashboard | 3000 | node process (not Docker), PID varies |
| Homelab dashboard | 3002 | Docker, network_mode: host |
| Netbird | — | in compose.yaml but not actively used |
Rebuild Command#
SSH to proxy VM, then:
sudo docker compose -f /home/iluvatar/compose.yaml up -d --build homelab-dashboardKnown Issues#
- Static files (
public/) are baked into the Docker image at build time. Any CSS/HTML/JS or server changes require a rebuild. A volume mount would allow live edits without rebuild.
Related Pages#
AI-Driven Monitoring Pipeline, n8n, PostgreSQL, MBTA Dashboard - Setup
Sources#
ingested/chats/Homelab-AI---2026-04-13.mdingested/chats/183-Backend API call to push to website.mdHomelab AI - 2026-04-18 ·raw/conversations/chunks/2026-04-18-31-Homelab AI.jsonCodebase review - 2026-04-30 ·/opt/homelab-dashboard/Codebase review - 2026-05-01 ·/opt/homelab-dashboard/