No description
Find a file
megaproxy de9b8c51bd Add Unraid deployment via the Portainer API
ops/deploy.py ships each service's build context to the remote Docker
daemon's /build endpoint and pushes docker-compose.prod.yml as a Portainer
stack — there is no docker CLI in WSL and Unraid's SSH is closed.

The prod compose file drops the dev bind mounts and uvicorn --reload,
publishes only the frontend port (8000 is taken by Portainer's Edge
tunnel), and pins bms_net to 172.31.42.0/24 because the host's default
address pools are fully subnetted. Mosquitto's config is baked into an
image since the repo is not checked out on the host.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 17:43:00 +01:00
backend Complete demo-mode auth conversion and fix the frontend build 2026-08-04 17:42:51 +01:00
frontend Complete demo-mode auth conversion and fix the frontend build 2026-08-04 17:42:51 +01:00
infra/mosquitto Add Unraid deployment via the Portainer API 2026-08-04 17:43:00 +01:00
ops Add Unraid deployment via the Portainer API 2026-08-04 17:43:00 +01:00
simulators first commit 2026-03-19 11:32:17 +00:00
.gitignore Add setup and deployment guide, env example files 2026-03-19 11:35:08 +00:00
ACTION_PLAN.md first commit 2026-03-19 11:32:17 +00:00
docker-compose.prod.yml Add Unraid deployment via the Portainer API 2026-08-04 17:43:00 +01:00
docker-compose.yml first commit 2026-03-19 11:32:17 +00:00
IMPROVEMENTS.md first commit 2026-03-19 11:32:17 +00:00
memory.md Add Unraid deployment via the Portainer API 2026-08-04 17:43:00 +01:00
project_plan.md first commit 2026-03-19 11:32:17 +00:00
README.md Add Unraid deployment via the Portainer API 2026-08-04 17:43:00 +01:00
UI_UX_PLAN.md first commit 2026-03-19 11:32:17 +00:00
zenvex_dcim_research_report.md first commit 2026-03-19 11:32:17 +00:00

BMS — Data Centre Infrastructure Management

A full-stack DCIM platform with live sensor dashboards, alarm management, power/cooling monitoring, and simulated sensor bots.

Stack: Next.js 16 · FastAPI · PostgreSQL + TimescaleDB · MQTT (Mosquitto) · Docker Compose


Table of Contents

  1. Prerequisites
  2. Quick Start — Docker Compose
  3. Environment Variables
  4. Verify Everything Is Running
  5. Accessing the App
  6. Ports Reference
  7. Local Development (without Docker)
  8. Project Structure
  9. Stopping & Resetting

Prerequisites

Requirement Minimum version Check
Docker 24+ docker --version
Docker Compose v2 (bundled with Docker Desktop / Engine) docker compose version
Git any git --version

That's it for the Docker path — Node and Python are not needed on the host.


Quick Start — Docker Compose

1. Clone the repo

git clone https://git.rdx4.com/youruser/bms.git
cd bms

2. Create the environment files

cp backend/.env.example backend/.env
cp frontend/.env.local.example frontend/.env.local

The defaults work as-is — they already match the service names in docker-compose.yml. The app runs in demo mode with authentication disabled, so there are no keys to obtain before the first run. See Environment Variables below.

3. Start all services

docker compose up --build

The first run downloads base images and builds the containers — this takes a few minutes. On subsequent starts it is much faster:

docker compose up

4. Done

Open your browser at http://your-server:5646


Environment Variables

Authentication is disabled. The app runs in demo mode: frontend/proxy.ts passes every request through and there is no auth provider in app/layout.tsx. Deploy it on a trusted network (LAN, VPN, or behind an authenticating reverse proxy) — every route is open.

To turn auth back on: reinstall @clerk/nextjs, wrap the tree in <ClerkProvider> in app/layout.tsx, restore <UserButton /> in components/layout/topbar.tsx, re-add the sign-in / sign-up route groups, and swap proxy.ts for clerkMiddleware.

backend/.env

# Database — matches the db service in docker-compose.yml, no change needed
DATABASE_URL=postgresql+asyncpg://dcim:dcim_pass@db:5432/dcim

# MQTT broker — matches the mqtt service, no change needed
MQTT_HOST=mqtt
MQTT_PORT=1883

# CORS — add your frontend origin if you expose the backend directly
# Leave empty when using the built-in Next.js proxy (recommended)
CORS_ORIGINS=[]

