refactor: move homelab config to module

This commit is contained in:
Nickolaj Jepsen 2025-12-12 04:30:57 +01:00
parent 742a55e5a0
commit e03f3af01d
19 changed files with 123 additions and 62 deletions

View file

@ -1,83 +0,0 @@
{
config,
username,
...
}: let
user = "media";
group = "media";
mkVirtualHost = port: {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString port}";
};
locations."/api" = {
proxyPass = "http://localhost:${toString port}";
extraConfig = ''
auth_request off;
'';
};
};
in {
# for linux ISOs
users.groups."${group}" = {
members = [username];
};
users.users."${user}" = {
inherit group;
isSystemUser = true;
};
services = {
oauth2-proxy.nginx.virtualHosts = {
"radarr.nickolaj.com".allowed_groups = ["arr"];
"sonarr.nickolaj.com".allowed_groups = ["arr"];
"prowlarr.nickolaj.com".allowed_groups = ["arr"];
"sabnzbd.nickolaj.com".allowed_groups = ["arr"];
"bazarr.nickolaj.com".allowed_groups = ["arr"];
};
nginx.virtualHosts = {
"radarr.nickolaj.com" = mkVirtualHost 7878;
"sonarr.nickolaj.com" = mkVirtualHost 8989;
"prowlarr.nickolaj.com" = mkVirtualHost 9696;
"sabnzbd.nickolaj.com" = mkVirtualHost 8080;
"bazarr.nickolaj.com" = mkVirtualHost config.services.bazarr.listenPort;
};
restic.backups.homelab = {
paths = [
"/var/lib/radarr"
"/var/lib/sonarr"
"/var/lib/prowlarr"
"/var/lib/sabnzbd"
"/var/lib/bazarr"
];
exclude = [
# arrs logs and media cover
"/var/lib/*/.config/*/logs/"
"/var/lib/*/.config/*/MediaCover/"
"/var/lib/sabnzbd/Downloads/"
"/var/lib/sabnzbd/logs/"
];
};
sabnzbd = {
inherit user group;
enable = true;
};
radarr = {
inherit user group;
enable = true;
};
sonarr = {
inherit user group;
enable = true;
};
bazarr = {
inherit user group;
enable = true;
};
prowlarr.enable = true;
};
}

View file

@ -4,6 +4,7 @@
...
}: {
fireproof.dev.enable = true;
fireproof.homelab.enable = true;
boot = {
# Use grub as bootloader as it works better with mdadm

View file

@ -1,32 +0,0 @@
_: let
dataDir = "/var/lib/flame";
domain = "flame.nickolaj.com";
in {
services.restic.backups.homelab = {
paths = [dataDir];
exclude = ["/var/lib/flame/db_backups"];
};
services.nginx.virtualHosts."${domain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:5005";
};
};
virtualisation.oci-containers = {
containers = {
flame = {
autoStart = true;
image = "pawelmalak/flame:2.3.1";
volumes = [
"${dataDir}:/app/data"
];
ports = [
"127.0.0.1:5005:5005"
];
};
};
};
}

View file

