Multi-server homelab dashboard for Noctalia Shell. Auto-discovers services on remote hosts via SSH + Docker, renders them as clickable tiles in a panel, opens them in the browser. Replaces Dashy / Homarr / Homepage without leaving the shell.
- Auto-discovery via SSH +
docker pson the remote host. Tiles are containers with an HTTP-reachable port. - Multi-server, with a switcher in the panel header.
- Tailscale peer import — one-click "Add" for any online peer.
- Auto-detect of SSH user. Tries a configurable list of common usernames (
root,ubuntu,debian,fedora,ec2-user,admin,opc,centos,arch,pi,deployby default, plus yourdefaultSshUser) and picks the first that connects. - IPv6 host support — pass the bare address (
2804:54:c100:1::200); the plugin wraps it in brackets for HTTP URLs and lets SSH handle it natively. - Real service logos via homarr-labs/dashboard-icons (30 bundled). Falls back to a colored letter tile for unknowns.
- Parallel
curlhealth check every 30s, color-coded status dot per tile. - Smart categorization — containers grouped by regex rules (Media, Cloud, Dev, Pentest, Home, Infra, etc).
- Per-container overrides — rename, recolor, recategorize, hide, custom icon.
- Manual mode — for hosts without SSH (or for monitoring external URLs), hardcode a service list per server.
- Per-server discovery cache, so the panel opens instantly even when SSH is slow.
Edit ~/.config/noctalia/plugins.json:
{
"sources": [
{ "enabled": true, "name": "Noctalia Plugins", "url": "https://github.com/noctalia-dev/noctalia-plugins" },
{ "enabled": true, "name": "Server Dashboard", "url": "https://github.com/ThiagoFrag/noctalia-server-dashboard" }
],
"states": {
"server-dashboard": { "enabled": true }
}
}Restart Noctalia (pkill -f "qs -c noctalia-shell"; qs -c noctalia-shell &). The plugin downloads on next start.
git clone https://github.com/ThiagoFrag/noctalia-server-dashboard.git \
~/.config/noctalia/plugins/server-dashboard
jq '.states["server-dashboard"] = {"enabled": true}' \
~/.config/noctalia/plugins.json > /tmp/p.json && mv /tmp/p.json ~/.config/noctalia/plugins.jsonAdd the bar widget via Settings → Bar → add plugin:server-dashboard.
- Noctalia Shell 4.0 or later
curl,sshon the client.dockeron the remote host.- Optional:
tailscaleon the client (only needed for the import feature).
All settings live in ~/.config/noctalia/plugins/server-dashboard/settings.json. The plugin's Settings UI handles the common cases (servers, intervals, count badge). For advanced stuff (overrides, category rules, icon map) edit the JSON directly with Noctalia stopped (the plugin overwrites the file on save).
Three ways:
- Settings → Servers → "+ Importar do Tailscale" — lists online peers; "+ Adicionar (auto)" detects the SSH user automatically.
- Settings → Servers → "+ Adicionar manual" — fill in host, user, port.
- Edit
settings.jsondirectly while Noctalia is stopped.
{
"servers": [
{
"id": "fedora-server",
"name": "Fedora Server",
"host": "100.118.33.37",
"user": "th",
"port": 22
},
{
"id": "vps-1",
"name": "Some VPS (no SSH access)",
"host": "1.2.3.4",
"manualServices": [
{ "name": "My App", "url": "http://1.2.3.4:3000", "category": "Projetos", "color": "#F2D4D7", "icon": "M" }
]
}
],
"activeServerId": "fedora-server"
}A server with manualServices skips SSH+docker and shows the predefined list. Useful for boxes you don't have shell access on.
Containers are matched by their raw name from docker ps. Override per-container:
"overrides": {
"my-app-container-1": {
"name": "My App",
"category": "Projetos",
"color": "#F59E0B",
"icon": "M",
"iconFile": "icons/custom.png",
"protocol": "https",
"hidden": true
}
}Auto-categorization runs a list of regex against the container name. First match wins. Unmatched containers fall into "Outros".
"categoryRules": [
{ "match": "jellyfin|sonarr|radarr", "category": "Media" },
{ "match": "nextcloud|immich", "category": "Cloud" }
]Maps service slugs to logo files. Partial matching: if the container name contains the slug (immich_server contains immich), the icon is used.
"iconMap": {
"jellyfin": "icons/jellyfin.png",
"my-thing": "icons/my-thing.png"
}Drop any PNG/SVG in icons/ and reference it.
Exposed via Quickshell IPC.
# Force a discovery cycle on the active server
qs -c noctalia-shell ipc call plugin:server-dashboard discover
# Health check (curl) all known services
qs -c noctalia-shell ipc call plugin:server-dashboard refresh
# Switch to another configured server
qs -c noctalia-shell ipc call plugin:server-dashboard switchServer my-vps
# Open a service in the browser by name
qs -c noctalia-shell ipc call plugin:server-dashboard open Jellyfin
# List Tailscale peers (populates internal state for the Settings UI)
qs -c noctalia-shell ipc call plugin:server-dashboard listTailscale
# Manually trigger SSH user auto-detection for a server
qs -c noctalia-shell ipc call plugin:server-dashboard autoDetect my-vps
# Add a server with explicit user
qs -c noctalia-shell ipc call plugin:server-dashboard addServerSimple my-vps 1.2.3.4 root
# Add a server and auto-detect the SSH user
qs -c noctalia-shell ipc call plugin:server-dashboard addServerAuto my-vps 1.2.3.4
# Edit SSH user of a server (clears its discovery cache, re-runs discovery)
qs -c noctalia-shell ipc call plugin:server-dashboard editServerUser my-vps root
# Remove a server
qs -c noctalia-shell ipc call plugin:server-dashboard removeServer my-vps
# JSON status (active server, counts, errors, timestamps)
qs -c noctalia-shell ipc call plugin:server-dashboard status
# Open the panel
qs -c noctalia-shell ipc call plugin:server-dashboard togglePanelpanel open / 5min timer / IPC discover
|
active server has manualServices?
/ \
yes no
| |
use that list ssh -o ConnectTimeout=3 -BatchMode=yes
user@host 'docker ps --format ...'
|
exit code 0?
/ \
yes no
| |
parse {Name|Ports|Image} err contains "user doesn't exist"
| or "Permission denied"?
filter: port on 0.0.0.0 |
or Tailscale IP, not 127.x yes (first attempt)
| |
apply overrides[name] auto-detect SSH user in parallel
| (10 candidates, picks first that works)
cache + render |
| editServer({user: detected})
parallel curl on each |
URL (xargs -P 8, 3s retry discovery
timeout) -> status dots
A second failure (after auto-recovery) shows the error in the panel with a retry button. The recovery flag clears once discovery succeeds.
Override via categoryColors in settings.json. Per-container color in overrides always wins.
| Category | Color |
|---|---|
| Media | #7B2CBF |
| Cloud | #0082C9 |
| Dev | #8B5CF6 |
| Home | #2BB67D |
| Pentest | #DC2626 |
| Projetos | #F59E0B |
| Infra | #64748B |
| Outros | #94A3B8 |
Panel says "Servidor inalcançável" — Tailscale isn't connected, or the host's IP changed. Check tailscale status.
"SSH negado" or "Nenhum usuário SSH funcionou" — your SSH key isn't on the remote host. Run ssh-copy-id <user>@<host> once. Auto-detection will then pick it up.
"Docker não encontrado no servidor" — install Docker on the remote, or ensure the SSH user is in the docker group so it can run docker ps without sudo.
Service shows as "down" but I can open it manually — by default any HTTP response (200, 302, 401, 403) is considered up. If your service returns 5xx, it'll show down. Adjust the curl logic in Main.qml if needed.
Plugin save wipes my manualServices — known limitation. Stop Noctalia (pkill -f noctalia-shell) before editing settings.json manually. The plugin's saveSettings() doesn't know about custom fields and may overwrite them.
- Noctalia Shell — the Wayland desktop shell this plugin runs in.
- homarr-labs/dashboard-icons — service logos.
- Quickshell — the QML engine powering Noctalia.
MIT — see LICENSE.
