462 lines
13 KiB
Nix
462 lines
13 KiB
Nix
# modules/global-config.nix
|
|
{
|
|
config,
|
|
lib,
|
|
outputs,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.homelab.global;
|
|
|
|
# Service type definition
|
|
serviceType = types.submodule {
|
|
options = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable this service";
|
|
};
|
|
|
|
description = mkOption {
|
|
type = types.str;
|
|
description = "Human-readable description of the service";
|
|
};
|
|
|
|
category = mkOption {
|
|
type = types.enum ["monitoring" "networking" "storage" "security" "media" "development" "backup" "other"];
|
|
default = "other";
|
|
description = "Service category for organization";
|
|
};
|
|
|
|
dependencies = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "List of other homelab services this depends on";
|
|
};
|
|
|
|
ports = mkOption {
|
|
type = types.listOf types.port;
|
|
default = [];
|
|
description = "Ports this service uses";
|
|
};
|
|
|
|
tags = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Additional tags for this service";
|
|
};
|
|
|
|
priority = mkOption {
|
|
type = types.int;
|
|
default = 100;
|
|
description = "Service priority (lower numbers start first)";
|
|
};
|
|
};
|
|
};
|
|
|
|
# Type definitions
|
|
monitoringEndpointType = types.submodule {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
description = "Name of the monitoring endpoint";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
description = "Port number for the endpoint";
|
|
};
|
|
path = mkOption {
|
|
type = types.str;
|
|
default = "/metrics";
|
|
description = "Path for the metrics endpoint";
|
|
};
|
|
jobName = mkOption {
|
|
type = types.str;
|
|
description = "Prometheus job name";
|
|
};
|
|
scrapeInterval = mkOption {
|
|
type = types.str;
|
|
default = "30s";
|
|
description = "Prometheus scrape interval";
|
|
};
|
|
labels = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = {};
|
|
description = "Additional labels for this endpoint";
|
|
};
|
|
};
|
|
};
|
|
|
|
backupJobType = types.submodule {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
description = "Name of the backup job";
|
|
};
|
|
backend = mkOption {
|
|
type = types.enum ["restic" "borg" "rclone"];
|
|
description = "Backup backend to use";
|
|
};
|
|
paths = mkOption {
|
|
type = types.listOf types.str;
|
|
description = "List of paths to backup";
|
|
};
|
|
schedule = mkOption {
|
|
type = types.str;
|
|
default = "daily";
|
|
description = "Backup schedule (cron format or preset)";
|
|
};
|
|
retention = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = {
|
|
daily = "7";
|
|
weekly = "4";
|
|
monthly = "6";
|
|
yearly = "2";
|
|
};
|
|
description = "Retention policy";
|
|
};
|
|
excludePatterns = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Patterns to exclude from backup";
|
|
};
|
|
preHook = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Script to run before backup";
|
|
};
|
|
postHook = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Script to run after backup";
|
|
};
|
|
};
|
|
};
|
|
|
|
reverseProxyEntryType = types.submodule {
|
|
options = {
|
|
subdomain = mkOption {
|
|
type = types.str;
|
|
description = "Subdomain for the service";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
description = "Internal port to proxy to";
|
|
};
|
|
path = mkOption {
|
|
type = types.str;
|
|
default = "/";
|
|
description = "Path prefix for the service";
|
|
};
|
|
enableAuth = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable authentication for this service";
|
|
};
|
|
enableSSL = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Enable SSL for this service";
|
|
};
|
|
customHeaders = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = {};
|
|
description = "Custom headers to add";
|
|
};
|
|
websockets = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable websocket support";
|
|
};
|
|
};
|
|
};
|
|
|
|
# Helper functions for services
|
|
enabledServices = filterAttrs (name: service: service.enable) cfg.services;
|
|
servicesByCategory = category: filterAttrs (name: service: service.enable && service.category == category) cfg.services;
|
|
in {
|
|
imports = [
|
|
./motd
|
|
];
|
|
|
|
options.homelab.global = {
|
|
enable = mkEnableOption "Global homelab configuration";
|
|
|
|
hostname = mkOption {
|
|
type = types.str;
|
|
description = "Hostname for this system";
|
|
};
|
|
|
|
domain = mkOption {
|
|
type = types.str;
|
|
default = "procopius.dk";
|
|
description = "Base domain for 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";
|
|
};
|
|
|
|
services = mkOption {
|
|
type = types.attrsOf serviceType;
|
|
default = {};
|
|
description = "Homelab services configuration";
|
|
example = literalExpression ''
|
|
{
|
|
prometheus = {
|
|
enable = true;
|
|
description = "Metrics collection and monitoring";
|
|
category = "monitoring";
|
|
ports = [ 9090 ];
|
|
tags = [ "metrics" "alerting" ];
|
|
};
|
|
|
|
traefik = {
|
|
enable = true;
|
|
description = "Reverse proxy and load balancer";
|
|
category = "networking";
|
|
ports = [ 80 443 8080 ];
|
|
tags = [ "proxy" "loadbalancer" ];
|
|
priority = 10;
|
|
};
|
|
}
|
|
'';
|
|
};
|
|
|
|
monitoring = {
|
|
endpoints = mkOption {
|
|
type = types.listOf monitoringEndpointType;
|
|
default = [];
|
|
description = "Monitoring endpoints exposed by this system";
|
|
};
|
|
|
|
nodeExporter = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Enable node exporter";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 9100;
|
|
description = "Node exporter port";
|
|
};
|
|
};
|
|
};
|
|
|
|
backups = {
|
|
jobs = mkOption {
|
|
type = types.listOf backupJobType;
|
|
default = [];
|
|
description = "Backup jobs for this system";
|
|
};
|
|
|
|
globalExcludes = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [
|
|
"*.tmp"
|
|
"*.cache"
|
|
"*/.git"
|
|
"*/node_modules"
|
|
"*/target"
|
|
];
|
|
description = "Global exclude patterns for all backup jobs";
|
|
};
|
|
};
|
|
|
|
reverseProxy = {
|
|
entries = mkOption {
|
|
type = types.listOf reverseProxyEntryType;
|
|
default = [];
|
|
description = "Reverse proxy entries for this system";
|
|
};
|
|
};
|
|
|
|
# Helper function to add monitoring endpoint
|
|
addMonitoringEndpoint = mkOption {
|
|
type = types.functionTo (types.functionTo types.anything);
|
|
default = name: endpoint: {
|
|
homelab.global.monitoring.endpoints = [
|
|
(endpoint // {inherit name;})
|
|
];
|
|
};
|
|
description = "Helper function to add monitoring endpoints";
|
|
};
|
|
|
|
# Helper function to add backup job
|
|
addBackupJob = mkOption {
|
|
type = types.functionTo (types.functionTo types.anything);
|
|
default = name: job: {
|
|
homelab.global.backups.jobs = [
|
|
(job // {inherit name;})
|
|
];
|
|
};
|
|
description = "Helper function to add backup jobs";
|
|
};
|
|
|
|
# Helper function to add reverse proxy entry
|
|
addReverseProxyEntry = mkOption {
|
|
type = types.functionTo (types.functionTo types.anything);
|
|
default = subdomain: entry: {
|
|
homelab.global.reverseProxy.entries = [
|
|
(entry // {inherit subdomain;})
|
|
];
|
|
};
|
|
description = "Helper function to add reverse proxy entries";
|
|
};
|
|
|
|
# Helper functions
|
|
enabledServicesList = mkOption {
|
|
type = types.listOf types.str;
|
|
default = attrNames enabledServices;
|
|
description = "List of enabled service names";
|
|
readOnly = true;
|
|
};
|
|
|
|
servicesByPriority = mkOption {
|
|
type = types.listOf types.str;
|
|
default =
|
|
map (x: x.name) (sort (a: b: a.priority < b.priority)
|
|
(mapAttrsToList (name: service: service // {inherit name;}) enabledServices));
|
|
description = "Services sorted by priority";
|
|
readOnly = true;
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
# Set hostname
|
|
networking.hostName = cfg.hostname;
|
|
|
|
# Configure node exporter if enabled
|
|
services.prometheus.exporters.node = mkIf cfg.monitoring.nodeExporter.enable {
|
|
enable = true;
|
|
port = cfg.monitoring.nodeExporter.port;
|
|
enabledCollectors = [
|
|
"systemd"
|
|
"textfile"
|
|
"filesystem"
|
|
"loadavg"
|
|
"meminfo"
|
|
"netdev"
|
|
"stat"
|
|
];
|
|
};
|
|
|
|
# Automatically add node exporter to monitoring endpoints
|
|
homelab.global.monitoring.endpoints = mkIf cfg.monitoring.nodeExporter.enable [
|
|
{
|
|
name = "node-exporter";
|
|
port = cfg.monitoring.nodeExporter.port;
|
|
path = "/metrics";
|
|
jobName = "node";
|
|
labels = {
|
|
instance = cfg.hostname;
|
|
environment = cfg.environment;
|
|
location = cfg.location;
|
|
};
|
|
}
|
|
];
|
|
|
|
# Export configuration for external consumption
|
|
environment.etc."homelab/config.json".text = builtins.toJSON {
|
|
inherit (cfg) hostname domain environment location tags;
|
|
|
|
services =
|
|
mapAttrs (name: service: {
|
|
inherit (service) enable description category dependencies ports tags priority;
|
|
})
|
|
cfg.services;
|
|
|
|
enabledServices = enabledServices;
|
|
|
|
servicesByCategory = {
|
|
monitoring = servicesByCategory "monitoring";
|
|
networking = servicesByCategory "networking";
|
|
storage = servicesByCategory "storage";
|
|
security = servicesByCategory "security";
|
|
media = servicesByCategory "media";
|
|
development = servicesByCategory "development";
|
|
backup = servicesByCategory "backup";
|
|
other = servicesByCategory "other";
|
|
};
|
|
|
|
monitoring = {
|
|
endpoints =
|
|
map (endpoint: {
|
|
name = endpoint.name;
|
|
url = "http://${cfg.hostname}:${toString endpoint.port}${endpoint.path}";
|
|
port = endpoint.port;
|
|
path = endpoint.path;
|
|
jobName = endpoint.jobName;
|
|
scrapeInterval = endpoint.scrapeInterval;
|
|
labels =
|
|
endpoint.labels
|
|
// {
|
|
hostname = cfg.hostname;
|
|
environment = cfg.environment;
|
|
};
|
|
})
|
|
cfg.monitoring.endpoints;
|
|
};
|
|
|
|
backups = {
|
|
jobs = cfg.backups.jobs;
|
|
};
|
|
|
|
reverseProxy = {
|
|
entries =
|
|
map (entry: {
|
|
subdomain = entry.subdomain;
|
|
url = "http://${cfg.hostname}:${toString entry.port}";
|
|
port = entry.port;
|
|
path = entry.path;
|
|
domain = "${entry.subdomain}.${cfg.domain}";
|
|
enableAuth = entry.enableAuth;
|
|
enableSSL = entry.enableSSL;
|
|
customHeaders = entry.customHeaders;
|
|
websockets = entry.websockets;
|
|
})
|
|
cfg.reverseProxy.entries;
|
|
};
|
|
};
|
|
|
|
# Create a status command that shows service information
|
|
environment.systemPackages = [
|
|
# (pkgs.writeScriptBin "homelab-services" ''
|
|
# #!/bin/bash
|
|
# echo "🏠 Homelab Services Status"
|
|
# echo "=========================="
|
|
# echo
|
|
|
|
# ${concatStringsSep "\n" (mapAttrsToList (name: service: ''
|
|
# echo "${name}: ${service.description}"
|
|
# echo " Category: ${service.category}"
|
|
# echo " Status: $(systemctl is-active ${name} 2>/dev/null || echo "not found")"
|
|
# ${optionalString (service.ports != []) ''
|
|
# echo " Ports: ${concatStringsSep ", " (map toString service.ports)}"
|
|
# ''}
|
|
# ${optionalString (service.tags != []) ''
|
|
# echo " Tags: ${concatStringsSep ", " service.tags}"
|
|
# ''}
|
|
# echo
|
|
# '')
|
|
# enabledServices)}
|
|
# '')
|
|
];
|
|
};
|
|
}
|