Compare commits

..

No commits in common. "4826cb6a72655cb1bb3fe6378e252131751599e1" and "28c9d0136bb89ffcabb6fa297b02c5f01d4def45" have entirely different histories.

5 changed files with 483 additions and 337 deletions

View file

@ -3,12 +3,16 @@
lib,
namespace,
config,
inputs,
system,
...
}: let
inherit (lib) mkIf mkEnableOption mkOption;
inherit (lib.types) str;
cfg = config.${namespace}.services.media;
arr = ["radarr"];
in {
options.${namespace}.services.media = {
enable = mkEnableOption "Enable media services";
@ -56,75 +60,285 @@ in {
};
systemd.tmpfiles.rules = [
# "d '${cfg.path}/series' 0770 ${cfg.user} ${cfg.group} - -"
# "d '${cfg.path}/movies' 0770 ${cfg.user} ${cfg.group} - -"
# "d '${cfg.path}/music' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/qbittorrent' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/sabnzbd' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/incomplete' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/done' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/series' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/movies' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/music' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/qbittorrent' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/sabnzbd' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -"
"d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -"
"d /var/lib/radarrApplyTerraform 0755 ${cfg.user} ${cfg.group} -"
];
#=========================================================================
# Services
#=========================================================================
services = {
bazarr = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
listenPort = 2005;
};
services = let
arr-services =
arr
|> lib.imap (i: service: {
name = service;
value =
{
enable = true;
openFirewall = true;
flaresolverr = {
enable = true;
openFirewall = true;
port = 2007;
};
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
# port is harcoded in nixpkgs module
jellyfin = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
};
settings = {
auth.authenticationMethod = "External";
postgresql = {
enable = true;
};
server = {
bindaddress = "0.0.0.0";
port = 2000 + i;
};
caddy = {
enable = true;
virtualHosts = {
"jellyfin.kruining.eu".extraConfig = ''
reverse_proxy http://[::1]:8096
'';
postgres = {
host = "localhost";
port = "5432";
user = service;
maindb = service;
logdb = service;
};
};
}
// (
if service != "prowlarr"
then {
user = cfg.user;
group = cfg.group;
}
else {}
);
})
|> lib.listToAttrs;
in
arr-services
// {
bazarr = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
listenPort = 2005;
};
# port is harcoded in nixpkgs module
jellyfin = {
enable = true;
openFirewall = true;
user = cfg.user;
group = cfg.group;
};
flaresolverr = {
enable = true;
openFirewall = true;
port = 2007;
};
qbittorrent = {
enable = true;
openFirewall = true;
webuiPort = 2008;
serverConfig = {
LegalNotice.Accepted = true;
Prefecences.WebUI = {
Username = "admin";
};
};
user = cfg.user;
group = cfg.group;
};
# port is harcoded in nixpkgs module
sabnzbd = {
enable = true;
openFirewall = true;
configFile = "${cfg.path}/sabnzbd/config.ini";
user = cfg.user;
group = cfg.group;
};
postgresql = let
databases = arr |> lib.concatMap (s: [s "${s}-log"]);
in {
enable = true;
ensureDatabases = arr;
ensureUsers =
arr
|> lib.map (service: {
name = service;
ensureDBOwnership = true;
});
};
caddy = {
enable = true;
virtualHosts = {
"jellyfin.kruining.eu".extraConfig = ''
reverse_proxy http://[::1]:8096
'';
};
};
};
systemd.services.radarrApplyTerraform = let
# this is a nix package, the generated json file to be exact
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
modules = [
({
config,
lib,
...
}: {
config = {
variable = {
api_key = {
type = "string";
description = "Radarr api key";
};
};
terraform.required_providers.radarr = {
source = "devopsarr/radarr";
version = "2.2.0";
};
provider.radarr = {
url = "http://127.0.0.1:2001";
api_key = lib.tfRef "var.api_key";
};
resource = {
radarr_root_folder.local = {
path = "/var/media/movies";
};
};
};
})
];
};
in {
description = "Radarr terraform apply";
wantedBy = ["multi-user.target"];
wants = ["radarr.service"];
script = ''
#!/usr/bin/env bash
if [ "$(systemctl is-active radarr)" != "active" ]; then
echo "Radarr is not running"
exit 1
fi
# Sleep for a bit to give radarr the chance to start up
sleep 5s
# Print the path to the source for easier debugging
echo "config location: ${terraformConfiguration}"
# Copy infra code into workspace
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init
# Run the infrastructure code
# ${lib.getExe pkgs.opentofu} plan -var-file='${config.sops.templates."radarr/config.tfvars".path}'
${lib.getExe pkgs.opentofu} apply -auto-approve -var-file='${config.sops.templates."radarr/config.tfvars".path}'
'';
serviceConfig = {
Type = "oneshot";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = "/var/lib/radarrApplyTerraform";
EnvironmentFile = [
config.sops.templates."radarr/config.env".path
];
};
};
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
sops = {
secrets = {
# "qbittorrent/password" = {};
"qbittorrent/password_hash" = {};
};
templates = {
"qbittorrent/password.conf" = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["qbittorrent.service"];
path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf";
content = ''
[Preferences]
WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}"
'';
secrets = let
arrSecrets =
arr
|> lib.map (service: {
name = "${service}/apikey";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["${service}.service"];
};
})
|> lib.listToAttrs;
in
arrSecrets
// {
# "qbittorrent/password" = {};
"qbittorrent/password_hash" = {};
};
};
templates = let
apikeys =
arr
|> lib.map (service: {
name = "${service}/config.env";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["${service}.service"];
content = ''
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
'';
};
})
|> lib.listToAttrs;
tfvars =
arr
|> lib.map (service: {
name = "${service}/config.tfvars";
value = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["${service}ApplyTerraform.service"];
content = ''
api_key = "${config.sops.placeholder."${service}/apikey"}"
'';
};
})
|> lib.listToAttrs;
qbittorrent = {
"qbittorrent/password.conf" = {
owner = cfg.user;
group = cfg.group;
restartUnits = ["qbittorrent.service"];
path = "${config.services.qbittorrent.profileDir}/qBittorrent/config/password.conf";
content = ''
[Preferences]
WebUI\Password_PBKDF2="${config.sops.placeholder."qbittorrent/password_hash"}"
'';
};
};
in
apikeys // tfvars // qbittorrent;
};
};
}