@ -1,186 +0,0 @@
{
pkgs,
config,
...
}: let
mosquittoPort = 1883;
zigbee2mqttPort = 8180;
homeAssistantPort = 8123;
in {
age.secrets = {
"zigbee2mqtt-secret.yaml" = {
rekeyFile = ../../secrets/hosts/homelab/zigbee2mqtt-secret.yaml.age;
owner = "zigbee2mqtt";
group = "zigbee2mqtt";
};
mosquitto-zigbee2mqtt.rekeyFile = ../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age;
mosquitto-sas.rekeyFile = ../../secrets/hosts/homelab/mosquitto-sas.age;
mosquitto-ha.rekeyFile = ../../secrets/hosts/homelab/mosquitto-ha.age;
hassSecrets = {
rekeyFile = ../../secrets/hosts/homelab/hass.yaml.age;
path = "${config.services.home-assistant.configDir}/secrets.yaml";
mode = "400";
owner = "hass";
group = "hass";
};
};
networking.firewall.allowedTCPPorts = [
mosquittoPort
];
services = {
restic.backups.homelab = {
paths = [
config.services.zigbee2mqtt.dataDir
config.services.home-assistant.configDir
];
exclude = [
"/var/lib/zigbee2mqtt/log/"
];
};
oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"];
nginx.virtualHosts = {
"zigbee.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString zigbee2mqttPort}";
proxyWebsockets = true;
};
};
"ha.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString homeAssistantPort}";
proxyWebsockets = true;
};
};
};
home-assistant = {
enable = true;
package = pkgs.home-assistant;
customComponents = with pkgs.home-assistant-custom-components; [
adaptive_lighting
sleep_as_android
(pkgs.buildHomeAssistantComponent rec {
owner = "Sian-Lee-SA";
domain = "switch_manager";
version = "v2.0.4b";
src = pkgs.fetchFromGitHub {
inherit owner;
repo = "Home-Assistant-Switch-Manager";
rev = version;
hash = "sha256-W9xO3JjnRKHk/dlXMA6y5nEJl/KsGzPvJoumGw+nohw=";
};
})
];
extraComponents = [
"default_config"
"met"
"mqtt"
"esphome"
"google"
"spotify"
"unifi"
"upnp"
"homeassistant_hardware"
];
config = {
homeassistant = {
name = "Home";
latitude = "!secret latitude";
longitude = "!secret longitude";
elevation = "!secret elevation";
unit_system = "metric";
time_zone = "Europe/Copenhagen";
};
frontend = {
themes = "!include_dir_merge_named themes";
};
http = {
server_port = homeAssistantPort;
use_x_forwarded_for = true;
trusted_proxies = [
"127.0.0.1"
"::1"
];
# base_url = "https://ha.nickolaj.com";
};
automation = "!include automations.yaml";
script = "!include scripts.yaml";
scene = "!include scenes.yaml";
};
};
mosquitto = {
enable = true;
listeners = [
{
port = mosquittoPort;
users."zigbee2mqtt" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-zigbee2mqtt.path}";
};
users."homeassistant" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-ha.path}";
};
users."sleep_as_android" = {
acl = ["readwrite SleepAsAndroid"];
passwordFile = "${config.age.secrets.mosquitto-sas.path}";
};
}
];
};
zigbee2mqtt = {
enable = true;
settings = {
homeassistant = {
enabled = true;
};
mqtt = {
base_topic = "zigbee2mqtt";
server = "mqtt://localhost:${toString mosquittoPort}";
user = "zigbee2mqtt";
password = "!${config.age.secrets."zigbee2mqtt-secret.yaml".path} password";
};
frontend = {
enabled = true;
port = zigbee2mqttPort;
};
serial = {
port = "/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0";
adapter = "zstack";
};
advanced = {
network_key = [
233
138
136
76
51
117
128
127
74
84
33
179
116
61
79
101
];
channel = 25;
log_level = "debug";
};
};
};
};
}

View file

@ -1,35 +0,0 @@
{
config,
pkgs,
...
}: {
age.secrets.nextcloud-admin-pass = {
rekeyFile = ../../secrets/hosts/homelab/nextcloud-admin-pass.age;
owner = "nextcloud";
group = "nextcloud";
};
services = {
restic.backups.homelab.paths = [config.services.nextcloud.home];
nginx.virtualHosts.${config.services.nextcloud.hostName} = {
forceSSL = true;
enableACME = true;
};
nextcloud = {
package = pkgs.nextcloud31;
enable = true;
https = true;
database.createLocally = true;
hostName = "nextcloud.nickolaj.com";
config = {
adminpassFile = "${config.age.secrets.nextcloud-admin-pass.path}";
dbtype = "pgsql";
};
extraApps = {
inherit (config.services.nextcloud.package.packages.apps) sociallogin;
};
};
};
}

View file

@ -1,16 +0,0 @@
_: {
networking.firewall.allowedTCPPorts = [80 443];
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
recommendedBrotliSettings = true;
};
security.acme = {
acceptTerms = true;
defaults.email = "nickolaj@fireproof.website";
};
}

View file

@ -1,21 +0,0 @@
{pkgsUnstable, ...}: let
domain = "plex.nickolaj.com";
in {
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
http2 = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://localhost:32400/";
};
};
services.plex = {
enable = true;
package = pkgsUnstable.plex;
openFirewall = true;
user = "media";
group = "media";
};
}

View file

@ -1,8 +0,0 @@
{config, ...}: {
services = {
restic.backups.homelab.paths = [config.services.postgresqlBackup.location];
postgresql.enable = true;
postgresqlBackup.enable = true;
};
}

View file

@ -1,52 +0,0 @@
{
config,
hostname,
...
}: let
mkScrapeConfig = name: {
job_name = name;
static_configs = [
{
labels = {
instance = hostname;
};
targets = [
"${toString config.services.prometheus.exporters.${name}.listenAddress}:${toString config.services.prometheus.exporters.${name}.port}"
];
}
];
};
in {
age.secrets.grafana-cloud-prometheus-api-key = {
rekeyFile = ../../secrets/grafana-cloud-prometheus.age;
owner = "prometheus";
group = "prometheus";
};
services.prometheus = {
enable = true;
enableAgentMode = true;
globalConfig.scrape_interval = "1m";
remoteWrite = [
{
url = "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push";
basic_auth = {
username = "432120";
password_file = "${config.age.secrets.grafana-cloud-prometheus-api-key.path}";
};
}
];
scrapeConfigs = [
(mkScrapeConfig "node")
];
exporters.node = {
enable = true;
extraFlags = [
"--web.disable-exporter-metrics"
];
};
};
}

