Compare commits
27 Commits
cd4c727255
...
e1a80bb7ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1a80bb7ce | ||
| 7d95ba04a9 | |||
| 9169205357 | |||
| 74721f6b09 | |||
| 668c0107f9 | |||
| 331291c54d | |||
| d10f53e485 | |||
| 82aea8268f | |||
| 050eaedca2 | |||
| be9cb270aa | |||
| a5e0e96b52 | |||
| 4366232f18 | |||
| 4398b1d888 | |||
| c4b5c47aa4 | |||
| ff64e6c231 | |||
| e7d656141a | |||
| cf3c2428fb | |||
| a2dc050b1c | |||
| 8bf332caf2 | |||
| 20d3786547 | |||
| 79422c180a | |||
| 65fc9c6df2 | |||
| a59cbd13a3 | |||
| 5f04ef7ae5 | |||
| 0aff508cda | |||
| 30df106b94 | |||
| 3abdb6d637 |
44
modules/server/containers/apps/.template.nix
Normal file
44
modules/server/containers/apps/.template.nix
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||||
|
let
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
image = pkgs.dockerTools.streamLayeredImage {
|
||||||
|
name = "EXAMPLE";
|
||||||
|
tag = "0.0.0";
|
||||||
|
contents = [ pkgs.bashInteractive ];
|
||||||
|
config = {
|
||||||
|
Entrypoint = [ "echo 1" ];
|
||||||
|
ExposedPorts = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
templateData = builder.mkData { name = "template"; dir = "template"; vars = {
|
||||||
|
_ARGUMENT = "template";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
paths = [{
|
||||||
|
path="${serverCfg.configPath}/example/";
|
||||||
|
mode = "0444";
|
||||||
|
}];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
server = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
imageStream = image;
|
||||||
|
port = 8080;
|
||||||
|
secret = name;
|
||||||
|
extraEnv = { };
|
||||||
|
overrides = {
|
||||||
|
cmd = [ ];
|
||||||
|
volumes = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
setup = {
|
||||||
|
trigger = "server";
|
||||||
|
envFile = config.sops.secrets."EXAMPLE".path;
|
||||||
|
script = pkgs.writeShellScript "setup" ''
|
||||||
|
...
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,3 +2,7 @@
|
|||||||
|
|
||||||
RSS: TTRSS / FreshRSS
|
RSS: TTRSS / FreshRSS
|
||||||
Monitoring: Telegraf + InfluxDB
|
Monitoring: Telegraf + InfluxDB
|
||||||
|
https://github.com/tarampampam/error-pages ?
|
||||||
|
|
||||||
|
- JellyFin external mkData for config (system.xml)
|
||||||
|
- Transmission Cfg and API/Token handling
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
|
db = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.configPath}/authentik/media";
|
path="${serverCfg.configPath}/authentik/media";
|
||||||
owner = "1000:1000";
|
owner = "1000:1000";
|
||||||
@@ -25,7 +27,6 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
image = "ghcr.io/goauthentik/server:${version}";
|
image = "ghcr.io/goauthentik/server:${version}";
|
||||||
port = 9000;
|
port = 9000;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
"AUTHENTIK_REDIS__HOST" = builder.host;
|
"AUTHENTIK_REDIS__HOST" = builder.host;
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ let
|
|||||||
version = "latest";
|
version = "latest";
|
||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
containers = {
|
containers = {
|
||||||
server = builder.mkContainer {
|
server = builder.mkContainer {
|
||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
image = "collabora/code:${version}";
|
image = "collabora/code:${version}";
|
||||||
port = 9980;
|
port = 9980;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.hostDomain}";
|
"aliasgroup1" = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.hostDomain}";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.dataPath}/ethercalc/";
|
path="${serverCfg.dataPath}/ethercalc/";
|
||||||
mode = "0666";
|
mode = "0666";
|
||||||
@@ -23,7 +24,6 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
imageStream = image;
|
imageStream = image;
|
||||||
port = 8080;
|
port = 8080;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
ETHERCALC_PORT = "8080";
|
ETHERCALC_PORT = "8080";
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
|
db = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.configPath}/etherpad/";
|
path="${serverCfg.configPath}/etherpad/";
|
||||||
mode = "0444";
|
mode = "0444";
|
||||||
@@ -86,7 +88,6 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
imageStream = image;
|
imageStream = image;
|
||||||
port = 8080;
|
port = 8080;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
TITLE = "Pad";
|
TITLE = "Pad";
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ let
|
|||||||
version = "latest";
|
version = "latest";
|
||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
|
db = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.dataPath}/gitea/data";
|
path="${serverCfg.dataPath}/gitea/data";
|
||||||
owner = "1000:1000";
|
owner = "1000:1000";
|
||||||
@@ -18,7 +19,6 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
image = "gitea/gitea:${version}";
|
image = "gitea/gitea:${version}";
|
||||||
port = 8080;
|
port = 8080;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
|
|
||||||
extraEnv = { # app.ini -> GITEA__<section>__<KEY> = "<VALUE>";
|
extraEnv = { # app.ini -> GITEA__<section>__<KEY> = "<VALUE>";
|
||||||
|
|||||||
3
modules/server/containers/apps/immich.nix
Normal file
3
modules/server/containers/apps/immich.nix
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{...}:{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,58 @@
|
|||||||
{...}:{
|
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||||
|
let
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
image = pkgs.dockerTools.streamLayeredImage {
|
||||||
|
name = pkgs.jellyfin.name;
|
||||||
|
tag = pkgs.jellyfin.version;
|
||||||
|
contents = [
|
||||||
|
pkgs.cacert
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
Entrypoint = [ "${pkgs.jellyfin}/bin/jellyfin" ];
|
||||||
|
ExposedPorts = { "8096/tcp" = { }; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
paths = [
|
||||||
|
{
|
||||||
|
path = "${serverCfg.dataPath}/media/";
|
||||||
|
mode = "0755";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
path = "${serverCfg.configPath}/jellyfin/";
|
||||||
|
mode = "0755";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
server = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
imageStream = image;
|
||||||
|
port = 8096;
|
||||||
|
# secret = name;
|
||||||
|
extraEnv = {
|
||||||
|
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "1";
|
||||||
|
# JELLYFIN_WEB_DIR = "${pkgs.jellyfin-web}/share/jellyfin-web";
|
||||||
|
JELLYFIN_HttpListenerHost__BindAddress= "0.0.0.0"; #we can use settings.xml override
|
||||||
|
};
|
||||||
|
extraOptions = [
|
||||||
|
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m"
|
||||||
|
];
|
||||||
|
overrides = {
|
||||||
|
cmd = [
|
||||||
|
"--datadir" "/config/data"
|
||||||
|
"--cachedir" "/config/cache"
|
||||||
|
"--configdir" "/config/config"
|
||||||
|
"--logdir" "/config/log"
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
"${serverCfg.dataPath}/movies:/media/movies:ro"
|
||||||
|
"${serverCfg.dataPath}/series:/media/series:ro"
|
||||||
|
"${serverCfg.configPath}/jellyfin:/config"
|
||||||
|
];
|
||||||
|
# If you have an Intel/AMD GPU for transcoding, add the device:
|
||||||
|
devices = lib.optionals (builtins.pathExists "/dev/dri") [ "/dev/dri:/dev/dri" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ let
|
|||||||
version = "31";
|
version = "31";
|
||||||
serverCfg = config.syscfg.server;
|
serverCfg = config.syscfg.server;
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
|
db = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.dataPath}/nextcloud/www";
|
path="${serverCfg.dataPath}/nextcloud/www";
|
||||||
owner = "33:33";
|
owner = "33:33";
|
||||||
@@ -19,7 +21,6 @@ in {
|
|||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
image = "nextcloud:${version}";
|
image = "nextcloud:${version}";
|
||||||
port = 80;
|
port = 80;
|
||||||
ip = containerCfg.ip;
|
|
||||||
secret = name;
|
secret = name;
|
||||||
extraEnv = {
|
extraEnv = {
|
||||||
REDIS_HOST = builder.host;
|
REDIS_HOST = builder.host;
|
||||||
|
|||||||
@@ -1,3 +1,92 @@
|
|||||||
{...}:{
|
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||||
|
let
|
||||||
|
version= "latest";
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
settings = pkgs.writeText"settings.yml" (pkgs.lib.generators.toYAML {}{
|
||||||
|
use_default_settings = true;
|
||||||
|
brand = {
|
||||||
|
issue_url = "";
|
||||||
|
docs_url = "";
|
||||||
|
public_instances = "";
|
||||||
|
wiki_url = "";
|
||||||
|
custom = {
|
||||||
|
links = {
|
||||||
|
"Home" = "https://${serverCfg.hostDomain}";
|
||||||
|
# "Status" = "https://status.${serverCfg.hostDomain}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
pwa_colors = {
|
||||||
|
theme_color_light = "${serverCfg.colorScheme.palette.base0C}";
|
||||||
|
background_color_light = "${serverCfg.colorScheme.palette.base07}";
|
||||||
|
theme_color_dark = "${serverCfg.colorScheme.palette.base0C}";
|
||||||
|
background_color_dark = "${serverCfg.colorScheme.palette.base02}";
|
||||||
|
theme_color_black = "${serverCfg.colorScheme.palette.base0C}";
|
||||||
|
background_color_black = "${serverCfg.colorScheme.palette.base01}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
general = {
|
||||||
|
debug = false;
|
||||||
|
instance_name = if containerCfg.extra ? instanceName then containerCfg.extra.instanceName else "SearXNG";
|
||||||
|
privacypolicy_url = false;
|
||||||
|
donation_url = false;
|
||||||
|
contact_url = false;
|
||||||
|
enable_metrics = false;
|
||||||
|
};
|
||||||
|
search = {
|
||||||
|
safe_search = 0;
|
||||||
|
autocomplete = if containerCfg.extra ? autocomplete then containerCfg.extra.autocomplete else "";
|
||||||
|
languages = [ "all" "en" "en-US" "ja" "de-CH" "fr-CH" "nb" ];
|
||||||
|
};
|
||||||
|
server = {
|
||||||
|
# secret_key = ""; SET BY ENV VAR
|
||||||
|
};
|
||||||
|
ui = {
|
||||||
|
default_locale = if containerCfg.extra ? defaultLocale then containerCfg.extra.defaultLocale else "en";
|
||||||
|
# query_in_title = "true";
|
||||||
|
#default_theme = "custom";
|
||||||
|
custom_css = "footer { display: none !important; }";
|
||||||
|
};
|
||||||
|
# categories_as_tabs = {
|
||||||
|
# general = {};
|
||||||
|
# images ={};
|
||||||
|
# videos = {};
|
||||||
|
# news = {};
|
||||||
|
# files = {};
|
||||||
|
# };
|
||||||
|
plugins = {
|
||||||
|
"searx.plugins.infinite_scroll.SXNGPlugin".active = true;
|
||||||
|
"searx.plugins.tracker_url_remover.SXNGPlugin".active = true;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in {
|
||||||
|
sops = true;
|
||||||
|
# paths = [{
|
||||||
|
# path="${serverCfg.dataPath}/searxng/";
|
||||||
|
# mode = "0444";
|
||||||
|
# }];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
server = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
image = "searxng/searxng:${version}";
|
||||||
|
port = 8080;
|
||||||
|
secret = name;
|
||||||
|
extraEnv = {
|
||||||
|
SEARXNG_BASE_URL = "https://${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||||
|
SEARXNG_PORT = "8080";
|
||||||
|
SEARXNG_BIND_ADDRESS = "[::]";
|
||||||
|
SEARXNG_PUBLIC_INSTANCE = "false";
|
||||||
|
SEARXNG_SETTINGS_PATH = "/etc/searxng/settings.yml";
|
||||||
|
#SEARXNG_VALKEY_URL = "valkey://user:password@${builder.host}:6379/0}";
|
||||||
|
};
|
||||||
|
overrides = {
|
||||||
|
cmd = [ ];
|
||||||
|
volumes = [
|
||||||
|
"${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/"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,73 @@
|
|||||||
{...}:{
|
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||||
|
let
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
|
||||||
|
mkServarrImage = appName: appPkg: binaryPath: pkgs.dockerTools.streamLayeredImage {
|
||||||
|
name = appPkg.name;
|
||||||
|
tag = appPkg.version;
|
||||||
|
contents = with pkgs; [ cacert openssl ];
|
||||||
|
config = {
|
||||||
|
Cmd = [ "${appPkg}/${binaryPath}" "-nobrowser" "-data=/config" ];
|
||||||
|
Env = [ "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
images = {
|
||||||
|
prowlarr = mkServarrImage "prowlarr" pkgs.prowlarr "bin/Prowlarr";
|
||||||
|
radarr = mkServarrImage "radarr" pkgs.radarr "bin/Radarr";
|
||||||
|
sonarr = mkServarrImage "sonarr" pkgs.sonarr "bin/Sonarr";
|
||||||
|
bazarr = mkPythonImage "bazarr" pkgs.bazarr "bin/bazarr";
|
||||||
|
lidarr = mkServarrImage "lidarr" pkgs.lidarr "bin/Lidarr";
|
||||||
|
readarr = mkServarrImage "readarr" pkgs.readarr "bin/Readarr";
|
||||||
|
};
|
||||||
|
|
||||||
|
sharedVolumes = [
|
||||||
|
"${serverCfg.mediaPath or "/mnt/media"}:/media" # Fast hardlinking requires a single shared root
|
||||||
|
"${serverCfg.configPath}/servarr:/config-root"
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
# Initialize atomic configuration structures
|
||||||
|
paths = [
|
||||||
|
{ path = "${serverCfg.configPath}/servarr/prowlarr"; mode = "0755"; }
|
||||||
|
{ path = "${serverCfg.configPath}/servarr/radarr"; mode = "0755"; }
|
||||||
|
{ path = "${serverCfg.configPath}/servarr/sonarr"; mode = "0755"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
prowlarr = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
subpath = "prowlarr";
|
||||||
|
imageStream = images.prowlarr;
|
||||||
|
port = 9696;
|
||||||
|
secret = name;
|
||||||
|
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/prowlarr:/config" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
radarr = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
subpath = "radarr";
|
||||||
|
imageStream = images.radarr;
|
||||||
|
port = 7878;
|
||||||
|
secret = name;
|
||||||
|
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/radarr:/config" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
sonarr = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain
|
||||||
|
subpath = "sonarr";
|
||||||
|
imageStream = images.sonarr;
|
||||||
|
port = 8989;
|
||||||
|
secret = name;
|
||||||
|
overrides.volumes = sharedVolumes ++ [ "${serverCfg.configPath}/servarr/sonarr:/config" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# setup = {
|
||||||
|
# trigger = "prowlarr"; # Triggers atomic environment verification on main controller
|
||||||
|
# envFile = config.sops.secrets."SERVARR".path;
|
||||||
|
# script = pkgs.writeShellScript "setup-servarr" ''
|
||||||
|
# echo "Validating multi-container path permission nodes..."
|
||||||
|
# # mkdir -p ${serverCfg.configPath}/servarr/{prowlarr,radarr,sonarr}
|
||||||
|
# '';
|
||||||
|
# };
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
sops = true;
|
||||||
paths = [{
|
paths = [{
|
||||||
path="${serverCfg.configPath}/traefik";
|
path="${serverCfg.configPath}/traefik";
|
||||||
owner = "1000:1000";
|
owner = "1000:1000";
|
||||||
@@ -21,7 +22,6 @@ in {
|
|||||||
server = builder.mkContainer {
|
server = builder.mkContainer {
|
||||||
imageStream = image;
|
imageStream = image;
|
||||||
subdomain = containerCfg.subdomain;
|
subdomain = containerCfg.subdomain;
|
||||||
ip = containerCfg.ip;
|
|
||||||
port = 8080;
|
port = 8080;
|
||||||
secret = name;
|
secret = name;
|
||||||
extraLabels = {
|
extraLabels = {
|
||||||
@@ -55,15 +55,19 @@ in {
|
|||||||
"--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
"--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
||||||
"--entrypoints.web-secure.transport.respondingtimeouts.readtimeout=0s"
|
"--entrypoints.web-secure.transport.respondingtimeouts.readtimeout=0s"
|
||||||
"--entrypoints.web-secure.proxyprotocol.trustedips=127.0.0.1/32,192.168.1.1/16,10.10.0.0/16"
|
"--entrypoints.web-secure.proxyprotocol.trustedips=127.0.0.1/32,192.168.1.1/16,10.10.0.0/16"
|
||||||
] ++ (if containerCfg.extra ? provider then [
|
] ++ (if serverCfg.containers ? umami then [
|
||||||
|
"--experimental.plugins.umami-feeder.moduleName=github.com/astappiev/traefik-umami-feeder"
|
||||||
|
"--experimental.plugins.umami-feeder.version=v1.4.1"
|
||||||
|
"--entrypoints.web-secure.http.middlewares=umami-global@docker"
|
||||||
|
] else []) ++ (if containerCfg.extra ? provider then [
|
||||||
"--certificatesresolvers.default.acme.email=acme@${serverCfg.hostDomain}"
|
"--certificatesresolvers.default.acme.email=acme@${serverCfg.hostDomain}"
|
||||||
"--certificatesresolvers.default.acme.dnschallenge=true"
|
"--certificatesresolvers.default.acme.dnschallenge=true"
|
||||||
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
|
"--certificatesresolvers.default.acme.dnschallenge.provider=${containerCfg.extra.provider}"
|
||||||
"--certificatesresolvers.default.acme.storage=/custom/acme.json"
|
"--certificatesresolvers.default.acme.storage=/custom/acme.json"
|
||||||
] else (if serverCfg.hostDomain != "localhost" then [
|
] else []) ++ (if serverCfg.hostDomain != "localhost" then [
|
||||||
"--certificatesresolvers.default.acme.httpchallenge=false"
|
"--certificatesresolvers.default.acme.httpchallenge=false"
|
||||||
"--certificatesresolvers.default.acme.tlschallenge=true"
|
"--certificatesresolvers.default.acme.tlschallenge=true"
|
||||||
] else [ ]));
|
] else []);
|
||||||
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"
|
||||||
|
|||||||
@@ -1,3 +1,57 @@
|
|||||||
{...}:{
|
{ config, containerCfg, pkgs, lib, builder, name, ... }:
|
||||||
|
let
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
image = pkgs.dockerTools.streamLayeredImage {
|
||||||
|
name = pkgs.transmission_4.name;
|
||||||
|
tag = pkgs.transmission_4.version;
|
||||||
|
contents = [ pkgs.cacert ];
|
||||||
|
config = {
|
||||||
|
Cmd = [ "${pkgs.transmission_4}/bin/transmission-daemon" "--foreground" "--config-dir" "/config" ];
|
||||||
|
ExposedPorts = {
|
||||||
|
"9091/tcp" = {};
|
||||||
|
"51413/tcp" = {}; "51413/udp" = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
paths = [{
|
||||||
|
path = "${serverCfg.dataPath}/transmission/complete";
|
||||||
|
owner = "1000:1000";
|
||||||
|
mode = "0755";
|
||||||
|
}{
|
||||||
|
path = "${serverCfg.dataPath}/transmission/incomplete";
|
||||||
|
owner = "1000:1000";
|
||||||
|
mode = "0755";
|
||||||
|
}{
|
||||||
|
path = "${serverCfg.dataPath}/transmission/config";
|
||||||
|
owner = "1000:1000";
|
||||||
|
mode = "0755";
|
||||||
|
}];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
server = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
imageStream = image;
|
||||||
|
port = 9091;
|
||||||
|
|
||||||
|
extraEnv = {
|
||||||
|
PUID = "1000";
|
||||||
|
PGID = "1000";
|
||||||
|
TZ = "Europe/Zurich";
|
||||||
|
};
|
||||||
|
extraLabels = { } // (if serverCfg.containers ? authentik then {
|
||||||
|
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = "authentik";
|
||||||
|
} else {});
|
||||||
|
|
||||||
|
overrides = {
|
||||||
|
cmd = [ ];
|
||||||
|
volumes = [
|
||||||
|
"${serverCfg.dataPath}/transmission/complete:/downloads/complete"
|
||||||
|
"${serverCfg.dataPath}/transmission/incomplete:/downloads/incomplete"
|
||||||
|
"${serverCfg.dataPath}/transmission/config:/config"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,58 @@
|
|||||||
{...}:{
|
{ config, containerCfg, pkgs, lib, builder, name,... }:
|
||||||
|
let
|
||||||
|
serverCfg = config.syscfg.server;
|
||||||
|
|
||||||
|
# Umami image built from nixpkgs
|
||||||
|
image = pkgs.dockerTools.streamLayeredImage {
|
||||||
|
name = pkgs.umami.name;
|
||||||
|
tag = pkgs.umami.version;
|
||||||
|
contents = [ pkgs.cacert ];
|
||||||
|
config = {
|
||||||
|
# Umami in nixpkgs typically provides a binary or script to start the server
|
||||||
|
Entrypoint = [ "${pkgs.umami}/bin/umami-server" ];
|
||||||
|
ExposedPorts = { "3000/tcp" = {}; };
|
||||||
|
Env = [ "NODE_ENV=production" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
sops = true;
|
||||||
|
db = true;
|
||||||
|
paths = [{
|
||||||
|
path = "${serverCfg.configPath}/umami/";
|
||||||
|
mode = "0444";
|
||||||
|
}];
|
||||||
|
|
||||||
|
containers = {
|
||||||
|
server = builder.mkContainer {
|
||||||
|
subdomain = containerCfg.subdomain;
|
||||||
|
imageStream = image;
|
||||||
|
port = 3000;
|
||||||
|
secret = name;
|
||||||
|
extraEnv = {
|
||||||
|
PORT = "3000";
|
||||||
|
# HOSTNAME = "${containerCfg.subdomain}.${serverCfg.hostDomain}";
|
||||||
|
DATABASE_TYPE = "postgresql";
|
||||||
|
REDIS_URL = "redis://${builder.host}";
|
||||||
|
CLIENT_IP_HEADER = "X-Forwarded-For";
|
||||||
|
BASE_PATH = lib.optionalString (containerCfg.subpath or null != null) "/${containerCfg.subpath}";
|
||||||
|
# DISABLE_LOGIN = "1";#(if serverCfg.containers?authentik then "1" else "0");
|
||||||
|
|
||||||
|
};
|
||||||
|
extraLabels = {
|
||||||
|
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiHost" = "http://umami-server:3000";
|
||||||
|
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiUsername" = "admin";
|
||||||
|
"traefik.http.middlewares.umami-global.plugin.umami-feeder.umamiPassword" = "umami";
|
||||||
|
"traefik.http.middlewares.umami-global.plugin.umami-feeder.createNewWebsites" = "true";
|
||||||
|
} // ( 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 = {
|
||||||
|
cmd = [ "start" ]; # Specific command for the umami binary
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,41 @@
|
|||||||
{ config, lib, pkgs, serverCfg }:
|
{ config, lib, pkgs, serverCfg }:
|
||||||
let
|
let
|
||||||
builder =
|
builder =
|
||||||
{ image ? null, imageStream ? null
|
{ image ? null, imageStream ? null, imageFile ? null
|
||||||
, secret ? null
|
, secret ? null
|
||||||
, subdomain ? null, ip ? null, port ? 0
|
, subdomain ? null, subpath?null, port ? 0
|
||||||
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
|
||||||
, overrides ? { }
|
, overrides ? { }
|
||||||
}:
|
}:
|
||||||
let base = {
|
let
|
||||||
|
routerName = if subpath != null
|
||||||
|
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
|
||||||
|
else subdomain;
|
||||||
|
base = {
|
||||||
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
image = if imageStream != null then "${imageStream.imageName}:${imageStream.imageTag}"
|
||||||
else image;
|
else image;
|
||||||
imageStream = imageStream;
|
imageStream = imageStream;
|
||||||
|
imageFile = imageFile;
|
||||||
|
|
||||||
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
environmentFiles = if secret!=null then [ config.sops.secrets."${lib.toUpper secret}".path ] else [];
|
||||||
environment = {} // extraEnv;
|
environment = {} // extraEnv;
|
||||||
|
|
||||||
labels = (if subdomain!=null then ({
|
labels = (if subdomain!=null then ({
|
||||||
"traefik.enable" = "true";
|
"traefik.enable" = "true";
|
||||||
"traefik.http.routers.${subdomain}.entrypoints" = "web-secure";
|
"traefik.http.routers.${routerName}.entrypoints" = "web-secure";
|
||||||
"traefik.http.routers.${subdomain}.rule" = "Host(`${subdomain}.${serverCfg.hostDomain}`)";
|
"traefik.http.routers.${routerName}.rule" = if subpath != null
|
||||||
"traefik.http.routers.${subdomain}.tls" = "true";
|
then "Host(`${subdomain}.${serverCfg.hostDomain}`) && PathPrefix(`/${subpath}`)"
|
||||||
|
else "Host(`${subdomain}.${serverCfg.hostDomain}`)";
|
||||||
|
"traefik.http.routers.${routerName}.tls" = "true";
|
||||||
} // lib.optionalAttrs (port!=null) {
|
} // lib.optionalAttrs (port!=null) {
|
||||||
"traefik.http.services.${subdomain}.loadbalancer.server.port" = toString port;
|
"traefik.http.services.${routerName}.loadbalancer.server.port" = toString port;
|
||||||
}) else {
|
}) else {
|
||||||
"traefik.enable" = "false";
|
"traefik.enable" = "false";
|
||||||
}) // extraLabels;
|
}) // extraLabels;
|
||||||
|
|
||||||
extraOptions = extraOptions ++ [
|
extraOptions = extraOptions ++ [
|
||||||
"--add-host=host.containers.internal:host-gateway"
|
"--add-host=host.containers.internal:host-gateway"
|
||||||
] ++ lib.optional (ip!=null) "--ip=${ip}";
|
];
|
||||||
};
|
};
|
||||||
in lib.recursiveUpdate base overrides;
|
in lib.recursiveUpdate base overrides;
|
||||||
in {
|
in {
|
||||||
|
|||||||
@@ -2,80 +2,77 @@
|
|||||||
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; };
|
||||||
enabledConfigs = lib.filterAttrs (name: c: c.enable) serverCfg.containers;
|
|
||||||
containerSetsList = lib.mapAttrsToList (name: containerCfg:
|
|
||||||
let apps = import (./apps + "/${name}.nix") {inherit config pkgs lib containerCfg builder name;};
|
|
||||||
in{
|
|
||||||
name = name;
|
|
||||||
containers = lib.mapAttrs' (cName: cValue:
|
|
||||||
lib.nameValuePair "${name}-${cName}" cValue
|
|
||||||
) apps.containers;
|
|
||||||
paths = apps.paths or [];
|
|
||||||
setup = apps.setup or null;
|
|
||||||
cron = apps.cron or [];
|
|
||||||
}
|
|
||||||
) enabledConfigs;
|
|
||||||
mergedContainers = lib.attrsets.mergeAttrsList (lib.map(e: e.containers) containerSetsList);
|
|
||||||
allPathConfigs = lib.flatten (lib.map (e: e.paths) containerSetsList);
|
|
||||||
allCronsConfigs = lib.flatten (lib.map (e: e.cron or []) containerSetsList);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
config = lib.mkIf ( enabledConfigs != {} ) {
|
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
in{
|
||||||
backend = "podman";
|
config = lib.mkMerge [{
|
||||||
containers = mergedContainers;
|
syscfg.server.loadedContainers = lib.mapAttrs (name: containerCfg:
|
||||||
};
|
(import (./apps + "/${name}.nix")) { inherit config pkgs lib containerCfg builder name; }
|
||||||
|
) config.syscfg.server.containers;
|
||||||
system.activationScripts.container-setup-dirs = {
|
} (lib.mkIf ( serverCfg.containers != {} ) (
|
||||||
deps = [ "users" "groups" ];
|
let
|
||||||
text = lib.concatStringsSep "\n" (map (cfg:
|
appsList = builtins.attrValues config.syscfg.server.loadedContainers;
|
||||||
let
|
mergedContainers = lib.concatMapAttrs (appName: app:
|
||||||
effectiveCfg = {
|
lib.mapAttrs' (cName: cCfg: lib.nameValuePair "${appName}-${cName}" cCfg) app.containers
|
||||||
owner = "root:root";
|
) config.syscfg.server.loadedContainers;
|
||||||
mode = "0400";
|
allPathConfigs = lib.concatMap (app: app.paths) appsList;
|
||||||
} // cfg;
|
allCronsConfigs = lib.concatMap (app: app.cron) appsList;
|
||||||
in ''
|
in{
|
||||||
${pkgs.coreutils}/bin/mkdir -p "${effectiveCfg.path}"
|
|
||||||
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
|
|
||||||
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
|
|
||||||
'') allPathConfigs);
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services = {
|
virtualisation.oci-containers = {
|
||||||
podman-gc = {
|
backend = "podman";
|
||||||
description = "Podman garbage collection";
|
containers = mergedContainers;
|
||||||
serviceConfig.Type = "oneshot";
|
|
||||||
script = ''
|
|
||||||
${pkgs.podman}/bin/podman container prune -f
|
|
||||||
${pkgs.podman}/bin/podman image prune -f
|
|
||||||
'';
|
|
||||||
startAt = "weekly";
|
|
||||||
};
|
};
|
||||||
} // lib.listToAttrs (lib.concatMap (containerSet:
|
|
||||||
if containerSet.setup != null then [{
|
system.activationScripts.container-setup-dirs = {
|
||||||
name = "${containerSet.name}-setup";
|
deps = [ "users" "groups" ];
|
||||||
value = {
|
text = lib.concatStringsSep "\n" (map (cfg:
|
||||||
description = "Run ${containerSet.name} setup";
|
let
|
||||||
after = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
effectiveCfg = {
|
||||||
wants = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
owner = "root:root";
|
||||||
wantedBy = [ "multi-user.target" ];
|
mode = "0400";
|
||||||
serviceConfig = {
|
} // cfg;
|
||||||
Type = "oneshot";
|
in ''
|
||||||
TimeoutStartSec = "360s";
|
${pkgs.coreutils}/bin/mkdir -p "${effectiveCfg.path}"
|
||||||
EnvironmentFile = if (containerSet.setup ? envFile) then containerSet.setup.envFile else [ ];
|
${pkgs.coreutils}/bin/chown ${effectiveCfg.owner} "${effectiveCfg.path}"
|
||||||
ExecStart = "${containerSet.setup.script}";
|
${pkgs.coreutils}/bin/chmod ${effectiveCfg.mode} "${effectiveCfg.path}"
|
||||||
RemainAfterExit = true;
|
'') allPathConfigs);
|
||||||
User = "root";
|
};
|
||||||
};
|
|
||||||
|
systemd.services = {
|
||||||
|
podman-gc = {
|
||||||
|
description = "Podman garbage collection";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = ''
|
||||||
|
${pkgs.podman}/bin/podman container prune -f
|
||||||
|
${pkgs.podman}/bin/podman image prune -f
|
||||||
|
'';
|
||||||
|
startAt = "weekly";
|
||||||
};
|
};
|
||||||
}] else []
|
} // lib.listToAttrs (lib.concatMap (containerSet:
|
||||||
) containerSetsList);
|
if containerSet.setup.script != null then [{
|
||||||
|
name = "${containerSet.name}-setup";
|
||||||
|
value = {
|
||||||
|
description = "Run ${containerSet.name} setup";
|
||||||
|
after = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
||||||
|
wants = [ "podman-${containerSet.name}-${containerSet.setup.trigger}.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
TimeoutStartSec = "360s";
|
||||||
|
EnvironmentFile = if (containerSet.setup ? envFile) then containerSet.setup.envFile else [ ];
|
||||||
|
ExecStart = "${containerSet.setup.script}";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}] else []
|
||||||
|
) appsList);
|
||||||
|
|
||||||
services.cron = {
|
services.cron = {
|
||||||
enable = true;
|
enable = true;
|
||||||
systemCronJobs = allCronsConfigs;
|
systemCronJobs = allCronsConfigs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}))];
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
|
|
||||||
{ 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.db) config.syscfg.server.loadedContainers);
|
||||||
containerNames = lib.mapAttrsToList
|
|
||||||
(name: cfg: name)
|
|
||||||
(lib.filterAttrs (name: cfg: cfg.db or false) config.syscfg.server.containers);
|
|
||||||
|
|
||||||
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) {
|
||||||
services.postgresql = {
|
services.postgresql = {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
listNames = config.syscfg.server.db;
|
listNames = config.syscfg.server.db;
|
||||||
containerNames = lib.mapAttrsToList (name: cfg: name)
|
containerNames = builtins.attrNames (lib.filterAttrs (appName: app: app.sops) config.syscfg.server.loadedContainers);
|
||||||
(lib.filterAttrs (name: cfg: ((cfg.db or false) || (cfg.sops or false))) config.syscfg.server.containers);
|
|
||||||
allApps = lib.unique (listNames ++ containerNames);
|
allApps = lib.unique (listNames ++ containerNames);
|
||||||
in{
|
in{
|
||||||
sops.secrets = {
|
sops.secrets = {
|
||||||
|
|||||||
@@ -26,3 +26,9 @@ GITEA: |
|
|||||||
GITEA__database__PASSWD=...
|
GITEA__database__PASSWD=...
|
||||||
GITEA__security__SECRET_KEY=...
|
GITEA__security__SECRET_KEY=...
|
||||||
GITEA__security__INTERNAL_TOKEN=...
|
GITEA__security__INTERNAL_TOKEN=...
|
||||||
|
SEARXNG: |
|
||||||
|
SEARXNG_SECRET=...
|
||||||
|
UMAMI: |
|
||||||
|
DB_PASSWORD=...
|
||||||
|
DATABASE_URL=postgresql://username:mypassword@localhost:5432/mydb
|
||||||
|
APP_SECRET=...
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ COLLABORA: ENC[AES256_GCM,data:cLGEziks5dyxTF1jugfpQE0l0nSkDP7MpROzCxCM94jv49sgu
|
|||||||
ETHERPAD: ENC[AES256_GCM,data:PSr06GyOgY0HDNC4Hr2XUjbNUszGlfBjxDbrrKNQOqSMSVfZj4iFIGamrS72WO0un4U7IENx0T6CTBN/ELoq7J/+W9zf879uzKWuNaAulLVtBqrUbbqA7hTJpidnveZXzdwZRvlz/bU8kWAmXyhiDb2Q42Sz3BDb6duM3PO1AgG8Ko1pi2IemCPjO3uzudeT8FAlO8NnCUxKgwIKSz8CodOXFVGk66NX4xJd4ycfdNYXvKBNlzt1+WuWsZeZzeWmF7WD2dt4wWA9fWxB90fnth6ZV5LdeXjyYnzwkFOWoyNazgqV4jBv+aXKVwX4fYvspu13cVdrak3gc698bS2N1guDss4A/sfXMbtaYPGm98xXkqz1LP7sXQzKUdZf9sAS9gtOVv2tmg==,iv:uQ0Roe+XefzMjZCF3It+U2D1MWPMT5f6CPwlz0gQ5W0=,tag:wSgp0CVr6Y6M3eqcoTy8cw==,type:str]
|
ETHERPAD: ENC[AES256_GCM,data:PSr06GyOgY0HDNC4Hr2XUjbNUszGlfBjxDbrrKNQOqSMSVfZj4iFIGamrS72WO0un4U7IENx0T6CTBN/ELoq7J/+W9zf879uzKWuNaAulLVtBqrUbbqA7hTJpidnveZXzdwZRvlz/bU8kWAmXyhiDb2Q42Sz3BDb6duM3PO1AgG8Ko1pi2IemCPjO3uzudeT8FAlO8NnCUxKgwIKSz8CodOXFVGk66NX4xJd4ycfdNYXvKBNlzt1+WuWsZeZzeWmF7WD2dt4wWA9fWxB90fnth6ZV5LdeXjyYnzwkFOWoyNazgqV4jBv+aXKVwX4fYvspu13cVdrak3gc698bS2N1guDss4A/sfXMbtaYPGm98xXkqz1LP7sXQzKUdZf9sAS9gtOVv2tmg==,iv:uQ0Roe+XefzMjZCF3It+U2D1MWPMT5f6CPwlz0gQ5W0=,tag:wSgp0CVr6Y6M3eqcoTy8cw==,type:str]
|
||||||
ETHERCALC: ENC[AES256_GCM,data:0ScnDsUNBt6wYJC4hTXn8huuTptBTDKZV4yFVQ4fuBWc6auWNWhDQlTc0ImJoK6efr2uyp3sVu3o+KlCNvUGhDOJ1you6socyTgRP0q7oLPC+Ln+bFP8gWG8v2nyEFY=,iv:YqvVjBFG/WZg1l4aMAiioOruWZ9zcTMr74DVW+1+2DQ=,tag:ePBXd4ddipJtxhFE1amfMg==,type:str]
|
ETHERCALC: ENC[AES256_GCM,data:0ScnDsUNBt6wYJC4hTXn8huuTptBTDKZV4yFVQ4fuBWc6auWNWhDQlTc0ImJoK6efr2uyp3sVu3o+KlCNvUGhDOJ1you6socyTgRP0q7oLPC+Ln+bFP8gWG8v2nyEFY=,iv:YqvVjBFG/WZg1l4aMAiioOruWZ9zcTMr74DVW+1+2DQ=,tag:ePBXd4ddipJtxhFE1amfMg==,type:str]
|
||||||
GITEA: ENC[AES256_GCM,data:TGsye52g8DVOk51o1dWfF6x3m6BFPe0MtUbOayxYejaYSZ4cDCfx02EPAhiL3FJGLfidZt7+WpjhVqJvFeCvJ1OXdYMQyuL3akPBCmEjmBgBhJvEjVtVgV3aNMS21gy3o0RG/MXDXOLpjHeaXn3G/XWKsv5bw+gg94bDirOguLC5eaFxddn5/UGFvfwXzDmkoNmc9zufGvVpUkRy8rju/TjOz4Q5GZk1gXWtJWdkTGhuVYVBDDHIQq9DBS0qXi8tP3FvY8prOi/c5ZbyyZjTOgNpWB9uU4HkHz4aUTMFIrPFwUeWmNMIdyUmarSQ7tDDdhBfIiLhb94jaMATHq+PfwwpCcmiTfBEG3OS/3SXPMWg6jhfUxvxsKNT2HQca31B9IRqlitaio3r6KEPBTYh2nWtFO9d5Is7TUWkGsOaV+0JSiNPNh5uD7kNyBxsKUKcND7JXMxW/TZOqARY64x6IwDrbcypsyMW5i+4Er1/0HCvZ7FBH1XkTul8vBGF5ZGD/tYp/m6Ld26GKYkj967eYIwlNFcOMGNue5PyJfFCq0qWbLWO9dFA9c2e9r81DCD8y/0S3Ts7X7TkXVGShm7JoMqb8dzg9i6CoNlmbPAM5GwoNV3ftQugvqEGMk5UIvf05YsgSNvq4UJbhsT4bZqpV0plnxYD5TswCGF/XQ3nwwFTudmf1OLcgYwM2NckbVui128o1Rw=,iv:vo6l0QirLIUvwLN675LYkffkXejJecvBesLJvoW/bjY=,tag:zyLyiCskF84A3QVoq5X3iw==,type:str]
|
GITEA: ENC[AES256_GCM,data:TGsye52g8DVOk51o1dWfF6x3m6BFPe0MtUbOayxYejaYSZ4cDCfx02EPAhiL3FJGLfidZt7+WpjhVqJvFeCvJ1OXdYMQyuL3akPBCmEjmBgBhJvEjVtVgV3aNMS21gy3o0RG/MXDXOLpjHeaXn3G/XWKsv5bw+gg94bDirOguLC5eaFxddn5/UGFvfwXzDmkoNmc9zufGvVpUkRy8rju/TjOz4Q5GZk1gXWtJWdkTGhuVYVBDDHIQq9DBS0qXi8tP3FvY8prOi/c5ZbyyZjTOgNpWB9uU4HkHz4aUTMFIrPFwUeWmNMIdyUmarSQ7tDDdhBfIiLhb94jaMATHq+PfwwpCcmiTfBEG3OS/3SXPMWg6jhfUxvxsKNT2HQca31B9IRqlitaio3r6KEPBTYh2nWtFO9d5Is7TUWkGsOaV+0JSiNPNh5uD7kNyBxsKUKcND7JXMxW/TZOqARY64x6IwDrbcypsyMW5i+4Er1/0HCvZ7FBH1XkTul8vBGF5ZGD/tYp/m6Ld26GKYkj967eYIwlNFcOMGNue5PyJfFCq0qWbLWO9dFA9c2e9r81DCD8y/0S3Ts7X7TkXVGShm7JoMqb8dzg9i6CoNlmbPAM5GwoNV3ftQugvqEGMk5UIvf05YsgSNvq4UJbhsT4bZqpV0plnxYD5TswCGF/XQ3nwwFTudmf1OLcgYwM2NckbVui128o1Rw=,iv:vo6l0QirLIUvwLN675LYkffkXejJecvBesLJvoW/bjY=,tag:zyLyiCskF84A3QVoq5X3iw==,type:str]
|
||||||
|
SEARXNG: ENC[AES256_GCM,data:gtKhEmMemzLRl4c3cYhMAQ+5vUth1IhWQeLvW1YtaG5TbhQHBR4PDREQOlGt+tlfGQrft+FeNhMSN/SKOp8gmScVWa+9qmltzxRGRpLm3m/VuBZvOlGdeUcKAX8zEH6A,iv:B2UEtjTRIjT6W+tH2gtcl6XMvZNgbvZUXTiBePGOu24=,tag:SHIF6eaWBLwy9RrEy1N9kg==,type:str]
|
||||||
|
UMAMI: ENC[AES256_GCM,data:O3kQ6YTWH3xbu4Tkpyh2S0HZvqymUUDDYQbN3I6TX3OUxoNZcogPn+X8AY9FoaS2/amqo0X4YCQCimEgCfdXqknydmMD5x957H5Lc/XyYxB9zk6DIdU9gN6yO4uZClG34TqOsjpjZkExivykNsTzaFfPRcmCz1EVXpY4GVAAKNWxmr7M/99bnT6mSE8zQUj0g/gskCeWGP4bRbpMFtvPOvGChmwjSoMDp5GmYdISiZsacHSv0Zp0o9XSYBWbux4ePHcFvWP+alnToY0oF9g+NJPyzj2X1ihtbcZxgLLHyfGkI5uYKNsuUjB9K+D8u1xLE2AGRPyBrRQPZftSw/Zu1brIpz13HicDbJkR+0zmWBxG1WsTnyhIbIn12o2lwO6CVPLWhuRIx5hv7xm8hTfBdOwsVZZfXCr/ZWkERSzQhp/yDN2Fq5IpyZHdhmMnpkkg1G1oiWtN,iv:7HPZ7b6OlxCoXSkdDlLNpQT1xkF0Uj8IYs+Cx701CYU=,tag:+PFDVpzkbd2tM5JKnNpIUw==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
|
- recipient: age1sxzuhh2fcd4pmaz4mdqq95t683d32ft22w9t2r7pk258u0s8wymsqdj7lg
|
||||||
@@ -26,8 +28,8 @@ sops:
|
|||||||
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
|
S1NaTVFTL0FCdm1EQmRsUnlhclZNZlEKEgIe60qkvY8+UocjQU+WM2dTL/1y3Kqk
|
||||||
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
|
d4RrlLP9NSozwVsPYI4ntygvMSApbT4v0YvoO7gV90lkGWEvW1YDfA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2026-05-11T22:33:41Z"
|
lastmodified: "2026-05-12T19:20:38Z"
|
||||||
mac: ENC[AES256_GCM,data:276HHpEW56HOvKKbNPM79QrEBYDM590bOLfsgssSb79jm+LzrgLlYk2QImmXArADWby4Ai4jBPL4EahNm+a3aBazMEbwAu+EorvORE2P12W5C1ztskx5XUI3yDKY96jlZvmpXsqefa2pOQc1USk8ai/Obd5MLK06kMr2w3a7P9s=,iv:NJoe1lvw1hrWNL79Ux065UkSEDEEc0+NqlqB4tk3mAw=,tag:YTjIvEP1BO69Pa0qispMLQ==,type:str]
|
mac: ENC[AES256_GCM,data:j/fFN/zTHHSbH+RUdu/wK80Gu+qfoVDiVSDQWrBfL1Orezohf3hlGWJrXu8UtYa82CRVJ5BHzp589GQJTgI8pMaDirbQfpRA6jnYvLaWIU0lN66cnyZ8x919Lc67ceZPqih1q8CDCjRG40NZSwOpRqQVqpJMiuZjFaJqec0BDMM=,iv:7QIA0DfLPmXOjKvbsquYQIWqXbyPGWa+SLCEHH7IawU=,tag:jNFpqDDKF3mUQclpmwvGQg==,type:str]
|
||||||
pgp:
|
pgp:
|
||||||
- created_at: "2026-05-05T23:46:27Z"
|
- created_at: "2026-05-05T23:46:27Z"
|
||||||
enc: |-
|
enc: |-
|
||||||
|
|||||||
@@ -5,161 +5,9 @@ let
|
|||||||
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
|
(name: type: type == "directory" && builtins.pathExists (systemsDir + "/${name}/cfg.nix"))
|
||||||
(builtins.readDir systemsDir));
|
(builtins.readDir systemsDir));
|
||||||
|
|
||||||
userOpt = with lib; {
|
|
||||||
username = mkOption { type = types.str; };
|
|
||||||
pubssh = mkOption { type = types.str; default=""; };
|
|
||||||
wm = mkOption {
|
|
||||||
type = types.enum [ "Wayland" "X11" "-" ];
|
|
||||||
default = "-";
|
|
||||||
};
|
|
||||||
git = {
|
|
||||||
username = mkOption { type = types.str; default = "Anonymous";};
|
|
||||||
email = mkOption { type = types.str; default = "anonymous@domain"; };
|
|
||||||
key = mkOption { type = types.nullOr types.str; default=null; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
netOpt = with lib; {
|
|
||||||
ble = {
|
|
||||||
enable = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
wlp = {
|
|
||||||
enable = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
nif = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
wg = {
|
|
||||||
enable = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
ip4 = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
};
|
|
||||||
ip6 = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
};
|
|
||||||
pubkey = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
makeOpt = with lib; {
|
|
||||||
cli = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
};
|
|
||||||
gui = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
virt = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
power = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
game = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
develop = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
serverOpt = with lib; {
|
|
||||||
hostDomain = mkOption { type = types.str; };
|
|
||||||
mailDomain = mkOption { type = types.str; };
|
|
||||||
mailServer = mkOption { type = types.str; };
|
|
||||||
|
|
||||||
configPath = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "/media/config";
|
|
||||||
};
|
|
||||||
dataPath = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "/media/data";
|
|
||||||
};
|
|
||||||
|
|
||||||
colorScheme = mkOption {
|
|
||||||
#type = types.submodule {
|
|
||||||
# options = {
|
|
||||||
# slug = mkOption { type = types.str; };
|
|
||||||
# name = mkOption { type = types.str; };
|
|
||||||
# palette = mkOption {
|
|
||||||
type = types.attrs; #default = {};# };
|
|
||||||
#};
|
|
||||||
# };
|
|
||||||
default = (lib.evalModules { modules =[ { freeformType = with lib.types; attrsOf anything; } ../colors ];}).config.colorScheme ;
|
|
||||||
};
|
|
||||||
containers = mkOption {
|
|
||||||
type = types.attrsOf (types.submodule {
|
|
||||||
options = {
|
|
||||||
enable = mkOption { type = types.bool;default = false; };
|
|
||||||
db = mkOption { type = types.bool;default = false; };
|
|
||||||
sops = mkOption { type = types.bool;default = false; };
|
|
||||||
ip = mkOption { type = types.nullOr types.str; default = null;};
|
|
||||||
subdomain = mkOption { type = types.nullOr types.str; default=null;};
|
|
||||||
port = mkOption { type = types.nullOr types.port; default = null; };
|
|
||||||
extra = mkOption { type = types.attrs; default = {}; };
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = {};
|
|
||||||
};
|
|
||||||
openssh = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
wireguard = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
web = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
ipfw = {
|
|
||||||
enable = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
};
|
|
||||||
ifs = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
ports = mkOption {
|
|
||||||
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
|
|
||||||
default = [];
|
|
||||||
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
|
|
||||||
example = [
|
|
||||||
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
|
|
||||||
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
|
|
||||||
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
db = mkOption {
|
|
||||||
type = types.listOf (types.str);
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
in with lib; {
|
in with lib; {
|
||||||
options.usercfg = userOpt;
|
options.usercfg = import ./user.nix {inherit lib;};
|
||||||
options.syscfg = {
|
options.syscfg = {
|
||||||
hostname = mkOption { type = types.str; };
|
hostname = mkOption { type = types.str; };
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
@@ -171,20 +19,17 @@ in with lib; {
|
|||||||
default = "x86_64-linux";
|
default = "x86_64-linux";
|
||||||
};
|
};
|
||||||
defaultUser = mkOption { type = types.str; };
|
defaultUser = mkOption { type = types.str; };
|
||||||
make = makeOpt;
|
make = import ./make.nix {inherit lib;};
|
||||||
net = netOpt;
|
net = import ./net.nix {inherit lib;};
|
||||||
users = mkOption {
|
users = mkOption {
|
||||||
type = types.listOf (types.submodule { options = userOpt; });
|
type = types.listOf (types.submodule { options = import ./user.nix {inherit lib;}; });
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
peers = mkOption {
|
peers = mkOption {
|
||||||
default = map (name: import (systemsDir + "/${name}/cfg.nix")) systemNames;
|
default = map (name: import (systemsDir + "/${name}/cfg.nix")) systemNames;
|
||||||
};
|
};
|
||||||
server = mkOption {
|
server = mkOption {
|
||||||
type = types.oneOf [
|
type = types.oneOf [ types.bool (types.submodule { options = import ./server.nix {inherit lib;}; }) ];
|
||||||
types.bool
|
|
||||||
(types.submodule { options = serverOpt; })
|
|
||||||
];
|
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
9
modules/shared/syscfg/make.nix
Normal file
9
modules/shared/syscfg/make.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{ lib,... }:
|
||||||
|
with lib; {
|
||||||
|
cli = mkOption { type = types.bool; default = true; };
|
||||||
|
gui = mkOption { type = types.bool; default = false; };
|
||||||
|
virt = mkOption { type = types.bool; default = false; };
|
||||||
|
power = mkOption { type = types.bool; default = false; };
|
||||||
|
game = mkOption { type = types.bool; default = false; };
|
||||||
|
develop = mkOption { type = types.bool; default = false; };
|
||||||
|
}
|
||||||
14
modules/shared/syscfg/net.nix
Normal file
14
modules/shared/syscfg/net.nix
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{ lib,... }:
|
||||||
|
with lib; {
|
||||||
|
ble.enable = mkOption { type = types.bool; default = false; };
|
||||||
|
wlp = {
|
||||||
|
enable = mkOption { type = types.bool; default = false; };
|
||||||
|
nif = mkOption { type = types.str; default = ""; };
|
||||||
|
};
|
||||||
|
wg = {
|
||||||
|
enable = mkOption { type = types.bool; default = false; };
|
||||||
|
ip4 = mkOption { type = types.str; default = ""; };
|
||||||
|
ip6 = mkOption { type = types.str; default = ""; };
|
||||||
|
pubkey = mkOption { type = types.str; default = ""; };
|
||||||
|
};
|
||||||
|
}
|
||||||
92
modules/shared/syscfg/server.nix
Normal file
92
modules/shared/syscfg/server.nix
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
{ lib,... }:
|
||||||
|
let
|
||||||
|
|
||||||
|
in with lib; {
|
||||||
|
hostDomain = mkOption { type = types.str; };
|
||||||
|
mailDomain = mkOption { type = types.str; };
|
||||||
|
mailServer = mkOption { type = types.str; };
|
||||||
|
|
||||||
|
configPath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/media/config";
|
||||||
|
};
|
||||||
|
dataPath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/media/data";
|
||||||
|
};
|
||||||
|
|
||||||
|
colorScheme = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = (lib.evalModules { modules =[ { freeformType = with lib.types; attrsOf anything; } ../colors ];}).config.colorScheme ;
|
||||||
|
};
|
||||||
|
loadedContainers = lib.mkOption {
|
||||||
|
readOnly = true;
|
||||||
|
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
|
||||||
|
options = {
|
||||||
|
name = lib.mkOption {type = lib.types.str; default = name;};
|
||||||
|
sops = lib.mkOption {type = lib.types.bool; default = false;};
|
||||||
|
db = lib.mkOption {type = lib.types.bool; default = false;};
|
||||||
|
|
||||||
|
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
|
||||||
|
containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };};
|
||||||
|
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
|
||||||
|
|
||||||
|
setup = {
|
||||||
|
trigger = lib.mkOption {type = lib.types.str; default = "";};
|
||||||
|
script = lib.mkOption {type = lib.types.nullOr lib.types.package; default = null;};
|
||||||
|
envFile = lib.mkOption {type = lib.types.nullOr lib.types.str; default = null;};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
};
|
||||||
|
containers = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
subdomain = mkOption { type = types.nullOr types.str; default=null;};
|
||||||
|
subpath = mkOption { type = types.nullOr types.str; default=null;};
|
||||||
|
port = mkOption { type = types.nullOr types.port; default = null; };
|
||||||
|
extra = mkOption { type = types.attrs; default = {}; };
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
openssh = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
wireguard = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
web = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
ipfw = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
ifs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
ports = mkOption {
|
||||||
|
type = types.listOf (types.listOf (types.oneOf [ types.str types.int ]));
|
||||||
|
default = [];
|
||||||
|
description = "Forwarding rules: [ [srcInterface dstAddr srcPort dstPort] ... ]";
|
||||||
|
example = [
|
||||||
|
[ "ens3" "10.10.1.2" "IPV6" 22 2222 ]
|
||||||
|
[ "ens3" "10.10.1.2" "IPV6" 80 80 ]
|
||||||
|
[ "ens3" "10.10.1.2" "IPV6" 443 443 ]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
db = mkOption {
|
||||||
|
type = types.listOf (types.str);
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
14
modules/shared/syscfg/user.nix
Normal file
14
modules/shared/syscfg/user.nix
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{ lib,... }:
|
||||||
|
with lib; {
|
||||||
|
username = mkOption { type = types.str; };
|
||||||
|
pubssh = mkOption { type = types.str; default=""; };
|
||||||
|
wm = mkOption {
|
||||||
|
type = types.enum [ "Wayland" "X11" "-" ];
|
||||||
|
default = "-";
|
||||||
|
};
|
||||||
|
git = {
|
||||||
|
username = mkOption { type = types.str; default = "Anonymous";};
|
||||||
|
email = mkOption { type = types.str; default = "anonymous@domain"; };
|
||||||
|
key = mkOption { type = types.nullOr types.str; default=null; };
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -29,37 +29,44 @@
|
|||||||
containers = {
|
containers = {
|
||||||
|
|
||||||
traefik = {
|
traefik = {
|
||||||
enable = true;
|
|
||||||
sops = true;
|
|
||||||
subdomain = "traefik";
|
subdomain = "traefik";
|
||||||
extra={provider="infomaniak";};
|
extra={provider="infomaniak";};
|
||||||
};
|
};
|
||||||
authentik = {
|
authentik = {
|
||||||
enable = true;
|
|
||||||
db = true;
|
|
||||||
subdomain = "sso";
|
subdomain = "sso";
|
||||||
port = 9000;
|
port = 9000;
|
||||||
};
|
};
|
||||||
nextcloud = {
|
nextcloud = {
|
||||||
enable = true;
|
|
||||||
db = true;
|
|
||||||
subdomain = "cloud";
|
subdomain = "cloud";
|
||||||
};
|
};
|
||||||
collabora = {
|
collabora = {
|
||||||
enable = true;
|
|
||||||
sops = true;
|
|
||||||
subdomain = "office";
|
subdomain = "office";
|
||||||
};
|
};
|
||||||
etherpad = {
|
etherpad = {
|
||||||
enable = true;
|
subdomain = "pad";
|
||||||
db = true;
|
};
|
||||||
|
ethercalc = {
|
||||||
subdomain = "pad";
|
subdomain = "pad";
|
||||||
};
|
};
|
||||||
gitea = {
|
gitea = {
|
||||||
enable = true;
|
|
||||||
db = true;
|
|
||||||
subdomain = "git";
|
subdomain = "git";
|
||||||
};
|
};
|
||||||
|
searxng = {
|
||||||
|
subdomain = "searx";
|
||||||
|
};
|
||||||
|
jellyfin = {
|
||||||
|
subdomain = "flix";
|
||||||
|
};
|
||||||
|
transmission = {
|
||||||
|
subdomain = "rflix";
|
||||||
|
subpath = "p2p";
|
||||||
|
};
|
||||||
|
servarr = {
|
||||||
|
subdomain = "arr";
|
||||||
|
};
|
||||||
|
umami = {
|
||||||
|
subdomain = "umami";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user