A two-component system for monitoring Linux server hardware. A lightweight Python agent runs on each monitored server and exposes metrics via HTTP. A Next.js dashboard runs on a client machine and displays all data live in the browser — with support for multiple servers via a built-in proxy.
┌─────────────────────┐
│ Linux Server 1 │ ← hosts the dashboard + its own agent
│ │
│ ┌───────────────┐ │ ┌──────────────────────┐
│ │ main.py │ │ │ Linux Server 2 │
│ │ (Agent) │ │ │ │
│ └───────────────┘ │ │ ┌───────────────┐ │
│ │ │ │ main.py │ │
│ ┌───────────────┐ │ HTTP │ │ (Agent) │ │
│ │ Next.js │──┼───────►│ └───────────────┘ │
│ │ Dashboard │ │ └──────────────────────┘
│ └───────────────┘ │
└─────────────────────┘
▲
│ Tailscale / VPN
│
Your Browser
The dashboard acts as a server-side proxy — agent URLs and API keys never leave the server. The browser only ever talks to the dashboard.
- Agent (
src/client/) — FastAPI service that runs on each Linux server. Collects CPU, RAM, GPU, disk, network, temperature, process and systemd service data. Listens on port33333. Optionally protected by an API key. - Dashboard (
src/website/) — Next.js 15 web app hosted on one machine. Proxies requests to all configured agents and renders live data in the browser with a server switcher.
- CPU usage, per-core breakdown, load averages, frequency
- Memory and swap utilization
- NVIDIA GPU usage, VRAM, temperature, power draw (via
pynvml) - Disk usage per partition
- Network interface transfer totals
- Hardware temperatures (via
lm-sensors) - Top processes sorted by CPU usage
- systemd service status with search and filter
- Multi-server support — switch between servers in the UI
- Auto-refresh every 5 seconds
hardware-manager/
└── src/
├── client/ # Agent (runs on each Linux server)
│ ├── main.py # FastAPI app — metric collectors + REST endpoints
│ └── requirements.txt
└── website/ # Dashboard (runs on one machine)
├── app/
│ ├── api/agents/ # Proxy routes (server-side)
│ ├── page.tsx # Main dashboard with server switcher
│ └── globals.css
├── components/
│ ├── UsageBar.tsx
│ ├── CpuCard.tsx
│ ├── MemoryCard.tsx
│ ├── GpuCard.tsx
│ ├── DiskCard.tsx
│ ├── NetworkCard.tsx
│ ├── TemperatureCard.tsx
│ ├── ProcessTable.tsx
│ └── ServiceTable.tsx
├── lib/
│ ├── api.ts # Types + client-side fetcher
│ └── agents.ts # Server-side agent config
└── .env.local.example
The easiest way to run both components together on the same machine:
# Optional: set an API key to protect the agent
export AGENT_API_KEY=your-secret-key
docker compose up -d
# Agent → http://localhost:33333
# Dashboard → http://localhost:3000To monitor a second server, run only the agent there:
docker compose up -d agentThen add its IP to the AGENTS env var of your dashboard (see Configuration).
cd src/client
pip install -r requirements.txt
# Optional: protect with an API key
export AGENT_API_KEY=your-secret-key
python main.py
# → listening on http://0.0.0.0:33333Don't forget to open port 33333 in your firewall if the dashboard is on a different machine.
API endpoints:
| Endpoint | Description |
|---|---|
GET /snapshot |
All metrics in one call |
GET /cpu |
CPU info and usage |
GET /memory |
RAM and swap |
GET /disks |
Disk partitions |
GET /network |
Network interfaces |
GET /gpus |
GPU info (NVIDIA only) |
GET /temperatures |
Sensor temperatures |
GET /processes?limit=20 |
Top processes by CPU |
GET /services |
systemd service list |
cd src/website
cp .env.local.example .env.local
# Edit .env.local — see configuration below
npm install
npm run dev
# → http://localhost:3000| Env var | Default | Description |
|---|---|---|
AGENT_API_KEY |
(none) | If set, all requests must include X-API-Key: <key>. Leave unset on a trusted network. |
To change the port, edit the last line of src/client/main.py:
uvicorn.run("main:app", host="0.0.0.0", port=33333)Configure all monitored servers in .env.local as a JSON array. The URLs stay server-side and are never exposed to the browser.
# .env.local
AGENTS=[
{"id":"homelab","name":"Home Lab","url":"http://localhost:33333"},
{"id":"server2","name":"Server 2","url":"http://<ip-of-server-2>:33333","apiKey":"your-secret-key"}
]| Field | Required | Description |
|---|---|---|
id |
Yes | Unique identifier (used in URLs) |
name |
Yes | Display name shown in the UI |
url |
Yes | Base URL of the agent |
apiKey |
No | Sent as X-API-Key header to the agent |
fastapi+uvicorn— HTTP serverpsutil— CPU, RAM, disk, network, processespynvml— NVIDIA GPU metrics (optional, graceful fallback if no GPU present)pydantic— response models
- Next.js 15 with App Router
- SWR — data fetching with auto-refresh
- Tailwind CSS — styling
MIT