homelab framework module init (everything is a mess)
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled

This commit is contained in:
plasmagoat 2025-07-28 02:05:13 +02:00
parent 0347f4d325
commit bcbcc8b17b
94 changed files with 7289 additions and 436 deletions

133
modules/homelab/default.nix Normal file
View file

@ -0,0 +1,133 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.homelab;
nodeAgg = import ./lib/node-aggregation.nix {inherit lib;};
in {
imports = [
./monitoring-config.nix
./proxy-config.nix
./backup-config.nix
./motd
./services
# Global aggregation modules
(nodeAgg.mkGlobalModule "monitoring" nodeAgg.aggregators.monitoring)
# (nodeAgg.mkGlobalModule "logs" nodeAgg.aggregators.logs)
(nodeAgg.mkGlobalModule "reverseProxy" nodeAgg.aggregators.reverseProxy)
(nodeAgg.mkGlobalModule "backups" nodeAgg.aggregators.backups)
];
options.homelab = {
enable = mkEnableOption "Homelab fleet configuration";
hostname = mkOption {
type = types.str;
description = "Hostname for this system";
};
domain = mkOption {
type = types.str;
default = "lab";
description = "Base domain for the homelab";
};
externalDomain = mkOption {
type = types.str;
default = "procopius.dk";
description = "External doamin to the homelab";
};
environment = mkOption {
type = types.enum ["production" "staging" "development"];
default = "production";
description = "Environment type";
};
location = mkOption {
type = types.str;
default = "homelab";
description = "Physical location identifier";
};
tags = mkOption {
type = types.listOf types.str;
default = [];
description = "Tags for this system";
};
};
config = mkIf cfg.enable {
# Set hostname
networking.hostName = cfg.hostname;
# Export configuration for external consumption
environment.etc."homelab/config.json".text = builtins.toJSON {
inherit (cfg) hostname domain environment location tags;
monitoring = {
# Metrics endpoints (Prometheus, etc.)
metrics =
map (endpoint: {
inherit (endpoint) name host port path jobName scrapeInterval labels;
url = "http://${endpoint.host}:${toString endpoint.port}${endpoint.path}";
})
cfg.global.monitoring.allMetrics or [];
# Health check endpoints
healthChecks =
map (check: let
# Determine the host based on useExternalDomain
actualHost =
if check.useExternalDomain
then "${check.subdomain}.${cfg.externalDomain}"
else check.host;
# Build the URL
portPart =
if check.port != null
then ":${toString check.port}"
else "";
url = "${check.protocol}://${actualHost}${portPart}${check.path}";
in {
inherit (check) name protocol method interval timeout conditions alerts group labels enabled;
host = actualHost;
port = check.port;
path = check.path;
url = url;
useExternalDomain = check.useExternalDomain;
subdomain = check.subdomain;
sourceNode = cfg.hostname;
})
cfg.global.monitoring.allHealthChecks or [];
};
reverseProxy = {
entries =
map (entry: {
inherit (entry) subdomain host port path enableAuth enableSSL;
internalHost = "${cfg.hostname}:${toString entry.port}${entry.path}";
externalHost = "${entry.subdomain}.${cfg.externalDomain}";
})
cfg.global.reverseProxy.all;
};
backups = {
jobs =
map (job: {
inherit (job) name backend labels;
backupId = job._backupId;
sourceNode = job._sourceNode;
})
cfg.global.backups.all;
backends = cfg.global.backups.allBackends;
summary = {
totalJobs = length cfg.global.backups.all;
jobsByBackend = mapAttrs (backend: jobs: length jobs) cfg.global.backups.byBackend;
jobsByNode = mapAttrs (node: jobs: length jobs) cfg.global.backups.byNode;
};
};
};
};
}