BrettKinny/tower-dashboard
BrettKinny/tower-dashboard has 46 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours and +1 in 7 days. Last checked 2026-11-09 22:19 UTC. Last push 2026-10-21.
TUI monitoring dashboard for Unraid: btop, nvtop, ctop, and fan-status composed in a single tmux window over SSH, launchable from any XDG app launcher.
Measured star growth
- in 24h
- 0
- in 7 days
- +1
- in 15 days
- Update delayed
- in 1 month
- Update delayed
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
README.md
Original
Tower Dashboard: a TUI for monitoring your Unraid box
A live, read-only monitoring dashboard for Unraid: btop, nvtop and
ctop composed in a tmux session that runs on the box itself, with fans,
CPU temp and the clock in a lazygit-style footer. The workstation just attaches
to it over a single SSH connection, launchable from any XDG app launcher. One
click, one keystroke, one window — everything you need to glance at to know
the box is healthy.
On omarchy it's also a bar plugin: an icon that turns red when something on the box needs attention (a disabled disk, a full or hot drive, parity errors, an unhealthy container, a stalled fan), a popup with the details, and a right-click into the full dashboard.
Running in
foot on omarchy with the etherial theme.
Built for Unraid. The tower-side pieces are Unraid-coupled (
mdcmd, emhttp'sdisks.ini,/mnt/disk*//mnt/cache//mnt/user, RAM-FS restored from/boot/config/go). The workstation-side launcher will happily SSH into any Linux host, but the array-status and boot-restore bits assume Unraid.
Shared as a starting point, not a finished product. Fork it, rip out what doesn't match your box, rewire the rest. The generalizable bits — the tmux composition pattern, the hwmon-via-sysfs fan reader, the FAT32-survives-boot install dance via
/boot/config/go— are the parts worth lifting even if your stack looks nothing like mine.
Layout
Two rows plus a footer, all running on the Unraid host inside a server-side tmux session. Each panel is titled in its top border, lazygit-style, with the focused one picked out in green:
| Region | Content | Source |
|---|---|---|
system — top ~65%, full width |
btop --force-utf — CPU/cores/load, mem, usage + live R/W per array disk, eth0 net, processes |
upstream btop |
gpu · containers — bottom ~35%, 50/50 |
nvtop (left) · ctop -a -s cpu (right, running containers: CPU, mem, PIDs) |
upstream |
| Footer — tmux status bar | box name + IP · tower-health light (● healthy / ▲ first problem) · fan-status --line (intake/exhaust %+RPM, CPU temp) · clock, every 2s |
this repo |
Colours are terminal-palette indices throughout (btop's TTY theme, tmux
colourN), so the whole thing follows your local terminal's theme.
Anything interactive (lazydocker, lazygit, etc.) was deliberately moved out — this view is pure observability.
How it works
The dashboard is a tmux session named dash that lives on the Unraid box:
tower/bin/tower-dash(on the box) builds that session — the layout, each pane running its monitor in a restart loop — then attaches. It builds once; every later run re-attaches to the warm session.workstation/tower-dashboard(on the workstation) is the launcher: it spawns a fresh terminal that runsssh -t <box> tower-dash. That's the whole client side — one SSH connection, no client-side tmux, no layout logic. On Hyprland, launching again focuses the open dashboard window instead of opening a second; the window's app-id istower-dashboard, for window rules. If SSH fails, the window stays open to show why.
Because the session lives on the box, the monitors stay warm between launches
and survive the workstation disconnecting — re-opening is instant. The session
lasts until the box reboots (or until tower-dash kill ends it sooner;
tower-dash rebuild restarts it to pick up config changes).
The omarchy bar plugin (manifest.json + omarchy/) polls the box with
tower/bin/tower-status, a read-only probe piped
over one non-interactive SSH request (ssh <box> bash -s < tower-status), so
it works without installing anything new on the box. omarchy/status.py
turns the probe's records into JSON and decides what needs attention.
Earlier versions built the layout client-side and drove it over
send-keysat attach time, then carried a 1-row fan-strip pane that needed resize hooks to stop it scaling into a fat band. Building on the box and moving the strip into tmux's status bar removed all of that.
Why this over the alternatives?
- vs. the Unraid web UI — the WebGUI lives behind a tab and refreshes slowly. This is one keystroke ("Tower Dashboard" in your launcher) and updates in real time. No browser, no bookmark, no auth.
- vs. Glances / Beszel / Netdata — those are excellent dashboards that
want a daemon on the box, a port, and (usually) a browser. This is no web
daemon and no port: just
tmuxplus the TUI tools you'd run by hand anyway. Read-only by design. - vs.
ssh root@tower htop— htop is one pane. This is the panels you actually want, laid out at the right sizes, with the right tool for each (btop for storage I/O, nvtop for GPU, ctop for containers, custom script for fan PWM).
Repo layout
manifest.json # omarchy plugin manifest (the repo root is the plugin)
omarchy/ # omarchy bar plugin
TowerWidget.qml # bar icon + popup panel
Service.qml # polling
status.py # runs tower-status over SSH → JSON snapshot + health rules
test_status.py # unit tests (python3 -m unittest)
workstation/ # Files that live on the laptop/desktop
tower-dashboard # launcher — spawns a terminal, SSHes in, attaches
tower-dashboard.svg # icon — symlinked into ~/.local/share/icons/.../apps/
Tower Dashboard.desktop.in # XDG launcher entry template (Exec=@BIN_PATH@)
ssh-config.snippet # optional SSH ControlMaster block for ~/.ssh/config
tower/ # Files that live on the Unraid host
bin/ # tower-dash, tower-status, tower-health, fan-status, array-status
config/ # btop.conf, ctop config, sensors-ignore-fans.conf
go.snippet # boot-restore lines for /boot/config/go
README.md # Unraid-side details
install-workstation.sh # Idempotent installer for the workstation side
install-tower.sh # Idempotent installer for the tower side (over SSH)
LICENSE # MIT
Install
You need key-based (non-interactive) SSH from the workstation to the box first — the plugin and launcher both rely on it.
Omarchy
omarchy plugin add https://github.com/BrettKinny/tower-dashboard --enable
That puts the icon in the bar (right section by default). Set the SSH target
in the widget's settings if your box isn't [email protected]. Then:
- Left-click — popup: array + pool disks (usage, temps, errors), parity
check status, CPU/RAM/GPU, fans, containers.
Rrefreshes,D/Enteropens the dashboard,Esccloses. - Right-click — open the dashboard. Middle-click — refresh.
- Notifications — a desktop notification when a problem appears (critical,
so it stays until dismissed) and a quiet one when it clears. Each problem is
announced once — a disk going from 91% to 92% full isn't news — and the box
has to be unreachable for three polls in a row before that's announced, so
sleep/wake and Wi-Fi blips stay silent. Toggle with the Notifications
setting; state lives in
~/.local/state/tower-dashboard/notified.json. - IPC —
omarchy-shell brettkinny.tower-dashboard dashboard(alsotoggle,refresh,status), e.g. for a Hyprland keybinding.
Update with omarchy plugin update brettkinny.tower-dashboard, then
omarchy restart shell: the shell's hot reload picks up the widget but keeps
a cached copy of omarchy/Service.qml (a same-folder QML type), so polling
changes only take effect after a restart.
The plugin brings the launcher with it, so you only need
install-workstation.sh too if you also want the "Tower Dashboard" app
launcher entry and the unraid-dash alias.
Workstation (any desktop)
git clone https://github.com/BrettKinny/tower-dashboard ~/Repos/tower-dashboard
~/Repos/tower-dashboard/install-workstation.sh
The installer is idempotent (safe to re-run): it symlinks the launcher script
and icon into the right XDG dirs, renders the .desktop entry from its
template with an absolute Exec= path (some launchers — notably walker on
omarchy — don't resolve ~/.local/bin from unqualified Exec lines), adds an
unraid-dash shell alias to ~/.bashrc, and refreshes the desktop database
so launchers pick up the new entry. If you move the checkout, re-run it — the
symlinks point into the checkout.
Tower (Unraid)
- Install tmux (NerdTools plugin) and nvtop (nvtop plugin) from the Unraid Apps tab.
- Drop static
btop+ctopbinaries at/boot/config/bin/. - Run
./install-tower.sh [root@your-box]from the workstation. It copies the scripts and configs onto the flash and into their live locations, appends any missinggo.snippetlines to/boot/config/go(review them first — the fan lines are specific to my board), and builds thetmux-256colorterminfo onto the flash if needed. Add--rebuildto restart the warmdashsession with the new bits.
See tower/README.md for what goes where and why.
Customize
Everything machine-specific is reachable via env vars or plugin settings, so you should rarely need to fork.
Workstation side (set in ~/.bashrc or shell rc)
| Var | Default | What it does |
|---|---|---|
TOWER_HOST |
[email protected] |
SSH target the launcher (and install-tower.sh) connects to. The bar plugin uses its own SSH target setting. |
TERMINAL |
autodetected | Override terminal autodetection (otherwise tries xdg-terminal-exec, ghostty, kitty, alacritty, foot, wezterm, gnome-terminal, konsole, xterm in that order) |
Plugin health thresholds
What counts as "needs attention" lives at the top of
omarchy/status.py: disks ≥ 90% full, array disks ≥ 50°C,
pool devices ≥ 70°C, CPU/GPU ≥ 85°C, plus any disabled/missing array disk,
unmounted pool, read errors, parity-check errors, stalled fan, unhealthy or
restarting container, or a stopped array. Empty slots (including an
unassigned parity2) are ignored.
Tower side — fan-status hardware mapping
fan-status defaults match an Asus board with NCT6779 superio + Intel
coretemp + 3-intake/2-exhaust fan wiring. To adapt:
ssh [email protected] fan-status --detect # lists all hwmon chips + their pwm/fan/temp inputs
Then export overrides on the tower side (e.g. in /etc/profile.d/), or edit
the defaults at the top of the script:
| Var | Default | Notes |
|---|---|---|
TOWER_INTAKE_CHIP |
nct6779 |
Chip name (prefix-matched against /sys/class/hwmon/*/name) |
TOWER_EXHAUST_CHIP |
nct6779 |
Same chip in most setups |
TOWER_TEMP_CHIP |
coretemp |
k10temp for AMD |
TOWER_INTAKE_NUM |
1 |
pwm1 + fan1_input |
TOWER_EXHAUST_NUM |
3 |
pwm3 + fan3_input |
TOWER_TEMP_NUM |
1 |
temp1_input |
Tower side — btop array disks
Edit disks_filter in tower/config/btop/btop.conf
to match your array (Unraid's /etc/fstab doesn't list array mounts, so an
explicit filter is required).
SSH
The dashboard uses a single SSH connection — it attaches to the tmux
session on the box. workstation/ssh-config.snippet
has an optional ControlMaster block; it's not required, but it makes the bar
plugin's polls near-free (~100 ms instead of a fresh handshake each time). The
launcher pins TERM=xterm-256color for the remote tmux, because the box's
RAM-FS won't have terminfo for xterm-ghostty / xterm-kitty and friends.
License
MIT — see LICENSE.
Explore related projects
awesome-selfhosted/awesome-selfhosted
A list of Free Software network services and web applications which can be hosted on your own servers
335,864 starsimmich-app/immich
High performance self-hosted photo and video management solution.
118,645 starsStirling-Tools/Stirling-PDF
#1 PDF Application on GitHub that lets you edit PDFs on any device anywhere
96,149 starslouislam/uptime-kuma
A fancy self-hosted monitoring tool
93,425 stars