8.7 KiB
8.7 KiB
NAS Dashboard — Implementation Plan
Context
A modern, self-hosted web dashboard on your Synology DS224+ NAS (Tailscale-only) that serves as a single entry point to manage repos, Docker containers, files, terminal sessions, and media — plus an AI assistant. Replaces the need to SSH into the NAS for routine tasks. Public access via a dedicated Caddy reverse proxy VPS.
Architecture
Public Internet → nas.jimmygan.com / music.jimmygan.com
│
└── New AMD VPS (Caddy, HTTPS, Tailscale)
│
└── Tailscale → NAS (100.78.131.124)
├── :4000 NAS Dashboard (FastAPI + Svelte)
│ ├── /api/docker/* → Docker socket proxy (limited API)
│ ├── /api/gitea/* → Gitea REST API
│ ├── /api/files/* → /volume1 filesystem
│ ├── /ws/terminal → WebSocket pty (Tailscale-only)
│ └── /* → Svelte SPA
├── :4533 Navidrome (music)
├── :2283 Immich (photos, Tailscale-only)
├── :3300 Gitea (Tailscale-only)
├── :8096 Jellyfin (video, Tailscale-only)
├── :3100 OpenClaw (AI assistant, Tailscale-only)
├── :8080 Speedtest (Tailscale-only)
├── :13378 Audiobookshelf (audiobooks, Tailscale-only)
├── :8030 Stirling PDF (PDF tools, Tailscale-only)
└── :8222 Vaultwarden (passwords, Tailscale-only)
Existing VPS (158.101.140.85): xray VLESS+REALITY on :443 — untouched
Tech Stack (Dashboard)
- Backend: Python FastAPI + uvicorn
- Frontend: Svelte + Tailwind CSS (light theme default, with dark/light toggle)
- Terminal: xterm.js + WebSocket pty bridge
- Docker mgmt: via docker-socket-proxy (Tecnativa) for safety
- Gitea:
httpxasync client - Build: Multi-stage Dockerfile (node → python-slim), built on Mac, pushed to Gitea container registry
- LLM: Claude API (key from ~/.zshrc)
File Structure
nas-tools/
dashboard/
docker-compose.yml # Dashboard + docker-socket-proxy
Dockerfile
.env.example
backend/
main.py
config.py
requirements.txt
routers/
docker.py
gitea.py
files.py
terminal.py
frontend/
package.json
vite.config.js
svelte.config.js
src/
App.svelte
lib/
api.js
terminal.js
routes/
Dashboard.svelte
Docker.svelte
Gitea.svelte
Files.svelte
Terminal.svelte
components/
Sidebar.svelte
ContainerCard.svelte
LogViewer.svelte
navidrome/
docker-compose.yml
immich/
docker-compose.yml
openclaw/
docker-compose.yml
cloud-monitor/
oci-amd-monitor.py # New: grab AMD free-tier instance
Implementation Phases
Phase 1 — Provision new AMD VPS ✅
- Add
oci-amd-monitor.pyto cloud-monitor repo:- Shape:
VM.Standard.E2.1.Micro(1 OCPU, 1GB RAM, AMD free tier) - Display name:
free-amd-caddy - AMD-compatible image (Ubuntu x86_64)
- Same compartment, availability domain, subnet as existing VPS
- Shape:
- Run script to grab AMD instance
- Harden new VPS: SSH key-only, firewall (only 22/80/443), fail2ban, unattended-upgrades
- Install Tailscale + Caddy on new VPS
- Configure Caddy with HTTP basic auth immediately (placeholder until Phase 7 upgrades to 2FA)
- Update Cloudflare DNS:
nas.jimmygan.comandmusic.jimmygan.com→ new VPS IP (set to "DNS only", let Caddy handle TLS) - Clean up stale DNS:
moldcost→ dead server1 (152.53.226.207) — remove or update
Phase 2 — Dashboard MVP: Docker management ✅
- Create directory structure, Dockerfile, docker-compose.yml
- Add docker-socket-proxy (Tecnativa) to limit Docker API access
- Backend: main.py + config.py + docker router (list, start/stop, logs)
- Frontend: Svelte app with sidebar + Docker container view
- Terminal access control: backend middleware blocks
/ws/terminalfor non-Tailscale IPs (100.x.x.x) - File browser: read-write (mount
/volume1as rw), with path traversal protection - Add Docker log rotation to all compose files (
max-size: 10m,max-file: 3) - Build image on Mac, push to Gitea container registry
- Deploy on NAS, port 4000
- Configure Caddy on new VPS:
nas.jimmygan.com→ NAS :4000 via Tailscale (behind basic auth from Phase 1)
Phase 3 — Gitea + Files + Terminal ✅
- Add gitea router (repos, commits, trigger workflows) — needs Gitea API token
- Add files router (browse /volume1, download, upload, delete) with path traversal protection
- Add terminal router (WebSocket pty bridge + xterm.js) — blocked for non-Tailscale IPs
- Dashboard overview page with links to all services
Phase 4 — Navidrome (Music) ✅
- Deploy Navidrome Docker stack on port 4533 (with log rotation)
- Point at
/volume1/music - Link from dashboard sidebar
- Configure Caddy:
music.jimmygan.com→ NAS :4533 via Tailscale
Phase 5 — Immich (Photos) ✅
- Deploy Immich Docker stack on port 2283 (disable ML/face recognition, with log rotation)
- Point at
/volume1/photo+/volume1/homes/zjgump/Photos - Link from dashboard sidebar (Tailscale-only)
Phase 6 — OpenClaw (AI Assistant) ✅
- Deploy OpenClaw Docker stack on port 3100 (with log rotation)
- Configure Claude API as LLM provider
- Configure messaging channels (Telegram, etc.)
- Link from dashboard sidebar (Tailscale-only)
Phase 7 — Authentication & Security ✅
Upgrade dashboard auth from Caddy basic auth → session-based login + TOTP 2FA— Done (password + TOTP 2FA + passkey/WebAuthn login)Rate limiting for brute force protection on Caddy— Done (fail2ban + failed login Telegram alerts)
Phase 8 — CI/CD for Dashboard ✅
Add Gitea workflow to nas-tools: on push to main, auto-deploy— Done (Gitea Actions runsdocker compose up -don push)Tag images with git SHA +— DonelatestAuto-deploy on NAS— Done- Rollback:
docker compose pull <previous-tag> && docker compose up -d
Phase 9 — Backup & Recovery
- Automated daily backup script for:
- Gitea DB (pg_dump) + gitea-data
- Immich DB + upload data
- Navidrome config/DB
- OpenClaw data
- Dashboard .env and config
- Backups stored to
/volume1/backups - Periodic restore test to verify backups work
Phase 10 — Monitoring & Alerts (partial)
Add system stats to dashboard overview: CPU, RAM, disk usage— DoneContainer health checks in docker-compose files— Done- Notification on failure (Telegram bot or email)
Phase 11 — Polish (partial)
Light/dark theme toggle— DoneAdd Jellyfin link to dashboard sidebar (port 8096)— Done (replaced Emby)
Phase 12 — Additional Services
- Deploy Audiobookshelf on port 13378 — Done, add to dashboard sidebar
- Deploy Stirling PDF on port 8030 — Done, add to dashboard sidebar
- Deploy Vaultwarden on port 8222 — Done, add to dashboard sidebar
- Deploy Watchtower for auto-updates — Done (internal, no sidebar link needed)
- Deploy Speedtest on port 8080 — Done, already in dashboard sidebar
Media Paths
- Music:
/volume1/music(70GB, FLAC/WAV/DSD/ALAC) - Photos:
/volume1/photo(186GB) +/volume1/homes/zjgump/Photos - Video:
/volume1/video(759GB) - Emby: replaced by Jellyfin (port 8096)
VPS Strategy
- Existing VPS (158.101.140.85): xray VLESS+REALITY on :443 — untouched
- New AMD VPS: Caddy on :80/:443, Tailscale, reverse proxy to NAS
- Both in Tokyo region, Oracle free tier
DNS (Cloudflare)
nas.jimmygan.com→ new AMD VPS IP (DNS only, Caddy handles TLS)music.jimmygan.com→ new AMD VPS IP (DNS only)moldcost.jimmygan.com→ remove (dead server1)- Existing
jimmygan.comA record → keep as-is
Build Strategy
- Docker images built on Mac (faster than NAS Celeron)
- Push to Gitea container registry (
100.78.131.124:3300) - NAS pulls and runs images
- CI/CD automates this via Gitea Actions
Verification
- Dashboard at
http://100.78.131.124:4000— Docker page shows containers, start/stop works - Gitea page lists repos and commits
- Files page browses /volume1
- Terminal opens a working shell session
- Navidrome at
:4533— plays music from/volume1/music - Immich at
:2283— browses photos - OpenClaw at
:3100— responds via Claude API https://nas.jimmygan.com— dashboard accessible from internet with login + 2FAhttps://music.jimmygan.com— Navidrome accessible from internet- Backups run daily, restore tested
- Dashboard shows system stats, alerts on disk/container issues