aboutsummaryrefslogtreecommitdiffstats
path: root/modules/by-name/mo/monitoring/components
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/by-name/mo/monitoring/components/grafana.nix309
-rw-r--r--modules/by-name/mo/monitoring/components/loki.nix170
-rw-r--r--modules/by-name/mo/monitoring/components/perses.nix17
-rw-r--r--modules/by-name/mo/monitoring/components/prometheus.nix272
-rw-r--r--modules/by-name/mo/monitoring/components/scrutiny.nix100
5 files changed, 868 insertions, 0 deletions
diff --git a/modules/by-name/mo/monitoring/components/grafana.nix b/modules/by-name/mo/monitoring/components/grafana.nix
new file mode 100644
index 0000000..6fe5b70
--- /dev/null
+++ b/modules/by-name/mo/monitoring/components/grafana.nix
@@ -0,0 +1,309 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.vhack.monitoring.grafana;
+in {
+ options.vhack.monitoring.grafana = {
+ enable = lib.mkEnableOption "grafana";
+
+ orgId = lib.mkOption {
+ type = lib.types.int;
+ description = "Org ID where all vhack related config will be stored.";
+ default = 1;
+ };
+
+ contactPoints = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "List of email addresses to send alerts to";
+ default = [];
+ };
+
+ dashboards = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ description = "dashboards to provision under 'Vhack' folder.";
+ default = [];
+ };
+
+ fqdn = lib.mkOption {
+ type = lib.types.str;
+ description = "Domain under which Grafana will be served.";
+ example = "grafana.example.org";
+ };
+
+ adminPassword = lib.mkOption {
+ type = lib.types.path;
+ description = "The age-encrypted initial admin password; passed to agenix";
+ };
+
+ secretKey = lib.mkOption {
+ type = lib.types.path;
+ description = "The age-encrypted secret key, used for signing; passed to agenix";
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = "Port where Grafana listens to HTTP requests.";
+ default = 3000;
+ };
+
+ debugLog = lib.mkOption {
+ type = lib.types.bool;
+ description = "Set to true to enable debug logging of the infrastructure serving Grafana.";
+ default = false;
+ example = true;
+ };
+
+ smtp = lib.mkOption {
+ description = "SMTP options.";
+ default = null;
+ type = lib.types.nullOr (
+ lib.types.submodule {
+ options = {
+ from_address = lib.mkOption {
+ type = lib.types.str;
+ description = "SMTP address from which the emails originate.";
+ example = "vaultwarden@mydomain.com";
+ };
+ from_name = lib.mkOption {
+ type = lib.types.str;
+ description = "SMTP name from which the emails originate.";
+ default = "Grafana";
+ };
+ host = lib.mkOption {
+ type = lib.types.str;
+ description = "SMTP host to send the emails to.";
+ };
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = "SMTP port to send the emails to.";
+ default = 25;
+ };
+ username = lib.mkOption {
+ type = lib.types.str;
+ description = "Username to connect to the SMTP host.";
+ };
+ passwordFile = lib.mkOption {
+ type = lib.types.str;
+ description = "File containing the password to connect to the SMTP host.";
+ };
+ };
+ }
+ );
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = builtins.length cfg.contactPoints > 0;
+ message = "Must have at least one contact point for alerting";
+ }
+ ];
+
+ age.secrets = {
+ grafanaInitialAdminPw = {
+ file = cfg.adminPassword;
+ mode = "0700";
+ owner = "grafana";
+ group = "grafana";
+ };
+ grafanaSecretKey = {
+ file = cfg.secretKey;
+ mode = "0700";
+ owner = "grafana";
+ group = "grafana";
+ };
+ };
+
+ users = {
+ groups = {
+ grafana = {
+ gid = config.vhack.constants.ids.gids.grafana;
+ };
+ };
+ users = {
+ grafana = {
+ isSystemUser = true;
+ group = "grafana";
+ uid = lib.mkForce config.vhack.constants.ids.uids.grafana;
+ };
+ };
+ };
+
+ vhack = {
+ postgresql.enable = true;
+
+ persist.directories = [
+ {
+ directory = "/var/lib/grafana/";
+ user = "grafana";
+ group = "grafana";
+ mode = "0700";
+ }
+ ];
+
+ monitoring.grafana.dashboards = [
+ ../dashboards/node_exporter.json
+ ../dashboards/performance.json
+ ../dashboards/scraping_jobs.json
+ ../dashboards/systemd_exporter.json
+ ];
+
+ anubis.instances."${cfg.fqdn}".target = "http://${config.services.grafana.settings.server.http_addr}:${toString cfg.port}";
+ };
+
+ services = {
+ postgresql = {
+ ensureUsers = [
+ {
+ name = "grafana";
+ ensureDBOwnership = true;
+ }
+ ];
+ ensureDatabases = [
+ "grafana"
+ ];
+ };
+
+ grafana = {
+ enable = true;
+
+ settings = {
+ database = {
+ host = "/run/postgresql";
+ user = "grafana";
+ name = "grafana";
+ type = "postgres";
+ };
+
+ security = {
+ secret_key = "$__file{${config.age.secrets.grafanaSecretKey.path}}";
+
+ disable_initial_admin_creation = false;
+ admin_password = "$__file{${config.age.secrets.grafanaInitialAdminPw.path}}";
+ };
+
+ server = {
+ http_addr = "127.0.0.1";
+ http_port = cfg.port;
+ domain = cfg.fqdn;
+ root_url = "https://${cfg.fqdn}";
+ router_logging = cfg.debugLog;
+ };
+
+ smtp = lib.mkIf (!(isNull cfg.smtp)) {
+ enabled = true;
+ inherit (cfg.smtp) from_address from_name;
+ host = "${cfg.smtp.host}:${toString cfg.smtp.port}";
+ user = cfg.smtp.username;
+ password = "$__file{${cfg.smtp.passwordFile}}";
+ };
+ };
+
+ provision = {
+ dashboards.settings = lib.mkIf (cfg.dashboards != []) {
+ apiVersion = 1;
+ providers = [
+ {
+ folder = "Vhack";
+ options.path = pkgs.symlinkJoin {
+ name = "dashboards";
+ paths = map (p: pkgs.runCommand "dashoard" {} "mkdir $out; cp ${p} $out") cfg.dashboards;
+ };
+ allowUiUpdates = true;
+ disableDeletion = true;
+ }
+ ];
+ };
+ datasources.settings = {
+ apiVersion = 1;
+ datasources = [
+ {
+ inherit (cfg) orgId;
+ name = "Prometheus";
+ type = "prometheus";
+ url = "http://127.0.0.1:${toString config.services.prometheus.port}";
+ uid = "df80f9f5-97d7-4112-91d8-72f523a02b09";
+ isDefault = true;
+ version = 1;
+ }
+ {
+ inherit (cfg) orgId;
+ name = "Loki";
+ type = "loki";
+ url = "http://127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}";
+ uid = "cd6cc53e-840c-484d-85f7-96fede324006";
+ version = 1;
+ }
+ ];
+ deleteDatasources = [
+ {
+ inherit (cfg) orgId;
+ name = "Prometheus";
+ }
+ {
+ inherit (cfg) orgId;
+ name = "Loki";
+ }
+ ];
+ };
+ alerting = {
+ contactPoints.settings = {
+ apiVersion = 1;
+ contactPoints = [
+ {
+ inherit (cfg) orgId;
+ name = "grafana-default-email";
+ receivers = lib.optionals ((builtins.length cfg.contactPoints) > 0) [
+ {
+ uid = "sysadmin";
+ type = "email";
+ settings.addresses = lib.concatStringsSep ";" cfg.contactPoints;
+ }
+ ];
+ }
+ ];
+ };
+ policies.settings = {
+ apiVersion = 1;
+ policies = [
+ {
+ inherit (cfg) orgId;
+ receiver = "grafana-default-email";
+ group_by = [
+ "grafana_folder"
+ "alertname"
+ ];
+ group_wait = "30s";
+ group_interval = "5m";
+ repeat_interval = "4h";
+ }
+ ];
+ # resetPolicies seems to happen after setting the above policies, effectively rolling back
+ # any updates.
+ };
+ rules.settings = let
+ rules = builtins.fromJSON (builtins.readFile ../rules.json);
+ in {
+ apiVersion = 1;
+ groups = [
+ {
+ inherit (cfg) orgId;
+ name = "SysAdmin";
+ folder = "Vhack";
+ interval = "10m";
+ inherit rules;
+ }
+ ];
+ # deleteRules seems to happen after creating the above rules, effectively rolling back
+ # any updates.
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/by-name/mo/monitoring/components/loki.nix b/modules/by-name/mo/monitoring/components/loki.nix
new file mode 100644
index 0000000..149e577
--- /dev/null
+++ b/modules/by-name/mo/monitoring/components/loki.nix
@@ -0,0 +1,170 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.vhack.monitoring.loki;
+in {
+ options.vhack.monitoring.loki = {
+ enable = lib.mkEnableOption "loki";
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = "Port where Loki listens to HTTP requests.";
+ default = 3002;
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ vhack = {
+ persist.directories = [
+ {
+ directory = "/var/lib/loki";
+ user = "loki";
+ group = "loki";
+ mode = "0700";
+ }
+ {
+ directory = "/var/lib/fluent-bit";
+ user = "fluent-bit";
+ group = "fluent-bit";
+ mode = "0700";
+ }
+ ];
+ };
+
+ users = {
+ groups = {
+ fluent-bit = {
+ gid = config.vhack.constants.ids.gids.fluent-bit;
+ };
+ loki = {
+ gid = config.vhack.constants.ids.gids.loki;
+ };
+ };
+ users = {
+ fluent-bit = {
+ isSystemUser = true;
+ group = "fluent-bit";
+ uid = config.vhack.constants.ids.uids.fluent-bit;
+ };
+ loki = {
+ isSystemUser = true;
+ group = "loki";
+ uid = config.vhack.constants.ids.uids.loki;
+ };
+ };
+ };
+
+ # I decided to switch to fluent-bit because it can be tested locally https://docs.fluentbit.io/manual/local-testing/logging-pipeline
+ systemd.services.fluent-bit = {
+ serviceConfig = {
+ StateDirectory = "fluent-bit";
+ User = "fluent-bit";
+ Group = "fluent-bit";
+ DynamicUser = lib.mkForce false;
+ };
+ };
+ services = {
+ fluent-bit = {
+ enable = true;
+ settings = {
+ service = {
+ flush = 1;
+ log_level = "info";
+ http_server = "true";
+ http_listen = "127.0.0.1";
+ http_port = 9080;
+ grace = 30;
+ };
+
+ pipeline = {
+ inputs = [
+ {
+ name = "systemd";
+
+ # The asterisk appends the _SYSTEMD_UNIT to the prefix.
+ tag = "systemd.*";
+
+ # Read logs from this systemd journal directory.
+ path = "/var/log/journal";
+
+ # Database file to keep track of the journald cursor.
+ db = "/var/lib/fluent-bit/systemd.db";
+
+ # Start reading new entries. Skip entries already stored in journald.
+ read_from_tail = true;
+
+ # Max entries to lookback on start.
+ max_entries = 10000;
+ }
+ ];
+
+ outputs = [
+ {
+ name = "loki";
+
+ match = "systemd.*";
+
+ host = "localhost";
+ port = config.services.loki.configuration.server.http_listen_port;
+
+ labels = lib.concatMapAttrsStringSep ", " (n: v: "${n}=${v}") {
+ job = "systemd-journal";
+ inherit (config.vhack.monitoring.grafana) fqdn;
+ hostname = config.networking.hostName;
+ };
+
+ label_keys = "$unit";
+ }
+ ];
+ };
+ };
+ graceLimit = "1m";
+ };
+
+ loki = {
+ enable = true;
+ dataDir = "/var/lib/loki";
+ package = pkgs.grafana-loki;
+ configuration = {
+ auth_enabled = false;
+
+ server.http_listen_port = cfg.port;
+
+ common = {
+ ring = {
+ instance_addr = "127.0.0.1";
+ kvstore = {
+ store = "inmemory";
+ };
+ };
+
+ replication_factor = 1;
+ path_prefix = "/tmp/loki";
+ };
+
+ schema_config = {
+ configs = [
+ {
+ from = "2020-05-15";
+ store = "tsdb";
+ object_store = "filesystem";
+ schema = "v13";
+ index = {
+ prefix = "index_";
+ period = "24h";
+ };
+ }
+ ];
+ };
+
+ storage_config = {
+ filesystem.directory = "/tmp/loki/chunks";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/by-name/mo/monitoring/components/perses.nix b/modules/by-name/mo/monitoring/components/perses.nix
new file mode 100644
index 0000000..b0f1ab6
--- /dev/null
+++ b/modules/by-name/mo/monitoring/components/perses.nix
@@ -0,0 +1,17 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.vhack.monitoring.perses;
+in {
+ options.vhack.monitoring.perses = {
+ enable = lib.mkEnableOption "perses";
+ };
+ config = lib.mkIf cfg.enable {
+ services.perses = {
+ enable = true;
+ };
+ };
+}
diff --git a/modules/by-name/mo/monitoring/components/prometheus.nix b/modules/by-name/mo/monitoring/components/prometheus.nix
new file mode 100644
index 0000000..ca947d3
--- /dev/null
+++ b/modules/by-name/mo/monitoring/components/prometheus.nix
@@ -0,0 +1,272 @@
+{
+ lib,
+ config,
+ nixLib,
+ ...
+}: let
+ cfg = config.vhack.monitoring.prometheus;
+
+ commonLabels = {
+ hostname = config.networking.hostName;
+ };
+
+ prom_cfg = config.services.prometheus;
+in {
+ options.vhack.monitoring.prometheus = {
+ enable = lib.mkEnableOption "prometheus";
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = "Port where Prometheus listens to HTTP requests.";
+ default = 3001;
+ };
+
+ remoteWriteReceiver = lib.mkOption {
+ example = "https://prometheus.server2.vhack.eu";
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ description = ''
+ If not null, this prometheus server will be exposed via a reverse proxy and allow
+ other prometheus servers to write metrics to it.
+ '';
+ };
+ remoteWriteTo = lib.mkOption {
+ example = "https://prometheus.server3.vhack.eu";
+ type = lib.types.nullOr (lib.types.submodule {
+ options = {
+ port = lib.mkOption {
+ type = lib.types.nullOr lib.types.port;
+ default = null;
+ };
+ url = lib.mkOption {
+ type = lib.types.str;
+ };
+ };
+ });
+ default = null;
+ description = ''
+ If not null, this prometheus server will run in agent mode, writing all recived
+ metrics to the specified remote prometheus server.
+ '';
+ };
+
+ sources = lib.mkOption {
+ type = lib.types.listOf (lib.types.submodule {
+ options = {
+ name = lib.mkOption {
+ type = lib.types.str;
+ };
+ target = lib.mkOption {
+ type = lib.types.str;
+ description = "The target url to scrape from.";
+ };
+ extra = lib.mkOption {
+ type = lib.types.attrsOf lib.types.anything;
+ };
+ };
+ });
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ vhack = {
+ nginx.enable = true;
+
+ persist.directories = [
+ {
+ directory = "/var/lib/prometheus2";
+ user = "prometheus";
+ group = "prometheus";
+ mode = "0700";
+ }
+ ];
+
+ monitoring.prometheus.sources = [
+ {
+ name = "node";
+ target = "127.0.0.1:${toString prom_cfg.exporters.node.port}";
+ }
+ {
+ name = "prometheus_internal";
+ target = "127.0.0.1:${toString prom_cfg.port}";
+ }
+ {
+ name = "systemd";
+ target = "127.0.0.1:${toString prom_cfg.exporters.systemd.port}";
+ }
+ ];
+ };
+
+ services.nginx = lib.mkIf (cfg.remoteWriteReceiver != null) {
+ virtualHosts.${cfg.remoteWriteReceiver} = {
+ locations."/" = {
+ proxyPass = "http://127.0.0.1:${toString cfg.port}/api/v1/write";
+
+ recommendedProxySettings = true;
+ proxyWebsockets = true;
+ };
+
+ enableACME = true;
+ forceSSL = true;
+ };
+ };
+
+ users = {
+ groups = {
+ node-exporter.gid = config.vhack.constants.ids.gids.node-exporter;
+ };
+ users = {
+ node-exporter = {
+ isSystemUser = true;
+ group = "node-exporter";
+ uid = config.vhack.constants.ids.uids.node-exporter;
+ };
+ };
+ };
+
+ services = {
+ prometheus = {
+ enable = true;
+ inherit (cfg) port;
+
+ globalConfig = {
+ scrape_interval = "15s";
+ };
+ remoteWrite = lib.optionals (cfg.remoteWriteTo != null) [
+ {
+ url = let
+ port =
+ if (cfg.remoteWriteTo.port != null)
+ then ":${toString cfg.remoteWriteTo.port}"
+ else "";
+ in "${cfg.remoteWriteTo.url}${port}";
+ # TODO: Needs RFC42 for prometheus <2026-07-19>
+ # protobuf_message = "io.prometheus.write.v2.Request";
+ }
+ ];
+
+ enableAgentMode = cfg.remoteWriteTo != null;
+ extraFlags = lib.mkMerge [
+ (
+ lib.mkIf (cfg.remoteWriteReceiver != null) [
+ "--web.enable-remote-write-receiver"
+ # "--web.remote-write-receiver.accepted-protobuf-messages=io.prometheus.write.v2.Request"
+ ]
+ )
+ ];
+
+ scrapeConfigs = builtins.map (attr:
+ nixLib.warnMerge {
+ job_name = attr.name;
+ static_configs = [
+ {
+ targets = [attr.target];
+ labels = commonLabels;
+ }
+ ];
+ }
+ attr.extra "prometheus scrapeConfig '${attr.name}'")
+ cfg.sources;
+ # ++ (lib.optional (builtins.length (lib.attrNames config.services.redis.servers) > 0) {
+ # job_name = "redis";
+ # static_configs = [
+ # {
+ # targets = ["127.0.0.1:${toString config.services.prometheus.exporters.redis.port}"];
+ # }
+ # ];
+ # }) ++ (lib.optional (builtins.length (lib.attrNames config.services.openvpn.servers) > 0) {
+ # job_name = "openvpn";
+ # static_configs = [
+ # {
+ # targets = ["127.0.0.1:${toString config.services.prometheus.exporters.openvpn.port}"];
+ # }
+ # ];
+ # })
+ # ++ (lib.optional config.services.dnsmasq.enable {
+ # job_name = "dnsmasq";
+ # static_configs = [
+ # {
+ # targets = ["127.0.0.1:${toString config.services.prometheus.exporters.dnsmasq.port}"];
+ # labels = commonLabels;
+ # }
+ # ];
+ # });
+
+ exporters = {
+ node = {
+ enable = true;
+ # https://github.com/prometheus/node_exporter#collectors
+ enabledCollectors = [
+ "arp"
+ "cpu"
+ "cpufreq"
+ "diskstats"
+ "dmi"
+ "edac"
+ "entropy"
+ "filefd"
+ "filesystem"
+ "hwmon"
+ "loadavg"
+ "meminfo"
+ "netclass"
+ "netdev"
+ "netstat"
+ "nvme"
+ "os"
+ "pressure"
+ "rapl"
+ "schedstat"
+ "stat"
+ "thermal_zone"
+ "time"
+ "uname"
+ "vmstat"
+ "zfs"
+
+ # Disabled by default
+ "cgroups"
+ "drm"
+ "ethtool"
+ "logind"
+ "wifi"
+ ];
+ port = 9112;
+ listenAddress = "127.0.0.1";
+ };
+ # services.prometheus.exporters.redis = lib.mkIf (builtins.length (lib.attrNames config.services.redis.servers) > 0) {
+ # enable = true;
+ # port = 9119;
+ # listenAddress = "127.0.0.1";
+ # };
+ # services.prometheus.exporters.openvpn = lib.mkIf (builtins.length (lib.attrNames config.services.openvpn.servers) > 0) {
+ # enable = true;
+ # port = 9121;
+ # listenAddress = "127.0.0.1";
+ # statusPaths = lib.mapAttrsToList (name: _config: "/tmp/openvpn/${name}.status") config.services.openvpn.servers;
+ # };
+ # dnsmasq = lib.mkIf config.services.dnsmasq.enable {
+ # enable = true;
+ # port = 9211;
+ # listenAddress = "127.0.0.1";
+ # };
+ systemd = {
+ enable = true;
+ port = 9116;
+ listenAddress = "127.0.0.1";
+ extraFlags = [
+ "--log.level=info"
+ "--web.disable-exporter-metrics"
+ # "--systemd.collector.enable-resolved"
+ "--systemd.collector.enable-restart-count"
+ # "--systemd.collector.enable-file-descriptor-size"
+ "--systemd.collector.enable-ip-accounting"
+ "--systemd.collector.unit-include=\".+\""
+ "--systemd.collector.unit-exclude=\".+\\\\.(device)\""
+ ];
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/by-name/mo/monitoring/components/scrutiny.nix b/modules/by-name/mo/monitoring/components/scrutiny.nix
new file mode 100644
index 0000000..adb515e
--- /dev/null
+++ b/modules/by-name/mo/monitoring/components/scrutiny.nix
@@ -0,0 +1,100 @@
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}: let
+ cfg = config.vhack.monitoring.scrutiny;
+in {
+ options.vhack.monitoring.scrutiny = {
+ enable = lib.mkEnableOption "scrutiny service";
+ };
+
+ # We don't currently _care_ about the health of the hardware, because hetzner does
+ # this for us (nor can we care, because it's not exposed in the VPS)
+ config = lib.mkIf false {
+ vhack.monitoring = {
+ prometheus.sources = [
+ {
+ name = "scrutiny";
+ extra.metrics_path = "/api/metrics";
+ target = "127.0.0.1:${toString config.services.scrutiny.settings.web.listen.port}";
+ }
+ {
+ name = "smartctl";
+ target = "127.0.0.1:${toString config.services.prometheus.exporters.smartctl.port}";
+ }
+ ];
+ grafana.dashboards = [
+ ../dashboards/Health.json
+ ];
+ };
+
+ users = {
+ groups = {
+ smartctl-exporter-access.gid = config.vhack.constants.ids.gids.smartctl-exporter-access;
+ };
+ users = {
+ smartctl-exporter-access = {
+ isSystemUser = true;
+ group = "smartctl-exporter-access";
+ uid = config.vhack.constants.ids.uids.smartctl-exporter-access;
+ };
+ };
+ };
+
+ services = {
+ scrutiny = {
+ enable = true;
+
+ openFirewall = false;
+
+ # This src includes Prometheus metrics exporter.
+ package = pkgs.scrutiny.overrideAttrs {
+ src = pkgs.fetchFromGitHub {
+ owner = "ibizaman";
+ repo = "scrutiny";
+ rev = "74faf06f77df83f29e7e1806cd88b2fafc0bbb82";
+ hash = "sha256-r0AVWL+E046xHxitwMPfRNTOpjuOk+W6tB41YgmLTPg=";
+ };
+
+ vendorHash = "sha256-kAlnlWnBMFCdgdak5L5hRquRtyLi5MTmDa/kxwqPs4E=";
+ };
+
+ settings = {
+ web = {
+ metrics.enabled = true; # Enables Prometheus exporter
+ listenHost = "127.0.0.1";
+ };
+ };
+
+ collector = {
+ enable = true;
+ };
+ };
+ prometheus = {
+ exporters.smartctl = {
+ enable = true;
+ port = 9115;
+ listenAddress = "127.0.0.1";
+ };
+ };
+ };
+
+ # nixpkgs.overlays = [
+ # (final: prev: {
+ # prometheus-systemd-exporter = prev.prometheus-systemd-exporter.overrideAttrs {
+ # src = final.fetchFromGitHub {
+ # owner = "ibizaman";
+ # repo = prev.prometheus-systemd-exporter.pname;
+ # # rev = "v${prev.prometheus-systemd-exporter.version}";
+ # rev = "next_timer";
+ # sha256 = "sha256-jzkh/616tsJbNxFtZ0xbdBQc16TMIYr9QOkPaeQw8xA=";
+ # };
+ #
+ # vendorHash = "sha256-4hsQ1417jLNOAqGkfCkzrmEtYR4YLLW2j0CiJtPg6GI=";
+ # };
+ # })
+ # ];
+ };
+}