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> |
||
|---|---|---|
| backend | ||
| frontend | ||
| infra/mosquitto | ||
| ops | ||
| simulators | ||
| .gitignore | ||
| ACTION_PLAN.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| IMPROVEMENTS.md | ||
| memory.md | ||
| project_plan.md | ||
| README.md | ||
| UI_UX_PLAN.md | ||
| zenvex_dcim_research_report.md | ||
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
- Prerequisites
- Quick Start — Docker Compose
- Environment Variables
- Verify Everything Is Running
- Accessing the App
- Ports Reference
- Local Development (without Docker)
- Project Structure
- 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.tspasses every request through and there is no auth provider inapp/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>inapp/layout.tsx, restore<UserButton />incomponents/layout/topbar.tsx, re-add thesign-in/sign-uproute groups, and swapproxy.tsforclerkMiddleware.
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