View file

@ -1,183 +0,0 @@
{
config,
lib,
namespace,
...
}: let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.media.glance;
in {
options.${namespace}.services.media.glance = {
enable = mkEnableOption "Enable Glance";
};
config = mkIf cfg.enable {
services.glance = {
enable = true;
openFirewall = true;
environmentFile = config.sops.templates."glance/secrets.env".path;
settings = {
server = {
host = "0.0.0.0";
port = 2000;
};
theme = {
# Teal city predefined theme (https://github.com/glanceapp/glance/blob/main/docs/themes.md#teal-city)
background-color = "225 14 15";
primary-color = "157 47 65";
contrast-multiplier = 1.1;
};
pages = [
{
name = "Home";
columns = [
{
size = "small";
widgets = [
{
type = "calendar";
first-day-of-the-week = "monday";
}
];
}
{
size = "full";
widgets = [
{
type = "monitor";
cache = "1m";
title = "Services";
sites = [
{
title = "Zitadel";
url = "https://auth.kruining.eu";
icon = "sh:zitadel";
}
{
title = "Forgejo";
url = "https://git.amarth.cloud/chris";
icon = "sh:forgejo";
}
{
title = "Vaultwarden";
url = "https://vault.kruining.eu";
icon = "sh:vaultwarden";
}
];
}
{
type = "monitor";
cache = "1m";
title = "Observability";
sites = [
{
title = "Grafana";
url = "http://${config.networking.hostName}:${builtins.toString config.services.grafana.settings.server.http_port}";
icon = "sh:grafana";
}
{
title = "Prometheus";
url = "http://${config.networking.hostName}:${builtins.toString config.services.prometheus.port}";
icon = "sh:prometheus";
}
];
}
{
type = "monitor";
cache = "1m";
title = "Media";
sites = [
{
title = "Jellyfin";
url = "http://${config.networking.hostName}:8096";
icon = "sh:jellyfin";
}
{
title = "Radarr";
url = "http://${config.networking.hostName}:2001";
icon = "sh:radarr";
}
{
title = "Sonarr";
url = "http://${config.networking.hostName}:2002";
icon = "sh:sonarr";
}
{
title = "Lidarr";
url = "http://${config.networking.hostName}:2003";
icon = "sh:lidarr";
}
{
title = "Prowlarr";
url = "http://${config.networking.hostName}:2004";
icon = "sh:prowlarr";
}
{
title = "qBittorrent";
url = "http://${config.networking.hostName}:${builtins.toString config.services.qbittorrent.webuiPort}";
icon = "sh:qbittorrent";
}
{
title = "SABnzbd";
url = "http://${config.networking.hostName}:8080";
icon = "sh:sabnzbd";
}
];
}
{
type = "videos";
channels = [
"UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips
"UCR-DXc1voovS8nhAvccRZhg" # Jeff Geerling
"UCsBjURrPoezykLs9EqgamOA" # Fireship
"UCBJycsmduvYEL83R_U4JriQ" # Marques Brownlee
"UCHnyfMqiRRG1u-2MsSQLbXA" # Veritasium
];
}
];
}
{
size = "small";
widgets = [
{
type = "weather";
location = "Amsterdam, The Netherlands";
units = "metric";
hour-format = "24h";
}
{
type = "server-stats";
servers = [
{
type = "local";
name = "Ulmo";
}
];
}
];
}
];
}
];
};
};
sops.templates."glance/secrets.env" = {
# owner = config.services.glance.user;
# group = config.services.glance.group;
content = ''
RADARR_KEY="${config.sops.placeholder."radarr/apikey"}"
SONARR_KEY="${config.sops.placeholder."sonarr/apikey"}"
LIDARR_KEY="${config.sops.placeholder."lidarr/apikey"}"
'';
};
};
}

