feat(observability): implement plg stack and ntfy alerting

This commit is contained in:
Tibo De Peuter 2026-07-17 22:51:04 +02:00
parent fb3758dbbb
commit 53a539dd91
Signed by: tdpeuter
SSH key fingerprint: SHA256:u/h/LVoqKF1Iz02uOyxe6hcjmoZASCGV2HM0TG9ZMoU
4 changed files with 210 additions and 0 deletions

View file

@ -8,6 +8,7 @@
./gitea ./gitea
./homepage ./homepage
./jellyfin ./jellyfin
./monitoring
./plex ./plex
./solidtime ./solidtime
./speedtest ./speedtest

View file

@ -0,0 +1,158 @@
{ config, lib, pkgs, ... }:
{
sops.secrets."grafana/admin_password" = {
sopsFile = ../../../secrets/prod/monitoring.yaml;
};
sops.secrets."alertmanager/smtp_password" = {
sopsFile = ../../../secrets/prod/monitoring.yaml;
};
# 1. Loki Log Storage
services.loki = {
enable = true;
configuration = {
auth_enabled = false;
server.http_listen_port = 3100;
common.ring.instance_addr = "127.0.0.1";
common.ring.kvstore.store = "inmemory";
schema_config = {
configs = [{
from = "2020-10-24";
store = "boltdb-shipper";
object_store = "filesystem";
schema = "v11";
index = {
prefix = "index_";
period = "24h";
};
}];
};
storage_config = {
boltdb_shipper = {
active_index_directory = "/var/lib/loki/boltdb-shipper-active";
cache_location = "/var/lib/loki/boltdb-shipper-cache";
};
filesystem.directory = "/var/lib/loki/chunks";
};
};
};
# 2. Prometheus Time-Series DB
services.prometheus = {
enable = true;
port = 9090;
scrapeConfigs = [
{
job_name = "node";
scrape_interval = "15s";
# In a real setup, we would use Prometheus service discovery (e.g., file_sd_configs)
# or list all homelab IPs here. For now, we scrape localhost.
static_configs = [{
targets = [ "127.0.0.1:9100" ];
}];
}
];
# Connect Prometheus to Alertmanager
alertmanagers = [{
static_configs = [{
targets = [ "127.0.0.1:9093" ];
}];
}];
};
# 3. Alertmanager (Routing alerts to NTFY and Email)
services.prometheus.alertmanager = {
enable = true;
port = 9093;
configuration = {
global = {
smtp_smarthost = "smtp.example.com:587";
smtp_from = "alerts@depeuter.dev";
smtp_auth_username = "alerts@depeuter.dev";
smtp_auth_password_file = config.sops.secrets."alertmanager/smtp_password".path;
};
route = {
receiver = "ntfy-and-email";
group_wait = "30s";
group_interval = "5m";
repeat_interval = "4h";
group_by = [ "alertname" "instance" ];
};
receivers = [{
name = "ntfy-and-email";
email_configs = [{
to = "your-email@example.com";
# Use smarthost settings defined in global
}];
webhook_configs = [{
# Alertmanager natively supports webhooks. We send the JSON to NTFY's Prometheus endpoint.
url = "http://127.0.0.1:2586/alerts";
}];
}];
};
};
# 4. NTFY Push Notification Server
services.ntfy-sh = {
enable = true;
settings = {
base-url = "https://ntfy.lab.depeuter.dev";
listen-http = ":2586";
# You can configure auth via the CLI once the service is running, or via auth-file.
# For now, it is open locally.
};
};
# 5. Grafana Visualization
services.grafana = {
enable = true;
settings.server = {
http_port = 3000;
http_addr = "127.0.0.1";
domain = "grafana.lab.depeuter.dev";
};
settings.security.admin_password = "$__file{${config.sops.secrets."grafana/admin_password".path}}";
# Declarative Data Sources
provision = {
enable = true;
datasources.settings.datasources = [
{
name = "Prometheus";
type = "prometheus";
access = "proxy";
url = "http://127.0.0.1:9090";
isDefault = true;
}
{
name = "Loki";
type = "loki";
access = "proxy";
url = "http://127.0.0.1:3100";
}
];
};
};
# 6. Traefik Reverse Proxy for Grafana and NTFY
services.traefik.dynamicConfigOptions.http = {
routers = {
grafana = {
rule = "Host(`grafana.lab.depeuter.dev`)";
service = "grafana";
};
ntfy = {
rule = "Host(`ntfy.lab.depeuter.dev`)";
service = "ntfy";
};
};
services = {
grafana.loadBalancer.servers = [{ url = "http://127.0.0.1:3000"; }];
ntfy.loadBalancer.servers = [{ url = "http://127.0.0.1:2586"; }];
};
};
# Open firewall for Loki so agents can push logs
networking.firewall.allowedTCPPorts = [ 3100 ];
}

View file

@ -1,4 +1,8 @@
{ {
imports = [
./monitoring.nix
];
config = { config = {
homelab = { homelab = {
services.openssh.enable = true; services.openssh.enable = true;

View file

@ -0,0 +1,47 @@
{ config, lib, pkgs, ... }:
{
# Prometheus Node Exporter for hardware metrics
services.prometheus.exporters = {
node = {
enable = true;
enabledCollectors = [ "systemd" ];
port = 9100;
};
};
# Promtail to ship logs to Loki
services.promtail = {
enable = true;
configuration = {
server = {
http_listen_port = 28183;
grpc_listen_port = 0;
};
positions = {
filename = "/tmp/positions.yaml";
};
clients = [{
# Use the internal DNS name for the Loki ingress
url = "http://loki.lab.depeuter.dev/loki/api/v1/push";
}];
scrape_configs = [{
job_name = "journal";
journal = {
max_age = "12h";
labels = {
job = "systemd-journal";
host = config.networking.hostName;
};
};
relabel_configs = [{
source_labels = [ "__journal__systemd_unit" ];
target_label = "unit";
}];
}];
};
};
# Open firewall ports for node-exporter so Prometheus can scrape it
networking.firewall.allowedTCPPorts = [ 9100 ];
}