DEBUG=true

frontend/.env.local

# API path — leave as-is, Next.js proxies /api/backend/* to the backend internally
NEXT_PUBLIC_API_URL=/api/backend

# Backend internal URL (used by the Next.js server-side proxy, not sent to the browser)
# In Docker: http://backend:8000    In local dev: http://localhost:8000
BACKEND_INTERNAL_URL=http://backend:8000

Verify Everything Is Running

docker compose ps

All five services should show healthy or running:

NAME                STATUS
dcim_mqtt           running (healthy)
dcim_db             running (healthy)
dcim_backend        running (healthy)
dcim_simulators     running
dcim_frontend       running

Check individual logs if something looks wrong:

docker compose logs backend     # FastAPI logs
docker compose logs frontend    # Next.js logs
docker compose logs db          # PostgreSQL logs
docker compose logs simulators  # Sensor bot logs

Test the backend API directly:

curl http://localhost:8000/api/health
# Expected: {"status":"ok"}

Accessing the App

What URL
Dashboard http://your-server:5646
API docs (Swagger) http://your-server:5646/api/backend/docs
API docs (ReDoc) http://your-server:5646/api/backend/redoc

Reverse proxy tip: The frontend is the only service that needs to be publicly reachable. Point Nginx / Caddy / Traefik at port 5646. The backend, database, and MQTT broker communicate only on the internal Docker network.

Browser → Reverse Proxy → :5646 (Next.js)
                               ↓ server-side rewrite
                           :8000 (FastAPI) — internal only

Ports Reference

Service Host port Container port Notes
Frontend (Next.js) 5646 5646 Public-facing
Backend (FastAPI) 8000 8000 Internal; exposed for dev/debugging
Database (PostgreSQL) 5433 5432 Mapped to 5433 to avoid conflicts with any local Postgres
MQTT (Mosquitto) 1883 1883 Internal

Deploying to the Unraid Server

The live instance runs on the Unraid Docker host at 192.168.1.249 as the Portainer stack bms — reachable at http://192.168.1.249:5646/dashboard.

python3 ops/deploy.py              # build all four images + deploy/update the stack
python3 ops/deploy.py --no-build   # redeploy the stack using the existing images

There is no docker CLI in WSL and no SSH to the Unraid box, so ops/deploy.py ships each service's build context to the remote daemon's /build endpoint over the Portainer API (token at ~/.portainer-token), then pushes docker-compose.prod.yml as a Portainer stack. The script is idempotent — rerun it after any code change.

docker-compose.prod.yml differs from the dev docker-compose.yml: images instead of build contexts, no source bind mounts, no --reload, and only the frontend publishes a host port. Postgres data is bind-mounted to /mnt/user/appdata/bms/db so it lands on the Unraid array instead of growing inside docker.img, and it survives redeploys.

Local Development (without Docker)

Useful if you want hot-reload on the frontend or backend without rebuilding containers.

Database only via Docker

docker compose up db mqtt

Backend

Requires Python 3.12+.

cd backend
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000

Frontend

Requires Node 22+ and pnpm.

# Install pnpm if you don't have it
npm install -g pnpm

cd frontend
pnpm install
pnpm dev

Frontend dev server runs on http://localhost:3000 by default.

Simulators

cd simulators
pip install -r requirements.txt
python main.py

Project Structure

bms/
├── frontend/            Next.js 16 app (TypeScript, Tailwind, shadcn/ui, Recharts)
│   ├── app/             Pages (dashboard, cooling, power, alarms, floor-map, …)
│   ├── components/      Shared UI components
│   └── lib/             API client, contexts, utilities
│
├── backend/             FastAPI app
│   ├── api/routes/      REST endpoints (alarms, cooling, power, env, …)
│   ├── core/            DB connection, config
│   ├── models/          SQLAlchemy models
│   └── services/        MQTT subscriber, alarm engine, WebSocket manager
│
├── simulators/          Python sensor bots — publish fake DCIM telemetry over MQTT
│   └── bots/            Per-device bots: CRAC, UPS, generator, PDU, env, …
│
├── infra/
│   └── mosquitto/       Mosquitto broker config
│
└── docker-compose.yml   Orchestrates all services

Stopping & Resetting

# Stop all containers (data is preserved)
docker compose down

# Stop and delete the database volume (full reset — all data lost)
docker compose down -v

# Rebuild images after code changes
docker compose up --build