Appearance
Architecture
Home Lab is a single container that serves both a JSON API and a static front-end from the same origin.
Components
| Component | Role |
|---|---|
| Express 5 (Node.js) | REST API + static file serving |
| better-sqlite3 | Embedded SQLite database (WAL mode) |
| React (via Babel) | Front-end, transpiled in the browser |
| docker-socket-proxy | Optional sidecar for container status & controls |
Data model
Everything lives in one SQLite database under DATA_DIR:
| Table | Holds |
|---|---|
config | Singletons: pages, favourites, weather, dashboard, docker config, health source |
services | One row per card (layout + non-secret config) |
credentials | One row per widget's secrets (API keys, passwords, tokens) |
State is loaded into memory on boot and persisted back transactionally on change. On first run it migrates from any legacy JSON files if present.
Request flow
- The browser loads the static app and asks the backend for its configuration.
- The backend is authoritative - it returns pages, services and non-secret widget config. Secrets are never sent to the browser.
- For each widget, the browser polls a per-service endpoint on the widget's refresh interval.
- The backend resolves the widget type to an integration, calls the upstream service with the stored credentials, caches the result briefly, and returns it.
Secrets
- API keys, passwords and tokens are stored only in the
credentialstable. - The public config returned to the browser contains only non-secret keys.
- Backups do include secrets (in plaintext) because a restore needs them - see Backups.
Docker control
Home Lab never opens the Docker socket itself. A docker-socket-proxy sidecar exposes a narrow, mostly read-only slice of the Docker API; Home Lab talks to that over HTTP to show container status and issue start/stop/restart.
Persistence & backups
- The database and the
backups/folder both live under the persistedDATA_DIRvolume. - A daily backup of the whole dataset is written and pruned to the last 7.
Front-end notes
- The app applies the saved theme before rendering (no flash on reload).
- It uses a network-first service worker for an installable, offline-capable shell - see PWA.
- A dedicated mobile layout turns the sidebar into a drawer below 760 px - see Mobile.