View file

@ -0,0 +1,161 @@
{ config, lib, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.services.media.homer;
in
{
options.${namespace}.services.media.homer = {
enable = mkEnableOption "Enable homer";
};
config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 2000 ];
services = {
homer = {
enable = true;
virtualHost = {
caddy.enable = true;
domain = "http://:2000";
};
settings = {
title = "Ulmo dashboard";
columns = 4;
connectivityCheck = true;
links = [];
services = [
{
name = "Services";
items = [
{
name = "Zitadel";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/zitadel.svg";
tag = "app";
url = "https://auth.kruining.eu";
target = "_blank";
}
{
name = "Forgejo";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/forgejo.svg";
tag = "app";
type = "Gitea";
url = "https://git.amarth.cloud";
target = "_blank";
}
{
name = "Vaultwarden";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/vaultwarden.svg";
type = "Vaultwarden";
tag = "app";
url = "https://vault.kruining.eu";
target = "_blank";
}
];
}
{
name = "Observability";
items = [
{
name = "Grafana";
type = "Grafana";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/grafana.svg";
tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.grafana.settings.server.http_port}";
target = "_blank";
}
{
name = "Prometheus";
type = "Prometheus";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prometheus.svg";
tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.prometheus.port}";
target = "_blank";
}
];
}
{
name = "Media";
items = [
{
name = "Jellyfin (Movies)";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/jellyfin.svg";
tag = "app";
type = "Emby";
url = "http://${config.networking.hostName}:8096";
apikey = "e3ceed943eeb409ba8342738db7cc1f5";
libraryType = "movies";
target = "_blank";
}
{
name = "Radarr";
type = "Radarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg";
tag = "app";
url = "http://${config.networking.hostName}:2001";
target = "_blank";
}
{
name = "Sonarr";
type = "Sonarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg";
tag = "app";
url = "http://${config.networking.hostName}:2002";
target = "_blank";
}
{
name = "Lidarr";
type = "Lidarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg";
tag = "app";
url = "http://${config.networking.hostName}:2003";
target = "_blank";
}
{
name = "Prowlarr";
type = "Prowlarr";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg";
tag = "app";
url = "http://${config.networking.hostName}:2004";
target = "_blank";
}
{
name = "qBittorrent";
type = "qBittorrent";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/qbittorrent.svg";
tag = "app";
url = "http://${config.networking.hostName}:${builtins.toString config.services.qbittorrent.webuiPort}";
target = "_blank";
}
{
name = "SABnzbd";
type = "SABnzbd";
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sabnzdb-light.svg";
tag = "app";
url = "http://${config.networking.hostName}:8080";
target = "_blank";
}
];
}
];
};
};
};
};
}

View file

