Monitor for a shared GPU workstation (Windows + WSL2 Ubuntu, multiple SSH users). Everyone gets a live dashboard showing who is logged in, each user's CPU / RAM / VRAM usage, and limit breaches — so people stop stepping on each other's runs.
- Backend: Python (FastAPI + psutil + nvidia-smi), runs inside WSL as a systemd service, samples every 3 s.
- Frontend: React (Vite), served by the backend at
http://<host>:8595. - CLI:
gpuwhoprints the same info in any SSH session. - Alerts: global thresholds (combined VRAM/RAM/CPU) and per-user limits.
On breach:
wallbroadcast to every SSH session, direct message to the offender's terminal, optional Slack/Discord webhook for the admin, and the dashboard shows it. Repeats are suppressed forcooldown_minutes.
-
Enable systemd in WSL (once), in
/etc/wsl.conf:[boot] systemd=true
Then from Windows:
wsl --shutdownand reopen WSL. -
Build the frontend (needs Node; can be done on Windows or in WSL):
cd frontend && npm install && npm run build
-
Install the service:
sudo bash deploy/install.sh
-
Set your limits in
/etc/gpumanager/config.yaml(seeconfig.example.yamlfor every option), thensudo systemctl restart gpumanager.
Dashboard: http://<workstation>:8595 · API: /api/status · CLI: gpuwho
WSL2 is NAT'd by default. Two options (you likely already solved this for SSH):
-
Mirrored networking (Windows 11, recommended): in
%UserProfile%\.wslconfigset[wsl2] networkingMode=mirrored, thenwsl --shutdown. WSL ports are then reachable on the Windows host's LAN IP directly. -
Port proxy: in an admin PowerShell on Windows:
netsh interface portproxy add v4tov4 listenport=8595 listenaddress=0.0.0.0 connectport=8595 connectaddress=(wsl hostname -I).Trim() New-NetFirewallRule -DisplayName gpumanager -Direction Inbound -LocalPort 8595 -Protocol TCP -Action Allow
- Sessions: from utmp (same source as
who), with idle time from tty activity — users see who is on and whether they're actually active. - CPU / RAM per user: aggregated over each user's processes.
cpu_percentis percent of the whole machine (all cores = 100). RAM is summed RSS, which double-counts shared pages slightly. Totals are for the WSL VM, i.e. the RAM/CPU you allotted WSL in.wslconfig— not the whole Windows box. - VRAM per user: from
nvidia-smi --query-compute-apps, mapping each GPU process to its owner. Caveat: some WSL driver versions don't report per-process GPU memory. gpumanager then falls back to scanning/dev/dxghandles: it still shows which users have GPU work running (and card-level totals stay exact), but per-user GB shows as "GPU active" instead of a number. Update the NVIDIA Windows driver to get per-process numbers. - Alerts notify, they don't enforce — nothing is killed or throttled.
Hard enforcement (per-user cgroups for RAM/CPU,
CUDA_VISIBLE_DEVICES/ MPS for GPU) is a natural next step.
# backend (WSL): python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python -m gpumanager --config config.example.yaml
# frontend (hot reload, proxies /api to :8595):
cd frontend && npm run devNote: the dashboard has no authentication — anyone on the LAN can view it. Keep it on the trusted network, or put a reverse proxy with auth in front.