View file

@ -1,28 +0,0 @@
{
pkgs,
config,
...
}: {
environment.systemPackages = with pkgs; [
restic
];
age.secrets.restic-password.rekeyFile = ../../secrets/hosts/homelab/restic-password.age;
age.secrets.restic-env.rekeyFile = ../../secrets/hosts/homelab/restic-env.age;
services.restic.backups.homelab = {
repository = "b2:fireproof-backup";
timerConfig = {
OnCalendar = "daily";
Persistent = true;
};
passwordFile = "${config.age.secrets.restic-password.path}";
environmentFile = "${config.age.secrets.restic-env.path}";
pruneOpts = [
"--keep-daily 7"
"--keep-weekly 5"
"--keep-monthly 12"
"--keep-yearly 75"
];
};
}

View file

@ -1,108 +0,0 @@
{
config,
pkgsUnstable,
...
}: let
port = 9190;
rootDomain = "nickolaj.com";
zitadelDomain = "sso.${rootDomain}";
oathproxyDomain = "oauth2-proxy.${rootDomain}";
in {
age.secrets.zitadel-master = {
rekeyFile = ../../secrets/hosts/homelab/zitadel-master.age;
owner = config.services.zitadel.user;
};
age.secrets.oauth2-proxy = {
rekeyFile = ../../secrets/hosts/homelab/oauth2-proxy-keyfile.age;
owner = "oauth2-proxy";
};
services.nginx.virtualHosts."${zitadelDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
grpc_pass grpc://127.0.0.1:${toString port};
grpc_set_header Host $host:$server_port;
'';
};
};
services.nginx.virtualHosts."${oathproxyDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://127.0.0.1:4180";
};
};
services.postgresql = {
ensureDatabases = ["zitadel"];
ensureUsers = [
{
name = "zitadel";
ensureDBOwnership = true;
ensureClauses.login = true;
ensureClauses.superuser = true;
}
];
};
services.zitadel = {
enable = true;
package = pkgsUnstable.zitadel;
masterKeyFile = config.age.secrets.zitadel-master.path;
settings = {
Port = port;
Database.postgres = {
Host = "/var/run/postgresql/";
Port = 5432;
Database = "zitadel";
User = {
Username = "zitadel";
SSL.Mode = "disable";
};
Admin = {
Username = "zitadel";
SSL.Mode = "disable";
ExistingDatabase = "zitadel";
};
};
ExternalDomain = zitadelDomain;
ExternalPort = 443;
ExternalSecure = true;
};
steps.FirstInstance = {
InstanceName = "Fireproof Auth";
Org = {
Name = "Fireproof Auth";
Human = {
UserName = "nickolaj1177@gmail.com";
FirstName = "Nickolaj";
LastName = "Jepsen";
Email.Verified = true;
Password = "Password1!";
PasswordChangeRequired = true;
};
};
LoginPolicy.AllowRegister = false;
};
};
services.oauth2-proxy = {
enable = true;
provider = "oidc";
reverseProxy = true;
redirectURL = "https://${oathproxyDomain}/oauth2/callback";
validateURL = "https://${zitadelDomain}/oauth2/";
oidcIssuerUrl = "https://${zitadelDomain}:443";
keyFile = config.age.secrets.oauth2-proxy.path;
nginx.domain = oathproxyDomain;
email.domains = ["*"];
extraConfig = {
whitelist-domain = ".${rootDomain}";
cookie-domain = ".${rootDomain}";
};
};
}

View file

@ -1,30 +0,0 @@
{config, ...}: let
domain = "bitwarden.nickolaj.com";
in {
services = {
vaultwarden = {
enable = true;
config = {
DOMAIN = "https://${domain}";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
};
};
restic.backups.homelab = {
paths = ["/var/lib/vaultwarden"];
exclude = [
"/var/lib/vaultwarden/icon_cache"
"/var/lib/vaultwarden/tmp"
];
};
nginx.virtualHosts."${domain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://${toString config.services.vaultwarden.config.ROCKET_ADDRESS}:${toString config.services.vaultwarden.config.ROCKET_PORT}";
};
};
};
}