DockDeck
DockDeck is a calm, premium personal start dashboard for Unraid. Its normal Unraid profile is one branded application container, reads container inventory through the existing AppOps GET API and enriches it with sanitized local WebUI metadata.
What the MVP includes
- automatic read-only container discovery with new apps hidden by default;
- live online/offline status and 30-second visibility-aware polling;
- editable app name, icon fallback, category, order, local URL and external URL fallback;
- configurable categories, favorites and overview/category navigation;
- fast local search with a Google fallback, always opening in a new tab;
- Porcelain, Midnight and Harbor themes, four accents, three density and content-width profiles, and selectable backgrounds;
- responsive layouts from compact mobile screens through tablets, desktops and ultrawide displays;
- optional dashboard hero and status badges, plus editable category and favorite icons;
- up to eight configurable service widgets with independent order, compact/standard/wide layouts, custom labels, metric order, per-metric value/gauge/progress presentation, primary metric, any-signal warning thresholds and refresh cadence;
- line, area or bar charts with a selectable app/provider series, legend and 10–60 in-memory samples;
- a default native Unraid widget with configurable statistics and charts for Docker inventory, array, parity, disk/pool, temperature and optional UPS status;
- rich GET-only providers for AdGuard, Plex, Immich, Home Assistant, Sonarr/Radarr/Lidarr, Ollama, Glances, Tautulli, Paperless, Gitea, Jellyfin, Seerr, Prowlarr, Authentik, NPM, Portainer, Grafana, Prometheus and Nextcloud;
- optional GET-only metrics bridges for Deluge, qBittorrent, JDownloader, Tdarr, Bazarr and Vaultwarden;
- a searchable Widget Studio that explains available metrics, recommends useful panels and activates up to eight widgets;
- persistent per-app metric visibility, order, presentation and card limits, with a tailored profile for every discovered app even before its optional data endpoint is connected;
- native Audiobookshelf, Netdata and PeerTube readers plus an optional GET-only JSON endpoint for every custom or niche app;
- an editable Tower name and Unraid WebUI URL that defaults to port
5000; - masked provider credential forms in Settings, with immediate read-only connection testing and no secret echo;
- automatic first-party favorite favicons with a resilient local monogram fallback;
- a useful desktop sidebar with time, deck health and quick service links;
- SQLite persistence plus versioned JSON export/import;
- reusable layout presets with independent import/export, direct dashboard edit mode and secret-free diagnostics;
- an installable PWA shell that explicitly excludes runtime API data from caching;
- optional read-only Nginx Proxy Manager host matching;
- one branded, read-only DockDeck container with a true transparent PNG Unraid-list icon, WebUI metadata and no Docker-socket mount.
Local development
Requirements: Node.js 22 or newer and npm 10 or newer.
cp .env.example .env
npm install
npm run dev
Open http://127.0.0.1:5173. The example environment enables deterministic discovery fixtures. Runtime data is written to ./data/dockdeck.db.
Quality commands:
npm run format:check
npm run lint
npm run typecheck
npm test
npm run test:e2e
npm run build
Unraid / Docker Compose
Create an .env beside docker-compose.yml:
DOCKDECK_BIND_ADDRESS=192.168.10.150
DOCKDECK_PORT=1218
DOCKDECK_EGRESS_SUBNET=172.31.240.16/28
APPOPS_URL=http://192.168.10.150:1216
UNRAID_TEMPLATES_DIR=/mnt/user/appdata/dockdeck/unraid-templates
UNRAID_HOST=192.168.10.150
UNRAID_URL=http://192.168.10.150:5000
For the current Unraid host, deploy/unraid.env.example contains this non-secret profile and can be copied to .env. It pins a small egress subnet because this Unraid host has exhausted its automatic address pools; change it if the host network layout changes.
Unraid keeps its source templates root-only and templates can contain sensitive values. Create DockDeck's sanitized metadata mirror before starting Compose; the helper copies only Name, WebUI and Icon:
python3 deploy/sanitize-unraid-templates.py \
/boot/config/plugins/dockerMan/templates-user \
/mnt/user/appdata/dockdeck/unraid-templates
Then run. The base Compose file creates exactly one container named DockDeck:
docker compose up -d --build
docker compose ps
sudo sh deploy/refresh-unraid-icon.sh
The final helper validates that the configured <Icon> URL returns a real PNG and replaces both DockDeck-specific caches used by Unraid DockerMan: the persistent cache and the active RAM cache. Run it after changing the icon or recreating the container. It never changes Docker state or touches another container's files. An explicit URL can be supplied as the second argument when no user template exists.
On a host without AppOps, use the optional hardened fallback overlay. Only that additional proxy sees the socket and it disables POST and all unused API families:
docker compose -f docker-compose.yml -f docker-compose.socket-proxy.yml up -d --build
By default Compose publishes DockDeck only on 127.0.0.1:1218. For this Unraid host, set DOCKDECK_BIND_ADDRESS=192.168.10.150 so the trusted LAN can reach http://192.168.10.150:1218. DockDeck has no built-in authentication; do not publish it directly to the internet.
Persistent configuration lives in the named volume dockdeck_data. Normal preferences use /data/dockdeck.db; credentials entered through Settings use the permission-restricted /data/integrations.env. Protect volume backups because that environment file contains plaintext secrets. The JSON export never includes it.
Environment variables
| Variable | Purpose |
|---|---|
DATABASE_PATH |
SQLite file path; defaults to ./data/dockdeck.db locally and /data/dockdeck.db in Docker. |
INTEGRATION_ENV_PATH |
Permission-restricted environment file used for credentials entered in Settings. |
DOCKDECK_BIND_ADDRESS, DOCKDECK_PORT |
Host interface and port published by Compose; safely default to 127.0.0.1:1218. |
DOCKDECK_EGRESS_SUBNET |
Explicit small subnet for the app's outbound network. |
APPOPS_URL |
Existing AppOps origin used for GET-only inventory and measured CPU/memory status. |
DOCKER_PROXY_URL |
Optional internal URL used only by the socket-proxy fallback overlay. |
UNRAID_TEMPLATES_PATH |
Read-only XML template mount inside the app. |
UNRAID_HOST |
Host used to resolve [IP] in WebUI templates; defaults to tower.local. |
UNRAID_URL |
Initial Tower/Unraid link; Settings can override it and a missing port defaults to 5000. |
UNRAID_RUNTIME_PATH, UNRAID_HWMON_PATH |
Container paths for minimum read-only Unraid runtime and hardware-monitor mounts. |
UNRAID_UPS_STATUS_PATH |
Optional read-only APC/UPS status file inside the app container. |
MOCK_DISCOVERY |
Enables deterministic local/test fixtures; never enable for production discovery. |
NPM_URL, NPM_TOKEN |
Optional server-only Nginx Proxy Manager read credential. |
NPM_USERNAME, NPM_PASSWORD |
Optional server-only alternative for requesting an NPM session token. |
ADGUARD_URL, ADGUARD_USERNAME, ADGUARD_PASSWORD |
Optional AdGuard Home statistics for its selected widget. |
PLEX_URL, PLEX_TOKEN |
Optional Plex sessions, transcodes and library count. |
IMMICH_URL, IMMICH_API_KEY |
Optional Immich photo, video and storage totals. |
HOME_ASSISTANT_URL, HOME_ASSISTANT_TOKEN |
Optional Home Assistant automation, light and availability totals. |
OLLAMA_URL, GLANCES_URL |
Optional overrides; their discovered local app URLs provide zero-config read-only metrics. |
TAUTULLI_URL, TAUTULLI_API_KEY |
Optional Plex activity, transcode and bandwidth metrics. |
PAPERLESS_URL, PAPERLESS_TOKEN |
Optional Paperless document, inbox and archive-size metrics. |
AUDIOBOOKSHELF_URL, AUDIOBOOKSHELF_TOKEN |
Optional library, book, podcast and item totals. |
NETDATA_URL, PEERTUBE_URL |
Optional discovered-URL overrides for their native GET-only readers. |
SONARR_*, RADARR_*, LIDARR_* |
Optional provider URL and API key pairs for queue and version data. |
GITEA_WIDGET_*, JELLYFIN_*, SEERR_*, PROWLARR_* |
Optional server-side URL/token pairs for native GET-only metrics. |
AUTHENTIK_*, PORTAINER_*, GRAFANA_* |
Optional server-side URL/token pairs for administrative summary metrics. |
PROMETHEUS_URL, NEXTCLOUD_*, NPM_* |
Optional read-only monitoring, cloud and proxy metrics. |
*_METRICS_URL, *_METRICS_TOKEN |
Optional GET-only bridge for providers whose native API would require POST-based control. |
CUSTOM_WIDGET_<APP>_URL, CUSTOM_WIDGET_<APP>_TOKEN |
Settings-managed GET-only JSON metrics endpoint and optional bearer token for any other app. |
GITEA_URL, GITEA_OWNER, GITEA_TOKEN |
Used only by npm run gitea:init to create/push the private repository. |
Provider secrets are never stored in SQLite, JSON exports, logs or tracked files and are never returned to the browser. Values entered in Settings are persisted as environment assignments in INTEGRATION_ENV_PATH with mode 0600; use DockDeck only on a trusted LAN or behind authenticated HTTPS, and protect raw volume backups.
Repository initialization
After a passing quality gate and with the three Gitea variables exported, run npm run gitea:init. The script refuses an unexpected existing remote, creates only a private repository, keeps the token out of Git configuration, and pushes main through a temporary authorization header.