Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

monitoring (aerialis)

This container runs our complete monitoring stack: Prometheus, Alertmanager, Grafana and Loki, together with the exporters that feed them.

General

It is the counterpart to the agents that the hosts and containers run themselves (node_exporter and Fluent Bit). Unlike the other containers it runs its own tailscaled, so that Prometheus can scrape the Chaotic-AUR mirrors over the Tailnet using MagicDNS names, and it listens on 10.0.5.100 on the host’s container bridge.

See Monitoring for the full picture, including how the stormwing containers are reached and how alerts are routed.

Nix expression

{
  config,
  garuda-lib,
  sources,
  ...
}:
let
  mon = garuda-lib.monitoring;

  units = [
    "grafana.service"
    "prometheus.service"
    "alertmanager.service"
    "prometheus-tailscale-exporter.service"
    "loki.service"
  ];
in
{
  imports = sources.defaultModules ++ [
    ../../modules
  ];

  # Own Tailnet identity so Prometheus scrapes mirrors/hosts directly
  # via MagicDNS instead of socat hairpins on the host.
  services.garuda-tailscale.enable = true;

  garuda.monitoring = {
    enable = true;

    domain = "grafana.garudalinux.net";
    baseDomain = "garudalinux.net";

    buildQueue.enable = true;

    grafana.enable = true;

    prometheus = {
      enable = true;
      port = mon.ports.prometheus;

      alertmanager = {
        enable = true;
        port = mon.ports.alertmanager;
        environmentFile = config.sops.templates."alertmanager-env".path;
        telegram.enable = true;
      };

      inherit (mon) smartctlTargets;

      inherit (mon) borgmaticTargets;

      nodeExporter = {
        enable = true;
        port = mon.ports.nodeExporter;
      };

      postgresExporter = {
        enable = true;
        environmentFile = config.sops.templates."postgres-exporter-env".path;
      };

      tailscaleExporter = {
        enable = true;
        port = 9813;
        environmentFile = config.sops.templates."tailscale-exporter-env".path;
      };

      cloudflareExporter = {
        enable = true;
        port = mon.ports.cloudflareExporter;
        environmentFile = config.sops.templates."cloudflare-exporter-env".path;
      };

      applicationTargets = [
        {
          name = "dovecot";
          address = mon.aerialisContainers.mail;
          port = mon.ports.dovecotMetrics;
        }
        {
          name = "postfix";
          address = mon.aerialisContainers.mail;
          port = mon.ports.postfixExporter;
        }
        {
          name = "redis";
          address = mon.aerialisContainers.chaotic-backend;
          port = mon.ports.redisExporter;
        }
      ];

      scrapeConfigs = [
        {
          job_name = "node-hosts";
          static_configs = map (t: {
            targets = [ t.target ];
            labels = {
              instance = t.name;
            };
          }) mon.nodeHostTargets;
        }
        {
          job_name = "node-aerialis-containers";
          static_configs = map (t: {
            targets = [ t.target ];
            labels = {
              inherit (t) instance;
            };
          }) mon.nodeContainerTargets;
        }
        {
          job_name = "node-stormwing-containers";
          static_configs = map (t: {
            targets = [ t.target ];
            labels = {
              inherit (t) instance;
            };
          }) mon.stormwingNodeContainerTargets;
        }
        {
          job_name = "nginx";
          static_configs = [
            {
              targets = mon.nginxTargets;
            }
          ];
        }
        {
          job_name = "cloudflared";
          static_configs = [
            {
              targets = mon.cloudflaredTargets;
            }
          ];
        }
        {
          job_name = "gitlab-runner";
          static_configs = [
            {
              targets = mon.gitlabRunnerTargets;
            }
          ];
          scrape_interval = "30s";
        }
        {
          job_name = "node-chaotic-mirrors";
          static_configs = map (m: {
            targets = [ m.target ];
            labels = {
              instance = m.name;
            };
          }) mon.chaoticMirrorTargets;
        }
        {
          job_name = "loki";
          static_configs = [
            {
              targets = [ "127.0.0.1:${toString mon.ports.loki}" ];
            }
          ];
        }
        {
          job_name = "alertmanager";
          static_configs = [
            {
              targets = [ "127.0.0.1:${toString mon.ports.alertmanager}" ];
            }
          ];
        }
      ];
    };

    loki = {
      enable = true;
      port = mon.ports.loki;
      dataDir = "/var/lib/loki";
    };

    fluent-bit = {
      enable = true;
      lokiAddress = "127.0.0.1";
      lokiPort = mon.ports.loki;
      extraSystemdUnits = units;
    };
  };

  networking.firewall = {
    allowedTCPPorts = with mon.ports; [
      grafana
      loki
      prometheus
      alertmanager
    ];
    interfaces."eth0".allowedTCPPorts = [ mon.ports.nodeExporter ];
  };

  sops.secrets."monitoring/telegram_bot_token" = { };
  sops.templates."alertmanager-env" = {
    mode = "0400";
    content = ''
      TELEGRAM_BOT_TOKEN=${config.sops.placeholder."monitoring/telegram_bot_token"}
    '';
  };

  sops.secrets."grafana/gitlab_client_secret" = {
    owner = "grafana";
  };
  sops.secrets."grafana/smtp_password" = {
    owner = "grafana";
  };
  sops.secrets."grafana/secret_key" = {
    owner = "grafana";
  };

  sops.secrets."postgres/netdata" = { };
  sops.templates."postgres-exporter-env" = {
    owner = "postgres-exporter";
    group = "postgres-exporter";
    mode = "0400";
    content = "PGPASSWORD=${config.sops.placeholder."postgres/netdata"}";
  };

  sops.secrets."tailscale/oauth_client_id" = { };
  sops.secrets."tailscale/oauth_client_secret" = { };
  sops.templates."tailscale-exporter-env" = {
    mode = "0400";
    content = ''
      TAILSCALE_TAILNET=${mon.tailnetDomain}
      TAILSCALE_OAUTH_CLIENT_ID=${config.sops.placeholder."tailscale/oauth_client_id"}
      TAILSCALE_OAUTH_CLIENT_SECRET=${config.sops.placeholder."tailscale/oauth_client_secret"}
    '';
  };

  sops.secrets."cloudflare/exporter_api_token" = { };
  sops.templates."cloudflare-exporter-env" = {
    mode = "0400";
    content = ''
      CF_API_TOKEN=${config.sops.placeholder."cloudflare/exporter_api_token"}
    '';
  };

  system.stateVersion = "26.11";
}