Compare commits

..

27 Commits

Author SHA1 Message Date
soraefir 9a89479f66 Refactor 2026-06-04 00:30:29 +02:00
soraefir b82393272c Refactor 2026-06-03 19:24:29 +02:00
sora-ext 1cb9e9b645 Update modules/server/containers/default.nix 2026-06-03 17:40:10 +02:00
sora-ext b8735803c4 Update flake.nix 2026-06-03 17:35:15 +02:00
sora-ext 14bf297897 Update modules/shared/syscfg/server.nix 2026-06-03 17:20:36 +02:00
sora-ext 1fad610dff Update modules/server/database/default.nix 2026-06-03 17:19:28 +02:00
sora-ext 2c00901b04 Update modules/server/containers/apps/umami.nix 2026-06-03 17:19:11 +02:00
sora-ext 2c0ac0db09 Update modules/server/containers/apps/transmission.nix 2026-06-03 17:19:02 +02:00
sora-ext 6be107374e Update modules/server/containers/apps/traefik.nix 2026-06-03 17:18:52 +02:00
sora-ext 9e4d8274b5 Update modules/server/containers/apps/suwayomi.nix 2026-06-03 17:18:42 +02:00
sora-ext f54dea8a13 Update modules/server/containers/apps/selfmark.nix 2026-06-03 17:17:45 +02:00
sora-ext a6788f13a8 Update modules/server/containers/apps/searxng.nix 2026-06-03 17:17:36 +02:00
sora-ext 5b4af162b9 Update modules/server/containers/apps/openhab.nix 2026-06-03 17:17:14 +02:00
sora-ext 4e5c956f78 Update modules/server/containers/apps/nextcloud.nix 2026-06-03 17:17:02 +02:00
sora-ext 083549e3c6 Update modules/server/containers/apps/jellyfin.nix 2026-06-03 17:16:53 +02:00
sora-ext fada3c79b3 Update modules/server/containers/apps/invidious.nix 2026-06-03 17:16:42 +02:00
sora-ext ce72e4421b Update modules/server/containers/apps/influx.nix 2026-06-03 17:16:30 +02:00
sora-ext ddc5c76a35 Update modules/server/containers/apps/immich.nix 2026-06-03 17:16:18 +02:00
sora-ext d0b6718254 Update modules/server/containers/apps/handbrake.nix 2026-06-03 17:16:06 +02:00
sora-ext c1c76ab3de Update modules/server/containers/apps/gitea.nix 2026-06-03 17:15:56 +02:00
sora-ext 558874731a Update modules/server/containers/apps/frigate.nix 2026-06-03 17:15:46 +02:00
sora-ext b14135274b Update modules/server/containers/apps/freshrss.nix 2026-06-03 17:15:37 +02:00
sora-ext 5df88ac25a Update modules/server/containers/apps/etherpad.nix 2026-06-03 17:15:29 +02:00
sora-ext 2d8e0da386 Update modules/server/containers/apps/ethercalc.nix 2026-06-03 17:15:20 +02:00
sora-ext 8f87c11cb5 Update modules/server/containers/apps/calibre.nix 2026-06-03 17:15:00 +02:00
sora-ext 7b8eeb917f Update modules/server/containers/apps/authentik.nix 2026-06-03 17:14:30 +02:00
sora-ext a2043cafe1 Update modules/server/containers/apps/.template.nix 2026-06-03 17:14:05 +02:00
32 changed files with 1515 additions and 1434 deletions
+18 -15
View File
@@ -38,24 +38,27 @@
}; };
outputs = inputs: outputs = inputs:
let gen = import ./generator.nix { inherit inputs; }; let
lib = inputs.nixpkgs.lib;
gen = import ./generator.nix { inherit inputs; };
systemsDir = ./systems;
systemNames = lib.attrNames (lib.filterAttrs
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
(builtins.readDir systemsDir));
hostsByType = systemType:
lib.filter
(host: (import (systemsDir + "/${host}/cfg.nix")).syscfg.type == systemType)
systemNames;
generateHosts = systemType:
builtins.listToAttrs (map
(host: lib.nameValuePair host (gen.generate { inherit host; }))
(hostsByType systemType));
in { in {
devShells = import ./shells { inherit inputs; }; devShells = import ./shells { inherit inputs; };
nixosConfigurations = { nixosConfigurations = generateHosts "nixos";
valinor = gen.generate { host = "valinor"; }; darwinConfigurations = generateHosts "macos";
iriy = gen.generate { host = "iriy"; }; homeConfigurations = generateHosts "home";
efir = gen.generate { host = "efir"; };
avalon = gen.generate { host = "avalon"; };
ci = gen.generate { host = "ci"; };
sandbox = gen.generate { host = "sandbox"; };
gateway = gen.generate { host = "gateway"; };
};
darwinConfigurations = { asgard = gen.generate { host = "asgard"; }; };
homeConfigurations = {
yomi = gen.generate { host = "example"; };
example = gen.generate { host = "example"; };
};
}; };
# ===== Unsupported/NotImplemented ====== # ===== Unsupported/NotImplemented ======
+8 -3
View File
@@ -17,10 +17,14 @@ let
}; };
}; };
in { in {
sops = false; requires = {
db = false; secrets = [ ];
databases = [ ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.configPath}/example/"; path="${serverCfg.path.config}/example/";
mode = "0444"; mode = "0444";
}]; }];
@@ -46,4 +50,5 @@ in {
... ...
''; '';
}; };
};
} }
+13 -11
View File
@@ -16,15 +16,16 @@ let
// (if serverCfg.containers?nextcloud then { NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";} else {}); // (if serverCfg.containers?nextcloud then { NEXTCLOUD_DOMAIN = "${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}";} else {});
}; };
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.configPath}/authentik/media"; path="${serverCfg.path.config}/authentik";
owner = "1000:1000";
mode = "0755";
}{
path="${serverCfg.configPath}/authentik/templates";
owner = "1000:1000"; owner = "1000:1000";
dirs = ["media" "templates"];
mode = "0755"; mode = "0755";
}]; }];
@@ -55,8 +56,8 @@ in {
cmd = [ "server" ]; cmd = [ "server" ];
volumes = [ volumes = [
"${serverCfg.configPath}/authentik/media:/media" "${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.configPath}/authentik/templates:/templates" "${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro" "${authentikData}:/blueprints/custom:ro"
]; ];
}; };
@@ -77,8 +78,8 @@ in {
overrides = { overrides = {
cmd = [ "worker" ]; cmd = [ "worker" ];
volumes = [ volumes = [
"${serverCfg.configPath}/authentik/media:/media" "${serverCfg.path.config}/authentik/media:/media"
"${serverCfg.configPath}/authentik/templates:/templates" "${serverCfg.path.config}/authentik/templates:/templates"
"${authentikData}:/blueprints/custom:ro" "${authentikData}:/blueprints/custom:ro"
]; ];
}; };
@@ -114,4 +115,5 @@ in {
echo "Completed Authentik Setup" echo "Completed Authentik Setup"
''; '';
}; };
};
} }
+4 -13
View File
@@ -3,16 +3,7 @@ let
version = "latest"; version = "latest";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = false; runtime = {
db = false;
paths = [
{
path = "${serverCfg.configPath}/calibre-web/";
mode = "0755";
dirs = ["library" "ingest"];
}
];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
@@ -35,14 +26,14 @@ in {
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true"; "traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
}; };
overrides = { overrides = {
cmd = [ ];
volumes = [ volumes = [
"${serverCfg.configPath}/calibre-web/library/:/calibre-library" "${serverCfg.path.book}:/calibre-library"
"${serverCfg.configPath}/calibre-web/ingest/:/cwa-book-ingest" "${serverCfg.path.dlBook}:/cwa-book-ingest"
]; ];
}; };
}; };
}; };
};
# curl 'https://books.test.helcel.net/admin/ajaxconfig' \ # curl 'https://books.test.helcel.net/admin/ajaxconfig' \
# -X POST # -X POST
+4 -1
View File
@@ -3,7 +3,9 @@ let
version = "latest"; version = "latest";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; requires.secrets = [ name ];
runtime = {
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
@@ -31,4 +33,5 @@ in {
}; };
}; };
}; };
};
} }
+6 -3
View File
@@ -13,9 +13,11 @@ let
}; };
}; };
in { in {
sops = true; requires.secrets = [ name ];
runtime = {
paths = [{ paths = [{
path="${serverCfg.dataPath}/ethercalc/"; path="${serverCfg.path.data}/ethercalc/";
mode = "0666"; mode = "0666";
}]; }];
@@ -31,9 +33,10 @@ in {
}; };
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.dataPath}/ethercalc:/data" "${serverCfg.path.data}/ethercalc:/data"
]; ];
}; };
}; };
}; };
};
} }
+11 -6
View File
@@ -76,10 +76,14 @@ let
}; };
}; };
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.configPath}/etherpad/"; path="${serverCfg.path.config}/etherpad/";
mode = "0444"; mode = "0444";
}]; }];
@@ -107,7 +111,7 @@ in {
cmd = [ "--settings" "/etc/etherpad/settings.json" "--apikey" "/etc/etherpad/APIKEY.txt" ]; cmd = [ "--settings" "/etc/etherpad/settings.json" "--apikey" "/etc/etherpad/APIKEY.txt" ];
volumes = [ volumes = [
"${settings}:/etc/etherpad/settings.json" "${settings}:/etc/etherpad/settings.json"
"${serverCfg.configPath}/etherpad/APIKEY.txt:/etc/etherpad/APIKEY.txt:ro" "${serverCfg.path.config}/etherpad/APIKEY.txt:/etc/etherpad/APIKEY.txt:ro"
]; ];
}; };
}; };
@@ -117,8 +121,9 @@ in {
trigger = "server"; trigger = "server";
envFile = config.sops.secrets."ETHERPAD".path; envFile = config.sops.secrets."ETHERPAD".path;
script = pkgs.writeShellScript "setup" '' script = pkgs.writeShellScript "setup" ''
echo "$APIKEY" > ${serverCfg.configPath}/etherpad/APIKEY.txt echo "$APIKEY" > ${serverCfg.path.config}/etherpad/APIKEY.txt
chmod 444 ${serverCfg.configPath}/etherpad/APIKEY.txt chmod 444 ${serverCfg.path.config}/etherpad/APIKEY.txt
''; '';
}; };
};
} }
+9 -4
View File
@@ -3,11 +3,15 @@ let
version = "latest"; version = "latest";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [ paths = [
{ {
path = "${serverCfg.configPath}/freshrss"; path = "${serverCfg.path.config}/freshrss";
owner = "1000:1000"; owner = "1000:1000";
mode = "0755"; mode = "0755";
} }
@@ -33,7 +37,7 @@ in {
overrides = { overrides = {
environmentFiles = [ config.sops.secrets."FRESHRSS".path config.sops.secrets."CUSTOM".path ]; environmentFiles = [ config.sops.secrets."FRESHRSS".path config.sops.secrets."CUSTOM".path ];
volumes = []; volumes = ["${serverCfg.path.config}/freshrss:/var/www/FreshRSS/data"];
}; };
}; };
}; };
@@ -53,4 +57,5 @@ in {
''; '';
}; };
};
} }
+8 -7
View File
@@ -27,12 +27,12 @@ let
}; };
}; };
in { in {
sops = true; # Enabled to safeguard sensitive camera RTSP stream credentials requires.secrets = [ name ];
db = false; # Internal SQLite is used by default in Frigate
runtime = {
paths = [ paths = [
{ {
path = "${serverCfg.configPath}/frigate/"; path = "${serverCfg.path.config}/frigate/";
mode = "0755"; mode = "0755";
} }
{ {
@@ -53,7 +53,7 @@ in {
overrides = { overrides = {
cmd = [ ]; cmd = [ ];
volumes = [ volumes = [
"${serverCfg.configPath}/frigate:/config" "${serverCfg.path.config}/frigate:/config"
"/var/lib/frigate/storage:/media/frigate" "/var/lib/frigate/storage:/media/frigate"
"/dev/bus/usb:/dev/bus/usb" # Passes Google Coral USB TPU to the container "/dev/bus/usb:/dev/bus/usb" # Passes Google Coral USB TPU to the container
"/dev/dri:/dev/dri" # Passes Intel/AMD GPU for hardware video decoding "/dev/dri:/dev/dri" # Passes Intel/AMD GPU for hardware video decoding
@@ -66,12 +66,12 @@ in {
trigger = "server"; trigger = "server";
envFile = config.sops.secrets."FRIGATE_ENV".path; envFile = config.sops.secrets."FRIGATE_ENV".path;
script = pkgs.writeShellScript "setup-frigate" '' script = pkgs.writeShellScript "setup-frigate" ''
mkdir -p "${serverCfg.configPath}/frigate" mkdir -p "${serverCfg.path.config}/frigate"
mkdir -p "/var/lib/frigate/storage" mkdir -p "/var/lib/frigate/storage"
# Bootstrap a standard configuration layout if missing # Bootstrap a standard configuration layout if missing
if [ ! -f "${serverCfg.configPath}/frigate/config.yml" ]; then if [ ! -f "${serverCfg.path.config}/frigate/config.yml" ]; then
cat <<EOF > "${serverCfg.configPath}/frigate/config.yml" cat <<EOF > "${serverCfg.path.config}/frigate/config.yml"
mqtt: mqtt:
enabled: False # Set to True and define host if connecting to Home Assistant enabled: False # Set to True and define host if connecting to Home Assistant
@@ -92,4 +92,5 @@ EOF
fi fi
''; '';
}; };
};
} }
+13 -11
View File
@@ -5,15 +5,16 @@ let
LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain)); LDAP_DC_DOMAIN = "dc=ldap," + (lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." serverCfg.domain));
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.dataPath}/gitea/data"; path="${serverCfg.path.data}/gitea";
owner = "1000:1000";
mode = "0755";
}{
path="${serverCfg.dataPath}/gitea/data-runner";
owner = "1000:1000"; owner = "1000:1000";
dirs = ["data" "runner"];
mode = "0755"; mode = "0755";
}]; }];
containers = { containers = {
@@ -73,7 +74,7 @@ in {
} else {}); } else {});
extraLabels = { extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/user/login`) "; "traefik.http.routers.${containerCfg.subdomain}-login.rule" = "Host(`${containerCfg.subdomain}.${serverCfg.domain}`) && Path(`/user/login`) ";
"traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if (serverCfg.containers?authentik && false) then "authentik" else ""; "traefik.http.routers.${containerCfg.subdomain}-login.middlewares" = if (serverCfg.containers?authentik && containerCg.extra?proxyauth) then "authentik" else "";
"traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100"; "traefik.http.routers.${containerCfg.subdomain}-login.priority" = "100";
"traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure"; "traefik.http.routers.${containerCfg.subdomain}-login.entrypoints" = "web-secure";
"traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true"; "traefik.http.routers.${containerCfg.subdomain}-login.tls" = "true";
@@ -81,7 +82,7 @@ in {
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.dataPath}/gitea/data:/data" "${serverCfg.path.data}/gitea/data:/data"
]; ];
ports = [ "2222:22" ]; ports = [ "2222:22" ];
}; };
@@ -98,7 +99,7 @@ in {
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.dataPath}/gitea/data-runner:/data" "${serverCfg.path.data}/gitea/runner:/data"
"/var/run/podman/podman.sock:/var/run/docker.sock" "/var/run/podman/podman.sock:/var/run/docker.sock"
]; ];
# ports = [ "8088:8088" ]; # ports = [ "8088:8088" ];
@@ -117,7 +118,7 @@ in {
$GT admin user create --username "$DEFAULT_ADMIN_USERNAME" --password "$DEFAULT_ADMIN_PASSWORD" --email "$DEFAULT_ADMIN_EMAIL" --admin || true $GT admin user create --username "$DEFAULT_ADMIN_USERNAME" --password "$DEFAULT_ADMIN_PASSWORD" --email "$DEFAULT_ADMIN_EMAIL" --admin || true
touch ${serverCfg.dataPath}/gitea/data-runner/config.yml touch ${serverCfg.path.data}/gitea/data-runner/config.yml
RUNNER_TOKEN=$($GT actions generate-runner-token) RUNNER_TOKEN=$($GT actions generate-runner-token)
$GTR register \ $GTR register \
@@ -141,4 +142,5 @@ in {
echo "Completed Gitea Setup" echo "Completed Gitea Setup"
''; '';
}; };
};
} }
+9 -20
View File
@@ -2,22 +2,17 @@
let let
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
version = "latest"; version = "latest";
routerName = if containerCfg.subpath != null
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
else containerCfg.subdomain;
in { in {
runtime = {
paths = [{ paths = [{
path = "${serverCfg.configPath}/handbrake/config"; path = "${serverCfg.path.config}/handbrake";
mode = "0755";
} {
path = "${serverCfg.dataPath}/handbrake/";
mode = "0755"; mode = "0755";
}]; }];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath; subpath = containerCfg.subpath;
image = "ghcr.io/jlesage/handbrake:${version}"; image = "ghcr.io/jlesage/handbrake:${version}";
@@ -30,18 +25,12 @@ in {
AUTOMATED_CONVERSION_FORMAT = "mkv"; AUTOMATED_CONVERSION_FORMAT = "mkv";
AUTOMATED_CONVERSION_OUTPUT_SUBDIR = "SAME_AS_SRC"; AUTOMATED_CONVERSION_OUTPUT_SUBDIR = "SAME_AS_SRC";
}; };
extraLabels = { } // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${routerName}.middlewares" = "authentik";
} else {});
extraOptions = [
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
];
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.configPath}/handbrake/config:/config:rw" "${serverCfg.path.config}/handbrake:/config:rw"
"${serverCfg.dataPath}/handbrake/watch:/watch:rw" "${serverCfg.path.dlComplete}:/watch:rw"
"${serverCfg.dataPath}/handbrake/output:/output:rw" "${serverCfg.path.dlConverted}:/output:rw"
]; ];
}; };
}; };
@@ -51,9 +40,9 @@ in {
setup = { setup = {
trigger = "server"; trigger = "server";
script = pkgs.writeShellScript "setup" '' script = pkgs.writeShellScript "setup" ''
mkdir -p ${serverCfg.dataPath}/handbrake/{watch,output} mkdir -p ${serverCfg.path.data}/handbrake/{watch,output}
''; '';
}; };
};
} }
@@ -4,6 +4,7 @@ let
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
runtime = {
vm = { vm = {
portForward = [ 8123 ]; portForward = [ 8123 ];
cfg = {cfg,...}: { cfg = {cfg,...}: {
@@ -97,5 +98,5 @@ in {
fi fi
''; '';
}; };
};
} }
+2 -4
View File
@@ -258,9 +258,7 @@ let
];}#)];} ];}#)];}
]; ];
in { in {
sops = false; runtime = {
db = false;
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
@@ -284,5 +282,5 @@ in {
}; };
}; };
}; };
};
} }
+15 -8
View File
@@ -4,14 +4,19 @@ let
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path = "${serverCfg.configPath}/immich/cache"; path = "${serverCfg.path.config}/immich";
dirs = ["cache"];
mode = "0750"; mode = "0750";
}{ }{
path = "${serverCfg.dataPath}/immich/"; path = "${serverCfg.path.data}/immich/";
dirs = ["upload" "thumbs" "encoded-video" "backups"];
mode = "0755"; mode = "0755";
}]; }];
@@ -33,7 +38,10 @@ in {
}; };
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.dataPath}/immich:/data" "${serverCfg.path.photo}:/data/upload"
"${serverCfg.path.data}/immich/backups:/data/backups"
"${serverCfg.path.config}/immich/thumbs:/data/thumbs"
"${serverCfg.path.config}/immich/encoded-video:/data/encoded-video"
]; ];
}; };
}; };
@@ -43,7 +51,7 @@ in {
port = 3003; port = 3003;
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.configPath}/immich/cache:/cache" "${serverCfg.path.config}/immich/cache:/cache"
]; ];
}; };
}; };
@@ -57,8 +65,6 @@ in {
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS vchord CASCADE;" $PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS vchord CASCADE;"
$PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;" $PSQL -d "immich_db" -tAc "CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;"
mkdir -p ${serverCfg.dataPath}/immich/{upload,library,thumbs,encoded-video,profile,backups}
IMMICH_URL="https://${containerCfg.subdomain}.${serverCfg.domain}" IMMICH_URL="https://${containerCfg.subdomain}.${serverCfg.domain}"
until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$IMMICH_URL")" =~ (200|301|302) ]]; do until [[ "$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "$IMMICH_URL")" =~ (200|301|302) ]]; do
sleep 5 sleep 5
@@ -94,4 +100,5 @@ in {
''; '';
}; };
};
} }
+17 -14
View File
@@ -5,14 +5,19 @@ let
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path = "${serverCfg.configPath}/influxdb/"; path = "${serverCfg.path.config}/influxdb/";
owner = "1500:1500";
mode = "0700"; mode = "0700";
}{ }{
path = "${serverCfg.dataPath}/influxdb/"; path = "${serverCfg.path.data}/influxdb/";
owner = "1500:1500";
mode = "0700"; mode = "0700";
}]; }];
@@ -28,13 +33,14 @@ in {
# }; # };
# overrides = { # overrides = {
# volumes = [ # volumes = [
# "${serverCfg.dataPath}/influxdb:/db:rw" # "${serverCfg.path.data}/influxdb:/db:rw"
# "${serverCfg.configPath}/influxdb:/config:ro" # "${serverCfg.path.config}/influxdb:/config:ro"
# ]; # ];
# }; # };
# }; # };
server = builder.mkContainer { server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
image = "influxdata/influxdb3-ui:${version}"; image = "influxdata/influxdb3-ui:${version}";
port = 8080; port = 8080;
@@ -43,15 +49,12 @@ in {
SESSION_SECRET_KEY = "7b0024c13ae770000f797c201e2f210b9932a689c04d34de04379faa44e88e97"; SESSION_SECRET_KEY = "7b0024c13ae770000f797c201e2f210b9932a689c04d34de04379faa44e88e97";
DATABASE_URL = "/db/sqlite.db"; DATABASE_URL = "/db/sqlite.db";
}; };
extraOptions = [
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
];
overrides = { overrides = {
ports = [ "8080:8080" ]; ports = [ "8080:8080" ];
cmd = [ "--mode=admin" ]; cmd = [ "--mode=admin" ];
volumes = [ volumes = [
"${serverCfg.dataPath}/influxdb:/db:rw" "${serverCfg.path.data}/influxdb:/db:rw"
"${serverCfg.configPath}/influxdb/:/app-root/config:ro" "${serverCfg.path.config}/influxdb/:/app-root/config:ro"
]; ];
}; };
}; };
@@ -60,7 +63,7 @@ in {
setup = { setup = {
trigger = "server"; trigger = "server";
script = pkgs.writeShellScript "setup" '' script = pkgs.writeShellScript "setup" ''
cat > ${serverCfg.configPath}/influxdb/config.json << 'EOF' cat > ${serverCfg.path.config}/influxdb/config.json << 'EOF'
{ {
"DEFAULT_INFLUX_SERVER": "http://${builder.host}:8181", "DEFAULT_INFLUX_SERVER": "http://${builder.host}:8181",
"DEFAULT_INFLUX_DATABASE": "main", "DEFAULT_INFLUX_DATABASE": "main",
@@ -68,8 +71,8 @@ in {
"DEFAULT_SERVER_NAME": "${serverCfg.domain}" "DEFAULT_SERVER_NAME": "${serverCfg.domain}"
} }
EOF EOF
chmod -R 755 ${serverCfg.configPath}/influxdb chmod -R 755 ${serverCfg.path.config}/influxdb
''; '';
}; };
};
} }
+10 -5
View File
@@ -24,10 +24,14 @@ let
}; };
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.configPath}/invidious"; path="${serverCfg.path.config}/invidious";
mode = "0755"; mode = "0755";
}]; }];
@@ -49,7 +53,7 @@ in {
}; };
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.configPath}/invidious:/data:ro" "${serverCfg.path.config}/invidious:/data:ro"
]; ];
}; };
}; };
@@ -72,7 +76,8 @@ in {
export DB_HOST=${builder.host} export DB_HOST=${builder.host}
export INVIDIOUS_DOMAIN=${containerCfg.subdomain}.${serverCfg.domain} export INVIDIOUS_DOMAIN=${containerCfg.subdomain}.${serverCfg.domain}
${pkgs.gettext}/bin/envsubst < "${../data/invidious/config.yml}" > "${serverCfg.configPath}/invidious/config.yml" ${pkgs.gettext}/bin/envsubst < "${../data/invidious/config.yml}" > "${serverCfg.path.config}/invidious/config.yml"
''; '';
}; };
};
} }
+7 -13
View File
@@ -25,14 +25,10 @@ let
}; };
}; };
in { in {
runtime = {
paths = [ paths = [
{ {
path = "${serverCfg.dataPath}/media/"; path = "${serverCfg.path.config}/jellyfin/";
owner = "1000:1000";
mode = "0755";
}
{
path = "${serverCfg.configPath}/jellyfin/";
owner = "1000:1000"; owner = "1000:1000";
mode = "0755"; mode = "0755";
} }
@@ -40,6 +36,7 @@ in {
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
imageStream = image; imageStream = image;
port = 8096; port = 8096;
@@ -49,9 +46,6 @@ in {
JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override
JELLYFIN_ServerName = if containerCfg.extra?name then containerCfg.extra.name else "Flix"; JELLYFIN_ServerName = if containerCfg.extra?name then containerCfg.extra.name else "Flix";
}; };
extraOptions = [
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
];
overrides = { overrides = {
cmd = [ cmd = [
"--datadir" "/config/data" "--datadir" "/config/data"
@@ -60,8 +54,8 @@ in {
"--logdir" "/config/log" "--logdir" "/config/log"
]; ];
volumes = [ volumes = [
"${serverCfg.dataPath}/media:/media:ro" "${serverCfg.path.film}:/media:ro"
"${serverCfg.configPath}/jellyfin:/config" "${serverCfg.path.config}/jellyfin:/config"
]; ];
# If you have an Intel/AMD GPU for transcoding, add the device: # If you have an Intel/AMD GPU for transcoding, add the device:
devices = lib.optionals (builtins.pathExists "/dev/dri") [ "/dev/dri:/dev/dri" ]; devices = lib.optionals (builtins.pathExists "/dev/dri") [ "/dev/dri:/dev/dri" ];
@@ -169,7 +163,7 @@ in {
fi fi
''} ''}
${pkgs.sqlite}/bin/sqlite3 ${serverCfg.configPath}/jellyfin/data/data/jellyfin.db <<EOF ${pkgs.sqlite}/bin/sqlite3 ${serverCfg.path.config}/jellyfin/data/data/jellyfin.db <<EOF
INSERT OR IGNORE INTO ApiKeys (Id, AccessToken, Name, DateCreated, DateLastActivity) INSERT OR IGNORE INTO ApiKeys (Id, AccessToken, Name, DateCreated, DateLastActivity)
VALUES ( 1, "$HOMEPAGE_VAR_JELLYFIN_API", 'Home', strftime('%Y-%m-%d %H:%M:%S', 'now'), strftime('%Y-%m-%d %H:%M:%S', 'now')); VALUES ( 1, "$HOMEPAGE_VAR_JELLYFIN_API", 'Home', strftime('%Y-%m-%d %H:%M:%S', 'now'), strftime('%Y-%m-%d %H:%M:%S', 'now'));
EOF EOF
@@ -177,5 +171,5 @@ EOF
''; '';
}; };
};
} }
+19 -20
View File
@@ -3,21 +3,21 @@ let
version = "31"; version = "31";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path="${serverCfg.dataPath}/nextcloud/www"; path="${serverCfg.path.config}/nextcloud";
owner = "33:33"; owner = "33:33";
mode = "0755"; mode = "0755";
}{
path="${serverCfg.dataPath}/nextcloud/data";
owner = "33:33";
mode = "0755";
backup = true;
}]; }];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
image = "nextcloud:${version}"; image = "nextcloud:${version}";
port = 80; port = 80;
@@ -28,13 +28,14 @@ in {
POSTGRES_USER = "nextcloud_user"; POSTGRES_USER = "nextcloud_user";
POSTGRES_DB = "nextcloud_db"; POSTGRES_DB = "nextcloud_db";
AUTHENTIK_POSTGRESQL__SSLMODE = "disable"; AUTHENTIK_POSTGRESQL__SSLMODE = "disable";
"NEXTCLOUD_TRUSTED_DOMAINS " = "${containerCfg.subdomain}.${serverCfg.domain} nextcloud-server"; NEXTCLOUD_TRUSTED_DOMAINS = "${containerCfg.subdomain}.${serverCfg.domain} nextcloud-server";
"SMTP_HOST" = serverCfg.mailServer; SMTP_HOST = serverCfg.mail.server;
"SMTP_NAME" = "mail_user"; SMTP_NAME = "mail_user";
"SMTP_PASSWORD" = "mail_password"; SMTP_PASSWORD = "mail_password";
"MAIL_FROM_ADDRESS" = "${containerCfg.subdomain}@${serverCfg.domain}"; MAIL_FROM_ADDRESS = "${containerCfg.subdomain}@${serverCfg.domain}";
"MAIL_DOMAIN" = serverCfg.mailDomain; MAIL_DOMAIN = serverCfg.mail.domain;
"TRUSTED_PROXIES" = "10.10.0.0/16 192.168.0.0/16"; TRUSTED_PROXIES = "10.10.0.0/16 192.168.0.0/16";
NEXTCLOUD_DATA_DIR = "/var/www/html/data";
}; };
extraLabels = { extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = "sts_headers,${containerCfg.subdomain}-caldav"; "traefik.http.routers.${containerCfg.subdomain}.middlewares" = "sts_headers,${containerCfg.subdomain}-caldav";
@@ -44,14 +45,11 @@ in {
"traefik.http.middlewares.sts_headers.headers.stsSeconds" = "15552000"; "traefik.http.middlewares.sts_headers.headers.stsSeconds" = "15552000";
"traefik.http.middlewares.sts_headers.headers.stsIncludeSubdomains" = "true"; "traefik.http.middlewares.sts_headers.headers.stsIncludeSubdomains" = "true";
}; };
extraOptions = [
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
];
overrides = { overrides = {
ports = if containerCfg.port!=null then [ "${toString containerCfg.port}:80" ] else []; ports = if containerCfg.port!=null then [ "${toString containerCfg.port}:80" ] else [];
volumes = [ volumes = [
"${serverCfg.dataPath}/nextcloud/www:/var/www/html" "${serverCfg.path.config}/nextcloud:/var/www/html"
"${serverCfg.dataPath}/nextcloud/data:/var/www/html/data" "${serverCfg.path.cloud}:/var/www/html/data"
]; ];
}; };
}; };
@@ -197,4 +195,5 @@ in {
}; };
cron = [ "*/5 * * * * root ${pkgs.podman}/bin/podman --events-backend=none exec -u www-data nextcloud-server php -f /var/www/html/cron.php" ]; cron = [ "*/5 * * * * root ${pkgs.podman}/bin/podman --events-backend=none exec -u www-data nextcloud-server php -f /var/www/html/cron.php" ];
};
} }
+8 -8
View File
@@ -4,12 +4,11 @@ let
version = "5.1.4"; version = "5.1.4";
in { in {
sops = false; runtime = {
db = false;
paths = [ paths = [
{ path="${serverCfg.configPath}/openhab/conf"; owner="1000:1000"; mode = "0755"; } { path="${serverCfg.path.config}/openhab/conf"; owner="1000:1000"; mode = "0755"; }
{ path="${serverCfg.configPath}/openhab/userdata"; owner="1000:1000"; mode = "0755"; } { path="${serverCfg.path.config}/openhab/userdata"; owner="1000:1000"; mode = "0755"; }
{ path="${serverCfg.configPath}/openhab/addons"; owner="1000:1000"; mode = "0755"; } { path="${serverCfg.path.config}/openhab/addons"; owner="1000:1000"; mode = "0755"; }
]; ];
containers = { containers = {
@@ -31,9 +30,9 @@ in {
]; ];
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.configPath}/openhab/conf:/openhab/conf" "${serverCfg.path.config}/openhab/conf:/openhab/conf"
"${serverCfg.configPath}/openhab/userdata:/openhab/userdata" "${serverCfg.path.config}/openhab/userdata:/openhab/userdata"
"${serverCfg.configPath}/openhab/addons:/opt/openhab/addons" "${serverCfg.path.config}/openhab/addons:/opt/openhab/addons"
"/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro" "/var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro"
]; ];
}; };
@@ -74,4 +73,5 @@ in {
#Extra: AndroidTV/Jellyfin (Bind with lights + more) #Extra: AndroidTV/Jellyfin (Bind with lights + more)
''; '';
}; };
};
} }
+3 -8
View File
@@ -59,12 +59,9 @@ let
}; };
}); });
in { in {
sops = true; requires.secrets = [ name ];
# paths = [{
# path="${serverCfg.dataPath}/searxng/";
# mode = "0444";
# }];
runtime = {
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
@@ -80,13 +77,11 @@ in {
#SEARXNG_VALKEY_URL = "valkey://user:password@${builder.host}:6379/0}"; #SEARXNG_VALKEY_URL = "valkey://user:password@${builder.host}:6379/0}";
}; };
overrides = { overrides = {
cmd = [ ];
volumes = [ volumes = [
"${settings}:/etc/searxng/settings.yml" "${settings}:/etc/searxng/settings.yml"
# "/path/to/your/logo.png:/usr/local/searxng/searx/static/themes/simple/img/searxng.png
# "${serverCfg.dataPath}/searxng:/var/cache/searxng/"
]; ];
}; };
}; };
}; };
};
} }
+9 -14
View File
@@ -2,20 +2,16 @@
let let
version = "latest"; version = "latest";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
routerName = if containerCfg.subpath != null
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
else containerCfg.subdomain;
in { in {
sops = false; runtime = {
db = false;
paths = [{ paths = [{
path = "${serverCfg.configPath}/selfmark/"; path = "${serverCfg.path.config}/selfmark/";
mode = "0444"; mode = "0444";
}]; }];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
authentik = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath; subpath = containerCfg.subpath;
image = "ghcr.io/calibrain/shelfmark:${version}"; image = "ghcr.io/calibrain/shelfmark:${version}";
@@ -81,15 +77,14 @@ in {
EXT_BYPASSER_PATH = "/v1"; EXT_BYPASSER_PATH = "/v1";
EXT_BYPASSER_TIMEOUT = "60000"; EXT_BYPASSER_TIMEOUT = "60000";
}; };
extraLabels = {
} // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${routerName}.middlewares" = "authentik";
} else {});
overrides = { overrides = {
volumes = [ ]; volumes = [
"${serverCfg.path.dlComplete}:/books:rw"
"${serverCfg.path.books}:/output:rw"
"${serverCfg.path.config}/selfmark:/config:rw"
];
};
}; };
}; };
}; };
} }
+11 -17
View File
@@ -30,9 +30,10 @@ let
in in
assert containerCfg.subpath == null || throw "Error: Servarr does not support subpath."; assert containerCfg.subpath == null || throw "Error: Servarr does not support subpath.";
{ {
sops = true; requires.secrets = [ name ];
# db = [ "prowlarr" "sonarr" "radarr" ]; -> one db for each # db = [ "prowlarr" "sonarr" "radarr" ]; -> one db for each
runtime = {
paths = [ paths = [
{ path = "${serverCfg.dataPath}/media/"; mode = "0755"; } { path = "${serverCfg.dataPath}/media/"; mode = "0755"; }
{ path = "${serverCfg.configPath}/servarr/prowlarr"; mode = "0755"; } { path = "${serverCfg.configPath}/servarr/prowlarr"; mode = "0755"; }
@@ -44,6 +45,8 @@ in
containers = { containers = {
}// lib.optionalAttrs (builtins.elem "prowlarr" (containerCfg.extra.modules or defaultModules)) { }// lib.optionalAttrs (builtins.elem "prowlarr" (containerCfg.extra.modules or defaultModules)) {
prowlarr = builder.mkContainer { prowlarr = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = "prowlarr"; subpath = "prowlarr";
imageStream = images.prowlarr; imageStream = images.prowlarr;
@@ -58,17 +61,15 @@ in
}; };
extraOptions = [ extraOptions = [
"--user=0:0" "--user=0:0"
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
"--passwd-entry=root:x:0:0:root:/root:/bin/sh" "--passwd-entry=root:x:0:0:root:/root:/bin/sh"
]; ];
extraLabels = { } // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${containerCfg.subdomain}-prowlarr.middlewares" = "authentik";
} else {});
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/prowlarr:/config" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/prowlarr:/config" ];
}; };
}// lib.optionalAttrs (builtins.elem "radarr" (containerCfg.extra.modules or defaultModules)) { }// lib.optionalAttrs (builtins.elem "radarr" (containerCfg.extra.modules or defaultModules)) {
radarr = builder.mkContainer { radarr = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = "radarr"; subpath = "radarr";
imageStream = images.radarr; imageStream = images.radarr;
@@ -83,17 +84,15 @@ in
}; };
extraOptions = [ extraOptions = [
"--user=0:0" "--user=0:0"
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
"--passwd-entry=root:x:0:0:root:/root:/bin/sh" "--passwd-entry=root:x:0:0:root:/root:/bin/sh"
]; ];
extraLabels = { } // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${containerCfg.subdomain}-radarr.middlewares" = "authentik";
} else {});
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/radarr:/config" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/radarr:/config" ];
}; };
}// lib.optionalAttrs (builtins.elem "sonarr" (containerCfg.extra.modules or defaultModules)) { }// lib.optionalAttrs (builtins.elem "sonarr" (containerCfg.extra.modules or defaultModules)) {
sonarr = builder.mkContainer { sonarr = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = "sonarr"; subpath = "sonarr";
imageStream = images.sonarr; imageStream = images.sonarr;
@@ -108,17 +107,15 @@ in
}; };
extraOptions = [ extraOptions = [
"--user=0:0" "--user=0:0"
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
"--passwd-entry=root:x:0:0:root:/root:/bin/sh" "--passwd-entry=root:x:0:0:root:/root:/bin/sh"
]; ];
extraLabels = { } // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${containerCfg.subdomain}-sonarr.middlewares" = "authentik";
} else {});
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/sonarr:/config" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/sonarr:/config" ];
}; };
}// lib.optionalAttrs (builtins.elem "lidarr" (containerCfg.extra.modules or defaultModules)) { }// lib.optionalAttrs (builtins.elem "lidarr" (containerCfg.extra.modules or defaultModules)) {
lidarr = builder.mkContainer { lidarr = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = "lidarr"; subpath = "lidarr";
imageStream = images.lidarr; imageStream = images.lidarr;
@@ -133,12 +130,8 @@ in
}; };
extraOptions = [ extraOptions = [
"--user=0:0" "--user=0:0"
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
"--passwd-entry=root:x:0:0:root:/root:/bin/sh" "--passwd-entry=root:x:0:0:root:/root:/bin/sh"
]; ];
extraLabels = { } // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${containerCfg.subdomain}-lidarr.middlewares" = "authentik";
} else {});
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/lidarr:/config" ]; overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/lidarr:/config" ];
}; };
@@ -526,4 +519,5 @@ in
''; '';
}; };
};
} }
+9 -4
View File
@@ -3,10 +3,13 @@ let
version = "stable"; version = "stable";
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; requires = {
db =true; secrets = [ name ];
containers = { databases = [ name ];
};
runtime = {
containers = {
server = builder.mkContainer { server = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
image = "ghcr.io/suwayomi/suwayomi-server:${version}"; image = "ghcr.io/suwayomi/suwayomi-server:${version}";
@@ -40,9 +43,11 @@ in {
overrides = { overrides = {
volumes = [ volumes = [
"${serverCfg.path.manga}:/home/suwayomi/.local/share/Tachidesk/downloads"
# "${serverCfg.path.config}/suwayomi:/home/suwayomi/.local/share/Tachidesk"
]; ];
}; };
}; };
}; };
};
} }
+7 -5
View File
@@ -11,9 +11,11 @@ let
}; };
}; };
in { in {
sops = true; requires.secrets = [ name ];
runtime = {
paths = [{ paths = [{
path="${serverCfg.configPath}/traefik"; path="${serverCfg.path.config}/traefik";
owner = "1000:1000"; owner = "1000:1000";
mode = "0755"; mode = "0755";
}]; }];
@@ -77,11 +79,11 @@ in {
ports = [ "443:443" "80:80" ] ++ (if containerCfg.port!=null then [ "${toString containerCfg.port}:8080" ] else []); ports = [ "443:443" "80:80" ] ++ (if containerCfg.port!=null then [ "${toString containerCfg.port}:8080" ] else []);
volumes = [ volumes = [
"/var/run/podman/podman.sock:/var/run/docker.sock" "/var/run/podman/podman.sock:/var/run/docker.sock"
# "${serverCfg.configPath}/traefik/access.log:/etc/traefik/access.log" # "${serverCfg.path.config}/traefik/access.log:/etc/traefik/access.log"
"${serverCfg.configPath}/traefik:/custom" "${serverCfg.path.config}/traefik:/custom"
]; ];
}; };
}; };
}; };
};
} }
@@ -13,26 +13,17 @@ let
}; };
}; };
}; };
routerName = if containerCfg.subpath != null
then "${containerCfg.subdomain}-${lib.strings.sanitizeDerivationName containerCfg.subpath}"
else containerCfg.subdomain;
in { in {
runtime = {
paths = [{ paths = [{
path = "${serverCfg.dataPath}/transmission/complete"; path = "${serverCfg.path.config}/transmission";
owner = "1000:1000";
mode = "0755";
}{
path = "${serverCfg.dataPath}/transmission/incomplete";
owner = "1000:1000";
mode = "0755";
}{
path = "${serverCfg.configPath}/transmission/config";
owner = "1000:1000"; owner = "1000:1000";
mode = "0755"; mode = "0755";
}]; }];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
authentik = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
subpath = containerCfg.subpath; subpath = containerCfg.subpath;
imageStream = image; imageStream = image;
@@ -44,17 +35,12 @@ in {
WHITELIST = "";# 127.0.0.1,::1,10.*"; WHITELIST = "";# 127.0.0.1,::1,10.*";
# HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker"; # HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker";
}; };
extraLabels = {
} // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${routerName}.middlewares" = "authentik";
} else {});
overrides = { overrides = {
cmd = [ ];
volumes = [ volumes = [
"${serverCfg.dataPath}/transmission/complete:/downloads/complete" "${serverCfg.path.dlComplete}:/downloads/complete"
"${serverCfg.dataPath}/transmission/incomplete:/downloads/incomplete" "${serverCfg.path.dlIncomplete}:/downloads/incomplete"
"${serverCfg.configPath}/transmission/config:/config" "${serverCfg.path.config}/transmission:/config"
]; ];
}; };
}; };
@@ -66,8 +52,8 @@ in {
envFile = [ config.sops.secrets."CUSTOM".path ]; envFile = [ config.sops.secrets."CUSTOM".path ];
script = pkgs.writeShellScript "setup" '' script = pkgs.writeShellScript "setup" ''
${pkgs.gettext}/bin/envsubst < "${../data/transmission/settings.json}" > "${serverCfg.configPath}/transmission/config/settings.json" ${pkgs.gettext}/bin/envsubst < "${../data/transmission/settings.json}" > "${serverCfg.path.config}/transmission/config/settings.json"
''; '';
}; };
};
} }
+10 -10
View File
@@ -15,15 +15,21 @@ let
}; };
}; };
in { in {
sops = true; requires = {
db = true; secrets = [ name ];
databases = [ name ];
};
runtime = {
paths = [{ paths = [{
path = "${serverCfg.configPath}/umami/"; path = "${serverCfg.path.config}/umami/";
mode = "0444"; mode = "0444";
}]; }];
containers = { containers = {
server = builder.mkContainer { server = builder.mkContainer {
authentik = true;
tmpfs = true;
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
image = "${pkgs.umami.name}:${pkgs.umami.version}"; image = "${pkgs.umami.name}:${pkgs.umami.version}";
imageStream = image; imageStream = image;
@@ -39,16 +45,10 @@ in {
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0"); # DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
}; };
extraLabels = { } // ( if serverCfg.containers?authentik then {
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = if serverCfg.containers?authentik then "authentik" else "";
} else {});
extraOptions = [
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
];
overrides = { overrides = {
cmd = [ "start" ]; # Specific command for the umami binary cmd = [ "start" ]; # Specific command for the umami binary
}; };
}; };
}; };
};
} }
+49 -5
View File
@@ -1,16 +1,32 @@
{ config, lib, pkgs, serverCfg }: { config, lib, pkgs, serverCfg }:
let let
mkRouterName = { subdomain, subpath ? null }:
if subpath != null
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
else subdomain;
getOr = attrs: path: default: lib.attrByPath path default attrs;
mkTmpfsOption = size: "--tmpfs=/tmp:rw,noexec,nosuid,size=${size}";
mkAuthentikLabels =
{ subdomain
, subpath ? null
, routerName ? mkRouterName { inherit subdomain subpath; }
, middleware ? "authentik"
}:
lib.optionalAttrs (serverCfg.containers ? authentik) {
"traefik.http.routers.${routerName}.middlewares" = middleware;
};
contBuilder = contBuilder =
{ image ? null, imageStream ? null, imageFile ? null { image ? null, imageStream ? null, imageFile ? null
, secret ? null , secret ? null
, subdomain ? null, subpath?null, port ? null , subdomain ? null, subpath?null, port ? null
, authentik ? false
, tmpfs ? false
, tmpfsSize ? "512m"
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ] , extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
, overrides ? { } , overrides ? { }
}: }:
let let
routerName = if subpath != null routerName = mkRouterName { inherit subdomain subpath; };
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
else subdomain;
base = { base = {
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}" image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image; else if imageFile != null then "${imageFile.imageName}:${imageFile.imageTag}" else image;
@@ -33,11 +49,15 @@ let
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port; "traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
}) else { }) else {
"traefik.enable" = "false"; "traefik.enable" = "false";
}) // extraLabels; })
// lib.optionalAttrs authentik (mkAuthentikLabels { inherit subdomain subpath routerName; })
// extraLabels;
extraOptions = [ extraOptions = [
"--add-host=host.containers.internal:host-gateway" "--add-host=host.containers.internal:host-gateway"
] ++ extraOptions; ]
++ lib.optional tmpfs (mkTmpfsOption tmpfsSize)
++ extraOptions;
}; };
in lib.recursiveUpdate base overrides; in lib.recursiveUpdate base overrides;
vmBuilder = { name, vm }: ((import "${pkgs.path}/nixos/lib/eval-config.nix" { vmBuilder = { name, vm }: ((import "${pkgs.path}/nixos/lib/eval-config.nix" {
@@ -69,6 +89,30 @@ let
in { in {
mkContainer = contBuilder; mkContainer = contBuilder;
mkVm = vmBuilder; mkVm = vmBuilder;
mkApp = name: app:
{
inherit name;
requires = {
secrets = getOr app [ "requires" "secrets" ] [ ];
databases = getOr app [ "requires" "databases" ] [ ];
};
exports = {
authentik = {
blueprints = getOr app [ "exports" "authentik" "blueprints" ] [ ];
};
};
runtime = {
paths = getOr app [ "runtime" "paths" ] [ ];
containers = getOr app [ "runtime" "containers" ] { };
vm = getOr app [ "runtime" "vm" ] null;
cron = getOr app [ "runtime" "cron" ] [ ];
setup = {
trigger = "";
script = null;
envFile = [ ];
} // getOr app [ "runtime" "setup" ] { };
};
};
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars '' mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
mkdir -p $out mkdir -p $out
cp -r ${./data + "/${dir}"}/. $out/ cp -r ${./data + "/${dir}"}/. $out/
+36 -33
View File
@@ -2,30 +2,25 @@
let let
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
builder = import ./builder.nix { inherit config lib pkgs serverCfg; }; builder = import ./builder.nix { inherit config lib pkgs serverCfg; };
loadApp = name: containerCfg:
in{ builder.mkApp name ((import (./apps + "/${name}.nix")) {
config = lib.mkMerge [{ inherit config pkgs lib containerCfg builder name;
syscfg.server.loadedContainers = lib.mapAttrs (name: containerCfg: });
(import (./apps + "/${name}.nix")) { inherit config pkgs lib containerCfg builder name; } loadedContainers = lib.mapAttrs loadApp serverCfg.containers;
) config.syscfg.server.containers; appsList = builtins.attrValues loadedContainers;
} (lib.mkIf ( serverCfg.containers != {} ) ( concatRuntimeLists = field: lib.concatMap (app: app.runtime.${field}) appsList;
let mkNamedUnits = mkUnit: items: lib.listToAttrs (map mkUnit items);
appsList = builtins.attrValues config.syscfg.server.loadedContainers;
mergedContainers = lib.concatMapAttrs (appName: app: mergedContainers = lib.concatMapAttrs (appName: app:
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.containers lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.runtime.containers
) config.syscfg.server.loadedContainers; ) loadedContainers;
allPathConfigs = lib.concatMap (app: app.paths) appsList; allPathConfigs = map (path: {
allSetupConfigs = lib.concatMap (app: if app.setup?script then [({name = app.name; envFile="";} // app.setup)] else []) appsList; inherit path;
allCronsConfigs = lib.concatMap (app: app.cron) appsList; mode = "0755";
allVMConfigs = builtins.filter (app: app.vm != null) appsList; }) (lib.unique (builtins.attrValues serverCfg.path)) ++ concatRuntimeLists "paths";
in{ allSetupConfigs = map (app: ({ name = app.name; envFile = ""; } // app.runtime.setup)) appsList;
virtualisation.oci-containers = { allCronsConfigs = concatRuntimeLists "cron";
backend = "podman"; allVMConfigs = builtins.filter (app: app.runtime.vm != null) appsList;
containers = mergedContainers; mkPathSetup = cfg:
};
system.activationScripts.container-setup-dirs = {
deps = [ "users" "groups" ];
text = lib.concatStringsSep "\n" (map (cfg:
let let
effectiveCfg = { effectiveCfg = {
owner = "root:root"; owner = "root:root";
@@ -37,8 +32,18 @@ in{
${lib.concatMapStringsSep "\n" (dir: "${pkgs.coreutils}/bin/mkdir -p ${effectiveCfg.path}/${lib.escapeShellArg dir}") effectiveCfg.dirs} ${lib.concatMapStringsSep "\n" (dir: "${pkgs.coreutils}/bin/mkdir -p ${effectiveCfg.path}/${lib.escapeShellArg dir}") effectiveCfg.dirs}
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}" ${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}" ${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
'';
'') allPathConfigs); in {
config = lib.mkMerge [{
syscfg.server.loadedContainers = loadedContainers;
} (lib.mkIf (loadedContainers != {}) {
virtualisation.oci-containers = {
backend = "podman";
containers = mergedContainers;
};
system.activationScripts.container-setup-dirs = {
deps = [ "users" "groups" ];
text = lib.concatStringsSep "\n" (map mkPathSetup allPathConfigs);
}; };
systemd.services = { systemd.services = {
@@ -52,7 +57,7 @@ in{
startAt = "weekly"; startAt = "weekly";
}; };
} }
// lib.listToAttrs (lib.concatMap (e: [{ // mkNamedUnits (e: {
name = "${e.name}-vm"; name = "${e.name}-vm";
value = { value = {
description = "Isolated NixOS Guest VM for ${e.name}"; description = "Isolated NixOS Guest VM for ${e.name}";
@@ -69,12 +74,12 @@ in{
RestartSec = "10s"; RestartSec = "10s";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media/data/kvm"; ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media/data/kvm";
ExecStart = '' ExecStart = ''
${builder.mkVm { name = e.name; vm = e.vm; }}/bin/run-${e.name}-vm -nographic ${builder.mkVm { name = e.name; vm = e.runtime.vm; }}/bin/run-${e.name}-vm -nographic
''; '';
}; };
}; };
}]) allVMConfigs) }) allVMConfigs
// lib.listToAttrs (lib.concatMap (e: [{ // mkNamedUnits (e: {
name = "${e.name}-setup"; name = "${e.name}-setup";
value = { value = {
description = "Run ${e.name} setup"; description = "Run ${e.name} setup";
@@ -90,13 +95,11 @@ in{
User = "root"; User = "root";
}; };
}; };
}]) allSetupConfigs ); }) allSetupConfigs;
services.cron = { services.cron = {
enable = true; enable = true;
systemCronJobs = allCronsConfigs; systemCronJobs = allCronsConfigs;
}; };
})];
}))];
} }
+4 -3
View File
@@ -3,7 +3,7 @@
let let
listNames = config.syscfg.server.db; listNames = config.syscfg.server.db;
containerNames = builtins.attrNames (lib.filterAttrs (appName: app: app.db) config.syscfg.server.loadedContainers); containerNames = lib.concatMap (app: app.requires.databases) (builtins.attrValues config.syscfg.server.loadedContainers);
allApps = lib.unique (listNames ++ containerNames); allApps = lib.unique (listNames ++ containerNames);
in { in {
config = lib.mkIf ( builtins.length allApps > 0) { config = lib.mkIf ( builtins.length allApps > 0) {
@@ -47,9 +47,10 @@ in {
environment = { environment = {
INFLUXDB3_NODE_IDENTIFIER_PREFIX = "node0"; INFLUXDB3_NODE_IDENTIFIER_PREFIX = "node0";
INFLUXDB3_OBJECT_STORE = "file"; INFLUXDB3_OBJECT_STORE = "file";
INFLUXDB3_DATA_DIR = "${config.syscfg.server.dataPath}/influxdb"; INFLUXDB3_DATA_DIR = "${config.syscfg.server.path.data}/influxdb";
INFLUXDB3_DB_DIR = "${config.syscfg.server.dataPath}/influxdb"; INFLUXDB3_DB_DIR = "${config.syscfg.server.path.data}/influxdb";
INFLUXDB3_ENABLE_INTERNAL_DB = "true"; INFLUXDB3_ENABLE_INTERNAL_DB = "true";
INFLUXDB3_AUTH_TOKEN = "b27686e85a883437666f61586e084f7deb763958497739479ca48bc913ee90afd1a920332156133c89fb8674cb197ced17706074e6a42fc7ce6b2d54ac6119c9";
INFLUXDB3_INITIAL_ADMIN_TOKEN = "b27686e85a883437666f61586e084f7deb763958497739479ca48bc913ee90afd1a920332156133c89fb8674cb197ced17706074e6a42fc7ce6b2d54ac6119c9"; INFLUXDB3_INITIAL_ADMIN_TOKEN = "b27686e85a883437666f61586e084f7deb763958497739479ca48bc913ee90afd1a920332156133c89fb8674cb197ced17706074e6a42fc7ce6b2d54ac6119c9";
}; };
serviceConfig = { serviceConfig = {
+1 -1
View File
@@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
listNames = config.syscfg.server.db; listNames = config.syscfg.server.db;
containerNames = builtins.attrNames (lib.filterAttrs (appName: app: app.sops) config.syscfg.server.loadedContainers); containerNames = lib.concatMap (app: app.requires.secrets) (builtins.attrValues config.syscfg.server.loadedContainers);
allApps = lib.unique (listNames ++ containerNames); allApps = lib.unique (listNames ++ containerNames);
in{ in{
sops.secrets = { sops.secrets = {
+55 -13
View File
@@ -3,16 +3,32 @@ let
in with lib; { in with lib; {
domain = mkOption { type = types.str; }; domain = mkOption { type = types.str; };
mailDomain = mkOption { type = types.str; }; mail = {
mailServer = mkOption { type = types.str; }; domain = mkOption { type = types.str; default = null;};
server = mkOption { type = types.str; default = null;};
configPath = mkOption {
type = types.str;
default = "/media/config";
}; };
dataPath = mkOption {
type = types.str; path = mkOption {
default = "/media/data"; type = types.submodule {
freeformType = types.attrsOf types.str;
options = {
config = mkOption { type = types.str; default = "/media/config"; };
data = mkOption { type = types.str; default = "/media/data"; };
download = mkOption { type = types.str; default = "/media/data/download"; };
cloud = mkOption { type = types.str; default ="/media/media/cloud"; };
film = mkOption { type = types.str; default ="/media/media/film"; };
book = mkOption { type = types.str; default ="/media/media/book"; };
manga = mkOption { type = types.str; default ="/media/media/manga"; };
photo = mkOption { type = types.str; default ="/media/media/photo"; };
# music = mkOption { type = types.str; default ="/media/media/music"; };
dlComplete = mkOption { type = types.str; default ="/media/download/complete"; };
dlIncomplete = mkOption { type = types.str; default ="/media/download/incomplete"; };
dlConverted = mkOption { type = types.str; default ="/media/download/converted"; };
};
};
default = {};
}; };
colorScheme = mkOption { colorScheme = mkOption {
@@ -24,18 +40,44 @@ in with lib; {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
options = { options = {
name = lib.mkOption {type = lib.types.str; default = name;}; name = lib.mkOption {type = lib.types.str; default = name;};
sops = lib.mkOption {type = lib.types.bool; default = false;}; requires = {
db = lib.mkOption {type = lib.types.bool; default = false;}; secrets = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
databases = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
exports = {
authentik = {
blueprints = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
};
runtime = {
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];}; paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };}; containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };};
vm = lib.mkOption {type = lib.types.nullOr lib.types.attrs; default = null;}; vm = lib.mkOption {type = lib.types.nullOr lib.types.attrs; default = null;};
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];}; cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
setup = lib.mkOption {
setup = { type = lib.types.submodule {
options = {
trigger = lib.mkOption {type = lib.types.str; default = "";}; trigger = lib.mkOption {type = lib.types.str; default = "";};
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;}; script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
envFile = lib.mkOption {type = with lib.types; coercedTo str (x: [x]) (listOf str); default = [];}; envFile = lib.mkOption {
type = with lib.types; coercedTo str (x: [x]) (listOf str);
default = [ ];
};
};
};
default = { };
};
}; };
}; };
})); }));
-2
View File
@@ -27,8 +27,6 @@
# user = ... # user = ...
# ... # ...
# }; # };
mailDomain = "test@helcel";
mailServer = "infomaniak.ch";
containers = { containers = {
# ===== BASE ===== # ===== BASE =====