@ -41,68 +41,36 @@ in {
port,
...
}: (mkIf enable {
"${service}" =
{
enable = true;
openFirewall = true;
"${service}" = {
enable = true;
openFirewall = true;
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
environmentFiles = [
config.sops.templates."${service}/config.env".path
];
settings = {
auth.authenticationMethod = "External";
settings = {
auth.authenticationMethod = "External";
server = {
bindaddress = "0.0.0.0";
port = port;
};
postgres = {
host = "localhost";
port = "5432";
user = service;
maindb = service;
logdb = service;
};
server = {
bindaddress = "0.0.0.0";
port = port;
};
}
// (lib.optionalAttrs (service != "prowlarr") {
user = service;
group = "media";
});
postgres = {
host = "localhost";
port = "5432";
user = service;
maindb = service;
logdb = service;
};
};
};
}))
|> lib.mkMerge
|> lib.mergeAttrsList
|> (set:
set
// {
qbittorrent = {
enable = true;
openFirewall = true;
webuiPort = 2008;
serverConfig = {
LegalNotice.Accepted = true;
Prefecences.WebUI = {
Username = "admin";
};
};
user = "qbittorrent";
group = "media";
};
# port is harcoded in nixpkgs module
sabnzbd = {
enable = true;
openFirewall = true;
configFile = "${cfg.path}/sabnzbd/config.ini";
user = "sabnzbd";
group = "media";
};
postgresql = {
ensureDatabases = cfg |> lib.attrNames;
ensureUsers =
@ -115,7 +83,7 @@ in {
};
});
systemd.services =
systemd =
cfg
|> lib.mapAttrsToList (service: {
enable,
@ -124,7 +92,11 @@ in {
rootFolders,
...
}: (mkIf enable {
"${service}ApplyTerraform" = let
tmpfiles.rules = [
"d /var/lib/${service}ApplyTerraform 0755 ${service} ${service} -"
];
services."${service}ApplyTerraform" = let
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
inherit system;
@ -144,17 +116,7 @@ in {
terraform.required_providers.${service} = {
source = "devopsarr/${service}";
version =
{
radarr = "2.3.3";
sonarr = "3.4.0";
prowlarr = "3.1.0";
lidarr = "1.13.0";
readarr = "2.1.0";
whisparr = "1.2.0";
}.${
service
};
version = "2.2.0";
};
provider.${service} = {
@ -163,11 +125,10 @@ in {
};
resource = {
"${service}_root_folder" = mkIf (lib.elem service ["radarr" "sonarr" "whisparr"]) (
"${service}_root_folder" =
rootFolders
|> lib.imap (i: f: lib.nameValuePair "local${toString i}" {path = f;})
|> lib.listToAttrs
);
|> lib.listToAttrs;
};
};
})
@ -179,16 +140,9 @@ in {
wantedBy = ["multi-user.target"];
wants = ["${service}.service"];
preStart = ''
install -d -m 0770 -o ${service} -g media /var/lib/${service}ApplyTerraform
${
rootFolders
|> lib.map (folder: "install -d -m 0770 -o media -g media ${folder}")
|> lib.join "\n"
}
'';
script = ''
#!/usr/bin/env bash
# Sleep for a bit to give the service a chance to start up
sleep 5s
@ -204,7 +158,7 @@ in {
cp -f ${terraformConfiguration} config.tf.json
# Initialize OpenTofu
${lib.getExe pkgs.opentofu} init -upgrade
${lib.getExe pkgs.opentofu} init
# Run the infrastructure code
${lib.getExe pkgs.opentofu} \
@ -219,7 +173,7 @@ in {
serviceConfig = {
Type = "oneshot";
User = service;
Group = "media";
Group = service;
WorkingDirectory = "/var/lib/${service}ApplyTerraform";
@ -229,33 +183,28 @@ in {
};
};
}))
|> lib.mkMerge;
|> lib.mergeAttrsList;
users =
users.users =
cfg
|> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable {
users.${service} = {
isSystemUser = true;
group = lib.mkDefault service;
extraGroups = ["media"];
};
groups.${service} = {};
"${service}".extraGroups = ["media"];
}))
|> lib.mkMerge;
|> lib.mergeAttrsList;
sops =
cfg
|> lib.mapAttrsToList (service: {enable, ...}: (mkIf enable {
secrets."${service}/apikey" = {
owner = service;
group = "media";
group = service;
restartUnits = ["${service}.service"];
};
templates = {
"${service}/config.env" = {
owner = service;
group = "media";
group = service;
restartUnits = ["${service}.service"];
content = ''
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
@ -264,7 +213,7 @@ in {
"${service}/config.tfvars" = {
owner = service;
group = "media";
group = service;
restartUnits = ["${service}.service"];
content = ''
api_key = "${config.sops.placeholder."${service}/apikey"}"
@ -272,6 +221,15 @@ in {
};
};
}))
|> lib.mkMerge;
|> lib.mergeAttrsList;
};
# cfg
# |> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable {
# # sops = {
# # };
# }))
# |> lib.mergeAttrsList
# ;
}

View file

@ -161,17 +161,13 @@
networking.ssh.enable = true;
media.enable = true;
media.glance.enable = true;
media.homer.enable = true;
media.mydia.enable = true;
media.nfs.enable = true;
media.servarr = {
radarr = {
enable = true;
port = 2001;
rootFolders = [
"/var/media/movies"
];
};
# radarr = {
# port = 2001;
# };
sonarr = {
enable = true;