Replace three custom Prometheus textfile collector scripts with
dedicated community-maintained exporters:
- jellyfin-collector.nix (25 LoC shell) -> rebelcore/jellyfin_exporter
Metric: jellyfin_active_streams -> count(jellyfin_now_playing_state)
Bonus: per-session labels (user, title, device, codec info)
- qbittorrent-collector.nix (40 LoC shell) -> anriha/qbittorrent-metrics-exporter
Metric: qbittorrent_{download,upload}_bytes_per_second -> qbit_{dl,up}speed
Bonus: per-torrent metrics with category/tag aggregation
- intel-gpu-collector.nix + .py (130 LoC Python) -> mike1808/igpu-exporter
Metric: intel_gpu_engine_busy_percent -> igpu_engines_busy_percent
Bonus: persistent daemon vs oneshot timer, no streaming JSON parser
All three run as persistent daemons scraped by Prometheus, replacing
the textfile-collector pattern of systemd timers writing .prom files.
Dashboard PromQL queries updated to match new metric names.
697 lines
16 KiB
Nix
697 lines
16 KiB
Nix
{
|
|
...
|
|
}:
|
|
let
|
|
promDs = {
|
|
type = "prometheus";
|
|
uid = "prometheus";
|
|
};
|
|
|
|
dashboard = {
|
|
editable = true;
|
|
graphTooltip = 1;
|
|
schemaVersion = 39;
|
|
tags = [
|
|
"system"
|
|
"monitoring"
|
|
];
|
|
time = {
|
|
from = "now-6h";
|
|
to = "now";
|
|
};
|
|
timezone = "browser";
|
|
title = "System Overview";
|
|
uid = "system-overview";
|
|
|
|
annotations.list = [
|
|
{
|
|
name = "Jellyfin Streams";
|
|
datasource = {
|
|
type = "grafana";
|
|
uid = "-- Grafana --";
|
|
};
|
|
enable = true;
|
|
iconColor = "green";
|
|
showIn = 0;
|
|
type = "tags";
|
|
tags = [ "jellyfin" ];
|
|
}
|
|
{
|
|
name = "ZFS Scrubs";
|
|
datasource = {
|
|
type = "grafana";
|
|
uid = "-- Grafana --";
|
|
};
|
|
enable = true;
|
|
iconColor = "orange";
|
|
showIn = 0;
|
|
type = "tags";
|
|
tags = [ "zfs-scrub" ];
|
|
}
|
|
{
|
|
name = "LLM Requests";
|
|
datasource = {
|
|
type = "grafana";
|
|
uid = "-- Grafana --";
|
|
};
|
|
enable = true;
|
|
iconColor = "purple";
|
|
showIn = 0;
|
|
type = "tags";
|
|
tags = [ "llama-cpp" ];
|
|
}
|
|
];
|
|
|
|
panels = [
|
|
# -- Row 1: UPS --
|
|
{
|
|
id = 1;
|
|
type = "timeseries";
|
|
title = "UPS Power Draw";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 8;
|
|
x = 0;
|
|
y = 0;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "apcupsd_ups_load_percent / 100 * apcupsd_nominal_power_watts";
|
|
legendFormat = "Power (W)";
|
|
refId = "A";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "avg_over_time((apcupsd_ups_load_percent / 100 * apcupsd_nominal_power_watts + 4.5)[5m:])";
|
|
legendFormat = "5m average (W)";
|
|
refId = "B";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "watt";
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 2;
|
|
fillOpacity = 20;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "A";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "custom.lineStyle";
|
|
value = {
|
|
fill = "dot";
|
|
};
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 10;
|
|
}
|
|
{
|
|
id = "custom.lineWidth";
|
|
value = 1;
|
|
}
|
|
{
|
|
id = "custom.pointSize";
|
|
value = 1;
|
|
}
|
|
];
|
|
}
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "B";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "custom.lineWidth";
|
|
value = 4;
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 0;
|
|
}
|
|
];
|
|
}
|
|
];
|
|
};
|
|
}
|
|
{
|
|
id = 7;
|
|
type = "stat";
|
|
title = "Energy Usage (24h)";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 4;
|
|
x = 8;
|
|
y = 0;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "avg_over_time((apcupsd_ups_load_percent / 100 * apcupsd_nominal_power_watts + 4.5)[24h:]) * 24 / 1000";
|
|
legendFormat = "";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "kwatth";
|
|
decimals = 2;
|
|
thresholds = {
|
|
mode = "absolute";
|
|
steps = [
|
|
{
|
|
color = "green";
|
|
value = null;
|
|
}
|
|
{
|
|
color = "yellow";
|
|
value = 5;
|
|
}
|
|
{
|
|
color = "red";
|
|
value = 10;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
options = {
|
|
reduceOptions = {
|
|
calcs = [ "lastNotNull" ];
|
|
fields = "";
|
|
values = false;
|
|
};
|
|
colorMode = "value";
|
|
graphMode = "none";
|
|
};
|
|
}
|
|
{
|
|
id = 2;
|
|
type = "gauge";
|
|
title = "UPS Load";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 6;
|
|
x = 12;
|
|
y = 0;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "apcupsd_ups_load_percent";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "percent";
|
|
min = 0;
|
|
max = 100;
|
|
thresholds = {
|
|
mode = "absolute";
|
|
steps = [
|
|
{
|
|
color = "green";
|
|
value = null;
|
|
}
|
|
{
|
|
color = "yellow";
|
|
value = 70;
|
|
}
|
|
{
|
|
color = "red";
|
|
value = 90;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
options.reduceOptions = {
|
|
calcs = [ "lastNotNull" ];
|
|
fields = "";
|
|
values = false;
|
|
};
|
|
}
|
|
{
|
|
id = 3;
|
|
type = "gauge";
|
|
title = "UPS Battery";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 6;
|
|
x = 18;
|
|
y = 0;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "apcupsd_battery_charge_percent";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "percent";
|
|
min = 0;
|
|
max = 100;
|
|
thresholds = {
|
|
mode = "absolute";
|
|
steps = [
|
|
{
|
|
color = "red";
|
|
value = null;
|
|
}
|
|
{
|
|
color = "yellow";
|
|
value = 20;
|
|
}
|
|
{
|
|
color = "green";
|
|
value = 50;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
options.reduceOptions = {
|
|
calcs = [ "lastNotNull" ];
|
|
fields = "";
|
|
values = false;
|
|
};
|
|
}
|
|
|
|
# -- Row 2: System --
|
|
{
|
|
id = 4;
|
|
type = "timeseries";
|
|
title = "CPU Temperature";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 12;
|
|
x = 0;
|
|
y = 8;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = ''node_hwmon_temp_celsius{chip=~"pci.*"}'';
|
|
legendFormat = "CPU {{sensor}}";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "celsius";
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 2;
|
|
fillOpacity = 10;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
}
|
|
{
|
|
id = 5;
|
|
type = "stat";
|
|
title = "System Uptime";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 6;
|
|
x = 12;
|
|
y = 8;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "time() - node_boot_time_seconds";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "s";
|
|
thresholds = {
|
|
mode = "absolute";
|
|
steps = [
|
|
{
|
|
color = "green";
|
|
value = null;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
options = {
|
|
reduceOptions = {
|
|
calcs = [ "lastNotNull" ];
|
|
fields = "";
|
|
values = false;
|
|
};
|
|
colorMode = "value";
|
|
graphMode = "none";
|
|
};
|
|
}
|
|
{
|
|
id = 6;
|
|
type = "stat";
|
|
title = "Jellyfin Active Streams";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 6;
|
|
x = 18;
|
|
y = 8;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "count(jellyfin_now_playing_state) or vector(0)";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
thresholds = {
|
|
mode = "absolute";
|
|
steps = [
|
|
{
|
|
color = "green";
|
|
value = null;
|
|
}
|
|
{
|
|
color = "yellow";
|
|
value = 3;
|
|
}
|
|
{
|
|
color = "red";
|
|
value = 6;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
options = {
|
|
reduceOptions = {
|
|
calcs = [ "lastNotNull" ];
|
|
fields = "";
|
|
values = false;
|
|
};
|
|
colorMode = "value";
|
|
graphMode = "area";
|
|
};
|
|
}
|
|
|
|
# -- Row 3: qBittorrent --
|
|
{
|
|
id = 11;
|
|
type = "timeseries";
|
|
title = "qBittorrent Speed";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 24;
|
|
x = 0;
|
|
y = 16;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "sum(qbit_dlspeed) or vector(0)";
|
|
legendFormat = "Download";
|
|
refId = "A";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "sum(qbit_upspeed) or vector(0)";
|
|
legendFormat = "Upload";
|
|
refId = "B";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "avg_over_time((sum(qbit_dlspeed) or vector(0))[10m:])";
|
|
legendFormat = "Download (10m avg)";
|
|
refId = "C";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "avg_over_time((sum(qbit_upspeed) or vector(0))[10m:])";
|
|
legendFormat = "Upload (10m avg)";
|
|
refId = "D";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "binBps";
|
|
min = 0;
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 1;
|
|
fillOpacity = 10;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "A";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "color";
|
|
value = {
|
|
fixedColor = "green";
|
|
mode = "fixed";
|
|
};
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 5;
|
|
}
|
|
];
|
|
}
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "B";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "color";
|
|
value = {
|
|
fixedColor = "blue";
|
|
mode = "fixed";
|
|
};
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 5;
|
|
}
|
|
];
|
|
}
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "C";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "color";
|
|
value = {
|
|
fixedColor = "green";
|
|
mode = "fixed";
|
|
};
|
|
}
|
|
{
|
|
id = "custom.lineWidth";
|
|
value = 3;
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 0;
|
|
}
|
|
];
|
|
}
|
|
{
|
|
matcher = {
|
|
id = "byFrameRefID";
|
|
options = "D";
|
|
};
|
|
properties = [
|
|
{
|
|
id = "color";
|
|
value = {
|
|
fixedColor = "blue";
|
|
mode = "fixed";
|
|
};
|
|
}
|
|
{
|
|
id = "custom.lineWidth";
|
|
value = 3;
|
|
}
|
|
{
|
|
id = "custom.fillOpacity";
|
|
value = 0;
|
|
}
|
|
];
|
|
}
|
|
];
|
|
};
|
|
}
|
|
|
|
# -- Row 4: Intel GPU --
|
|
{
|
|
id = 8;
|
|
type = "timeseries";
|
|
title = "Intel GPU Utilization";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 24;
|
|
x = 0;
|
|
y = 24;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "igpu_engines_busy_percent";
|
|
legendFormat = "{{engine}}";
|
|
refId = "A";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "percent";
|
|
min = 0;
|
|
max = 100;
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 2;
|
|
fillOpacity = 10;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
}
|
|
|
|
# -- Row 5: Storage --
|
|
{
|
|
id = 12;
|
|
type = "timeseries";
|
|
title = "ZFS Pool Utilization";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 12;
|
|
x = 0;
|
|
y = 32;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "zpool_used_bytes{pool=\"tank\"} / zpool_size_bytes{pool=\"tank\"} * 100";
|
|
legendFormat = "tank";
|
|
refId = "A";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "zpool_used_bytes{pool=\"hdds\"} / zpool_size_bytes{pool=\"hdds\"} * 100";
|
|
legendFormat = "hdds";
|
|
refId = "B";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "percent";
|
|
min = 0;
|
|
max = 100;
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 2;
|
|
fillOpacity = 20;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
}
|
|
{
|
|
id = 13;
|
|
type = "timeseries";
|
|
title = "Boot Drive Partitions";
|
|
gridPos = {
|
|
h = 8;
|
|
w = 12;
|
|
x = 12;
|
|
y = 32;
|
|
};
|
|
datasource = promDs;
|
|
targets = [
|
|
{
|
|
datasource = promDs;
|
|
expr = "partition_used_bytes{mount=\"/boot\"} / partition_size_bytes{mount=\"/boot\"} * 100";
|
|
legendFormat = "/boot";
|
|
refId = "A";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "partition_used_bytes{mount=\"/persistent\"} / partition_size_bytes{mount=\"/persistent\"} * 100";
|
|
legendFormat = "/persistent";
|
|
refId = "B";
|
|
}
|
|
{
|
|
datasource = promDs;
|
|
expr = "partition_used_bytes{mount=\"/nix\"} / partition_size_bytes{mount=\"/nix\"} * 100";
|
|
legendFormat = "/nix";
|
|
refId = "C";
|
|
}
|
|
];
|
|
fieldConfig = {
|
|
defaults = {
|
|
unit = "percent";
|
|
min = 0;
|
|
max = 100;
|
|
color.mode = "palette-classic";
|
|
custom = {
|
|
lineWidth = 2;
|
|
fillOpacity = 20;
|
|
spanNulls = true;
|
|
};
|
|
};
|
|
overrides = [ ];
|
|
};
|
|
}
|
|
];
|
|
};
|
|
in
|
|
{
|
|
environment.etc."grafana-dashboards/system-overview.json" = {
|
|
text = builtins.toJSON dashboard;
|
|
mode = "0444";
|
|
};
|
|
}
|