{ config, pkgs, inputs, service_configs, lib, ... }: let jellyfinExporterPort = service_configs.ports.private.jellyfin_exporter.port; qbitExporterPort = service_configs.ports.private.qbittorrent_exporter.port; igpuExporterPort = service_configs.ports.private.igpu_exporter.port; in { # -- Jellyfin Prometheus Exporter -- # Replaces custom jellyfin-collector.nix textfile timer. # Exposes per-session metrics (jellyfin_now_playing_state) and library stats. systemd.services.jellyfin-exporter = lib.mkIf (config.services.grafana.enable && config.services.jellyfin.enable) { description = "Prometheus exporter for Jellyfin"; after = [ "network.target" "jellyfin.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = lib.getExe ( pkgs.writeShellApplication { name = "jellyfin-exporter-wrapper"; runtimeInputs = [ pkgs.jellyfin-exporter ]; text = '' exec jellyfin_exporter \ --jellyfin.address=http://127.0.0.1:${toString service_configs.ports.private.jellyfin.port} \ --jellyfin.token="$(cat "$CREDENTIALS_DIRECTORY/jellyfin-api-key")" \ --web.listen-address=127.0.0.1:${toString jellyfinExporterPort} ''; } ); Restart = "on-failure"; RestartSec = "10s"; DynamicUser = true; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; MemoryDenyWriteExecute = true; LoadCredential = "jellyfin-api-key:${config.age.secrets.jellyfin-api-key.path}"; }; }; # -- qBittorrent Prometheus Exporter -- # Replaces custom qbittorrent-collector.nix textfile timer. # Exposes per-torrent metrics (qbit_dlspeed, qbit_upspeed) and aggregate stats. # qBittorrent runs in a VPN namespace; the exporter reaches it via namespace address. systemd.services.qbittorrent-exporter = lib.mkIf (config.services.grafana.enable && config.services.qbittorrent.enable) { description = "Prometheus exporter for qBittorrent"; after = [ "network.target" "qbittorrent.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = lib.getExe' inputs.qbittorrent-metrics-exporter.packages.${pkgs.system}.default "qbittorrent-metrics-exporter"; Restart = "on-failure"; RestartSec = "10s"; DynamicUser = true; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; }; environment = { HOST = "127.0.0.1"; PORT = toString qbitExporterPort; SCRAPE_INTERVAL = "15"; BACKEND = "in-memory"; # qBittorrent has AuthSubnetWhitelist=0.0.0.0/0, so no real password needed. # The exporter still expects the env var to be set. QBITTORRENT_PASSWORD = "unused"; QBITTORRENT_USERNAME = "admin"; TORRENT_HOSTS = "qbit:main=http://${config.vpnNamespaces.wg.namespaceAddress}:${toString config.services.qbittorrent.webuiPort}|http://${config.vpnNamespaces.wg.namespaceAddress}:${toString config.services.qbittorrent.webuiPort}"; RUST_LOG = "warn"; }; }; # -- Intel GPU Prometheus Exporter -- # Replaces custom intel-gpu-collector.nix + intel-gpu-collector.py textfile timer. # Exposes engine busy%, frequency, and RC6 metrics via /metrics. # Requires privileged access to GPU debug interfaces (intel_gpu_top). systemd.services.igpu-exporter = lib.mkIf config.services.grafana.enable { description = "Prometheus exporter for Intel integrated GPU"; wantedBy = [ "multi-user.target" ]; path = [ pkgs.intel-gpu-tools ]; serviceConfig = { ExecStart = lib.getExe pkgs.igpu-exporter; Restart = "on-failure"; RestartSec = "10s"; # intel_gpu_top requires root-level access to GPU debug interfaces ProtectHome = true; PrivateTmp = true; }; environment = { PORT = toString igpuExporterPort; REFRESH_PERIOD_MS = "30000"; }; }; }