This commit is contained in:
parent
169b62e6f3
commit
2d3da197ee
13 changed files with 711 additions and 74 deletions
|
|
@ -1,19 +1,23 @@
|
||||||
|
set unstable
|
||||||
|
|
||||||
base_path := invocation_directory() / "systems/x86_64-linux"
|
base_path := invocation_directory() / "systems/x86_64-linux"
|
||||||
sops := "nix shell nixpkgs#sops --command sops"
|
# sops := "nix shell nixpkgs#sops --command sops"
|
||||||
yq := "nix shell nixpkgs#yq --command yq"
|
# yq := "nix shell nixpkgs#yq --command yq"
|
||||||
|
sops := "sops"
|
||||||
|
yq := "yq"
|
||||||
|
|
||||||
@_default:
|
@_default:
|
||||||
just --list
|
just --list
|
||||||
|
|
||||||
[doc('list all vars of the target machine')]
|
[doc('list all vars of the target machine')]
|
||||||
list machine:
|
list machine:
|
||||||
{{ sops }} decrypt {{ base_path }}/{{ machine }}/secrets.yml
|
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml
|
||||||
|
|
||||||
@edit machine:
|
@edit machine:
|
||||||
{{ sops }} edit {{ base_path }}/{{ machine }}/secrets.yml
|
sops edit {{ base_path }}/{{ machine }}/secrets.yml
|
||||||
|
|
||||||
@set machine key value:
|
@set machine key value:
|
||||||
{{ sops }} set {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" "\"$(echo '{{ value }}' | sed 's/\"/\\"/g')\""
|
sops set {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')" "\"$(echo '{{ value }}' | sed 's/\"/\\\"/g')\""
|
||||||
|
|
||||||
git add {{ base_path }}/{{ machine }}/secrets.yml
|
git add {{ base_path }}/{{ machine }}/secrets.yml
|
||||||
git commit -m 'chore(secrets): set secret "{{ key }}" for machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
|
git commit -m 'chore(secrets): set secret "{{ key }}" for machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
|
||||||
|
|
@ -21,10 +25,10 @@ list machine:
|
||||||
echo "Done"
|
echo "Done"
|
||||||
|
|
||||||
@get machine key:
|
@get machine key:
|
||||||
{{ sops }} decrypt {{ base_path }}/{{ machine }}/secrets.yml | {{ yq }} ".$(echo "{{ key }}" | sed -E 's/\//./g')"
|
sops decrypt {{ base_path }}/{{ machine }}/secrets.yml | yq ".$(echo "{{ key }}" | sed -E 's/\//./g')"
|
||||||
|
|
||||||
@remove machine key:
|
@remove machine key:
|
||||||
{{ sops }} unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')"
|
sops unset {{ base_path }}/{{ machine }}/secrets.yml "$(printf '%s\n' '["{{ key }}"]' | sed -E 's#/#"]["#g; s/\["([0-9]+)"\]/[\1]/g')"
|
||||||
|
|
||||||
git add {{ base_path }}/{{ machine }}/secrets.yml
|
git add {{ base_path }}/{{ machine }}/secrets.yml
|
||||||
git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
|
git commit -m 'chore(secrets): removed secret "{{ key }}" from machine "{{ machine}}"' -- {{ base_path }}/{{ machine }}/secrets.yml > /dev/null
|
||||||
|
|
|
||||||
38
lib/options/default.nix
Normal file
38
lib/options/default.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{ lib, ...}:
|
||||||
|
let
|
||||||
|
inherit (builtins) isString typeOf;
|
||||||
|
inherit (lib) mkOption types throwIfNot concatStringsSep splitStringBy toLower map;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
mkUrlOptions =
|
||||||
|
defaults:
|
||||||
|
{
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "host.tld";
|
||||||
|
description = ''
|
||||||
|
Hostname
|
||||||
|
'';
|
||||||
|
} // (defaults.host or {});
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 1234;
|
||||||
|
example = "1234";
|
||||||
|
description = ''
|
||||||
|
Port
|
||||||
|
'';
|
||||||
|
} // (defaults.port or {});
|
||||||
|
|
||||||
|
protocol = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "https";
|
||||||
|
example = "https";
|
||||||
|
description = ''
|
||||||
|
Which protocol to use when creating a url string
|
||||||
|
'';
|
||||||
|
} // (defaults.protocol or {});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
{ lib, ...}:
|
{ lib, ...}:
|
||||||
let
|
let
|
||||||
inherit (builtins) isString typeOf;
|
inherit (builtins) isString typeOf match toString head;
|
||||||
inherit (lib) throwIfNot concatStringsSep splitStringBy toLower map;
|
inherit (lib) throwIfNot concatStringsSep splitStringBy toLower map concatMapAttrsStringSep;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
strings = {
|
strings = {
|
||||||
|
#========================================================================================
|
||||||
|
# Converts a string to snake case
|
||||||
|
#
|
||||||
|
# simply replaces any uppercase letter to its lowercase variant preceeded by an underscore
|
||||||
|
#========================================================================================
|
||||||
toSnakeCase =
|
toSnakeCase =
|
||||||
str:
|
str:
|
||||||
throwIfNot (isString str) "toSnakeCase only accepts string values, but got ${typeOf str}" (
|
throwIfNot (isString str) "toSnakeCase only accepts string values, but got ${typeOf str}" (
|
||||||
|
|
@ -13,5 +18,22 @@ in
|
||||||
|> map (p: toLower p)
|
|> map (p: toLower p)
|
||||||
|> concatStringsSep "_"
|
|> concatStringsSep "_"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#========================================================================================
|
||||||
|
# Converts a set of url parts to a string
|
||||||
|
#========================================================================================
|
||||||
|
toUrl =
|
||||||
|
{ protocol ? null, host, port ? null, path ? null, query ? null, hash ? null }:
|
||||||
|
let
|
||||||
|
trim_slashes = str: str |> match "^\/*(.+?)\/*$" |> head;
|
||||||
|
encode_to_str = set: concatMapAttrsStringSep "&" (n: v: "${n}=${v}") set;
|
||||||
|
|
||||||
|
_protocol = if protocol != null then "${protocol}://" else "";
|
||||||
|
_port = if port != null then ":${toString port}" else "";
|
||||||
|
_path = if path != null then "/${path |> trim_slashes}" else "";
|
||||||
|
_query = if query != null then "?${query |> encode_to_str}" else "";
|
||||||
|
_hash = if hash != null then "#${hash |> encode_to_str}" else "";
|
||||||
|
in
|
||||||
|
"${_protocol}${host}${_port}${_path}${_query}${_hash}";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
emoji = {
|
emoji = {
|
||||||
package = pkgs.noto-fonts-emoji;
|
package = pkgs.noto-fonts-color-emoji;
|
||||||
name = "Noto Color Emoji";
|
name = "Noto Color Emoji";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{ config, lib, pkgs, namespace, system, inputs, ... }:
|
{ config, lib, pkgs, namespace, system, inputs, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames;
|
inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs concatMapStringsSep filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames;
|
||||||
inherit (lib.${namespace}.strings) toSnakeCase;
|
inherit (lib.${namespace}.strings) toSnakeCase;
|
||||||
|
|
||||||
cfg = config.${namespace}.services.authentication.zitadel;
|
cfg = config.${namespace}.services.authentication.zitadel;
|
||||||
|
|
@ -334,6 +334,16 @@ in
|
||||||
concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set
|
concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set
|
||||||
;
|
;
|
||||||
|
|
||||||
|
append = attrList: set: set // (listToAttrs attrList);
|
||||||
|
forEach = src: key: set:
|
||||||
|
let
|
||||||
|
_key = concatMapStringsSep "_" (k: "\${item.${k}}") key;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
forEach = "{ for item in ${src} : \"${_key}\" => item }";
|
||||||
|
}
|
||||||
|
// set;
|
||||||
|
|
||||||
config' = config;
|
config' = config;
|
||||||
|
|
||||||
# this is a nix package, the generated json file to be exact
|
# this is a nix package, the generated json file to be exact
|
||||||
|
|
@ -418,7 +428,7 @@ in
|
||||||
|
|
||||||
# Users
|
# Users
|
||||||
zitadel_human_user =
|
zitadel_human_user =
|
||||||
(cfg.organization
|
cfg.organization
|
||||||
|> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }:
|
|> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }:
|
||||||
{
|
{
|
||||||
inherit email userName firstName lastName;
|
inherit email userName firstName lastName;
|
||||||
|
|
@ -427,24 +437,20 @@ in
|
||||||
}
|
}
|
||||||
|> withRef "org" org
|
|> withRef "org" org
|
||||||
|> toResource "${org}_${name}"
|
|> toResource "${org}_${name}"
|
||||||
))
|
)
|
||||||
|
|> append
|
||||||
// {
|
[
|
||||||
"extra_users" = {
|
(forEach "local.extra_users" [ "org" "name" ] {
|
||||||
for_each = lib.tfRef ''{
|
orgId = lib.tfRef "local.orgs[each.value.org]";
|
||||||
for user in local.extra_users :
|
userName = lib.tfRef "each.value.name";
|
||||||
"''${user.org}_''${user.name}" => user
|
|
||||||
}'';
|
|
||||||
|
|
||||||
org_id = lib.tfRef "local.orgs[each.value.org]";
|
|
||||||
user_name = lib.tfRef "each.value.name";
|
|
||||||
email = lib.tfRef "each.value.email";
|
email = lib.tfRef "each.value.email";
|
||||||
first_name = lib.tfRef "each.value.firstName";
|
firstName = lib.tfRef "each.value.firstName";
|
||||||
last_name = lib.tfRef "each.value.lastName";
|
lastName = lib.tfRef "each.value.lastName";
|
||||||
|
|
||||||
is_email_verified = true;
|
isEmailVerified = true;
|
||||||
};
|
}
|
||||||
}
|
|> toResource "extraUsers")
|
||||||
|
]
|
||||||
;
|
;
|
||||||
|
|
||||||
# Global user roles
|
# Global user roles
|
||||||
|
|
@ -708,6 +714,12 @@ in
|
||||||
restartUnits = [ "zitadelApplyTerraform.service" ];
|
restartUnits = [ "zitadelApplyTerraform.service" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
templates = {
|
||||||
|
"users.yml" = {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,22 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
programs.ssh.extraConfig = ''
|
||||||
|
Host beheer.hazelhof.nl
|
||||||
|
Port 222
|
||||||
|
User chris
|
||||||
|
AddressFamily inet
|
||||||
|
IdentityFile /home/chris/.ssh/id_ed25519
|
||||||
|
'';
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
borgbackup.jobs = {
|
borgbackup.jobs = {
|
||||||
media = {
|
media = {
|
||||||
paths = "/var/media/test";
|
paths = "/var/media/test";
|
||||||
encryption.mode = "none";
|
encryption.mode = "none";
|
||||||
environment.BORG_SSH = "ssh -i /home/chris/.ssh/id_ed25519 -4";
|
# environment.BORG_SSH = "ssh -4 -i /home/chris/.ssh/id_ed25519";
|
||||||
repo = "ssh://chris@beheer.hazelhof.nl:222/media";
|
environment.BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
|
||||||
|
repo = "ssh://beheer.hazelhof.nl//media";
|
||||||
compression = "auto,zstd";
|
compression = "auto,zstd";
|
||||||
startAt = "daily";
|
startAt = "daily";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
{ pkgs, lib, namespace, config, ... }:
|
{ pkgs, lib, namespace, config, inputs, system, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkIf mkEnableOption mkOption;
|
inherit (lib) mkIf mkEnableOption mkOption;
|
||||||
inherit (lib.types) str;
|
inherit (lib.types) str;
|
||||||
|
|
||||||
cfg = config.${namespace}.services.media;
|
cfg = config.${namespace}.services.media;
|
||||||
|
|
||||||
|
arr = ["radarr" ];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.${namespace}.services.media = {
|
options.${namespace}.services.media = {
|
||||||
|
|
@ -60,47 +62,48 @@ in
|
||||||
"d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.path}/reiverr/config' 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
"d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.path}/downloads/incomplete' 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
"d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -"
|
"d '${cfg.path}/downloads/done' 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d /var/lib/radarrApplyTerraform 0755 ${cfg.user} ${cfg.group} -"
|
||||||
];
|
];
|
||||||
|
|
||||||
#=========================================================================
|
#=========================================================================
|
||||||
# Services
|
# Services
|
||||||
#=========================================================================
|
#=========================================================================
|
||||||
services = let
|
services = let
|
||||||
arrService = {
|
arr-services =
|
||||||
enable = true;
|
arr
|
||||||
openFirewall = true;
|
|> lib.imap (i: service: {
|
||||||
|
name = service;
|
||||||
|
value = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
|
||||||
settings = {
|
environmentFiles = [
|
||||||
auth.AuthenticationMethod = "External";
|
config.sops.templates."${service}/config.env".path
|
||||||
};
|
];
|
||||||
};
|
|
||||||
|
|
||||||
withPort = port: service: service // { settings.server.Port = builtins.toString port; };
|
settings = {
|
||||||
|
auth.authenticationMethod = "External";
|
||||||
|
|
||||||
withUserAndGroup = service: service // {
|
server = {
|
||||||
user = cfg.user;
|
bindaddress = "0.0.0.0";
|
||||||
group = cfg.group;
|
port = 2000 + i;
|
||||||
};
|
};
|
||||||
in {
|
|
||||||
radarr =
|
|
||||||
arrService
|
|
||||||
|> withPort 2001
|
|
||||||
|> withUserAndGroup;
|
|
||||||
|
|
||||||
sonarr =
|
|
||||||
arrService
|
|
||||||
|> withPort 2002
|
|
||||||
|> withUserAndGroup;
|
|
||||||
|
|
||||||
lidarr =
|
|
||||||
arrService
|
|
||||||
|> withPort 2003
|
|
||||||
|> withUserAndGroup;
|
|
||||||
|
|
||||||
prowlarr =
|
|
||||||
arrService
|
|
||||||
|> withPort 2004;
|
|
||||||
|
|
||||||
|
postgres = {
|
||||||
|
host = "localhost";
|
||||||
|
port = "5432";
|
||||||
|
user = service;
|
||||||
|
maindb = service;
|
||||||
|
logdb = service;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// (if service != "prowlarr" then { user = cfg.user; group = cfg.group; } else {});
|
||||||
|
})
|
||||||
|
|> lib.listToAttrs
|
||||||
|
;
|
||||||
|
in
|
||||||
|
arr-services // {
|
||||||
bazarr = {
|
bazarr = {
|
||||||
enable = true;
|
enable = true;
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
|
|
@ -146,6 +149,19 @@ in
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
postgresql =
|
||||||
|
let
|
||||||
|
databases = arr |> lib.concatMap (s: [ s "${s}-log" ]);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = true;
|
||||||
|
ensureDatabases = arr;
|
||||||
|
ensureUsers = arr |> lib.map (service: {
|
||||||
|
name = service;
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
caddy = {
|
caddy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
virtualHosts = {
|
virtualHosts = {
|
||||||
|
|
@ -156,6 +172,136 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.radarrApplyTerraform =
|
||||||
|
let
|
||||||
|
# this is a nix package, the generated json file to be exact
|
||||||
|
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
|
||||||
|
inherit system;
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
({ config, lib, ... }: {
|
||||||
|
config = {
|
||||||
|
variable = {
|
||||||
|
api_key = {
|
||||||
|
type = "string";
|
||||||
|
description = "Radarr api key";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
terraform.required_providers.radarr = {
|
||||||
|
source = "devopsarr/radarr";
|
||||||
|
version = "2.2.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
provider.radarr = {
|
||||||
|
url = "http://127.0.0.1:2001";
|
||||||
|
api_key = lib.tfRef "var.api_key";
|
||||||
|
};
|
||||||
|
|
||||||
|
resource = {
|
||||||
|
radarr_root_folder.local = {
|
||||||
|
path = "/var/media/movies";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "Radarr terraform apply";
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [ "radarr.service" ];
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ "$(systemctl is-active radarr)" != "active" ]; then
|
||||||
|
echo "Radarr is not running"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sleep for a bit to give radarr the chance to start up
|
||||||
|
sleep 5s
|
||||||
|
|
||||||
|
# Print the path to the source for easier debugging
|
||||||
|
echo "config location: ${terraformConfiguration}"
|
||||||
|
|
||||||
|
# Copy infra code into workspace
|
||||||
|
cp -f ${terraformConfiguration} config.tf.json
|
||||||
|
|
||||||
|
# Initialize OpenTofu
|
||||||
|
${lib.getExe pkgs.opentofu} init
|
||||||
|
|
||||||
|
# Run the infrastructure code
|
||||||
|
# ${lib.getExe pkgs.opentofu} plan -var-file='${config.sops.templates."radarr/config.tfvars".path}'
|
||||||
|
${lib.getExe pkgs.opentofu} apply -auto-approve -var-file='${config.sops.templates."radarr/config.tfvars".path}'
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
|
||||||
|
WorkingDirectory = "/var/lib/radarrApplyTerraform";
|
||||||
|
|
||||||
|
EnvironmentFile = [
|
||||||
|
config.sops.templates."radarr/config.env".path
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
|
systemd.services.jellyfin.serviceConfig.killSignal = lib.mkForce "SIGKILL";
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
secrets =
|
||||||
|
arr
|
||||||
|
|> lib.map (service: {
|
||||||
|
name = "${service}/apikey";
|
||||||
|
value = {
|
||||||
|
owner = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
restartUnits = [ "${service}.service" ];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|> lib.listToAttrs
|
||||||
|
;
|
||||||
|
|
||||||
|
templates =
|
||||||
|
let
|
||||||
|
apikeys =
|
||||||
|
arr
|
||||||
|
|> lib.map (service: {
|
||||||
|
name = "${service}/config.env";
|
||||||
|
value = {
|
||||||
|
owner = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
restartUnits = [ "${service}.service" ];
|
||||||
|
content = ''
|
||||||
|
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|> lib.listToAttrs;
|
||||||
|
|
||||||
|
tfvars =
|
||||||
|
arr
|
||||||
|
|> lib.map(service: {
|
||||||
|
name = "${service}/config.tfvars";
|
||||||
|
value = {
|
||||||
|
owner = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
restartUnits = [ "${service}ApplyTerraform.service" ];
|
||||||
|
content = ''
|
||||||
|
api_key = "${config.sops.placeholder."${service}/apikey"}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|> lib.listToAttrs;
|
||||||
|
in
|
||||||
|
apikeys // tfvars
|
||||||
|
;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ in
|
||||||
type = "Radarr";
|
type = "Radarr";
|
||||||
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg";
|
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/radarr.svg";
|
||||||
tag = "app";
|
tag = "app";
|
||||||
url = "http://${config.networking.hostName}:${builtins.toString config.services.radarr.settings.server.port}";
|
url = "http://${config.networking.hostName}:2001";
|
||||||
target = "_blank";
|
target = "_blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +112,7 @@ in
|
||||||
type = "Sonarr";
|
type = "Sonarr";
|
||||||
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg";
|
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/sonarr.svg";
|
||||||
tag = "app";
|
tag = "app";
|
||||||
url = "http://${config.networking.hostName}:${builtins.toString config.services.sonarr.settings.server.port}";
|
url = "http://${config.networking.hostName}:2002";
|
||||||
target = "_blank";
|
target = "_blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ in
|
||||||
type = "Lidarr";
|
type = "Lidarr";
|
||||||
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg";
|
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/lidarr.svg";
|
||||||
tag = "app";
|
tag = "app";
|
||||||
url = "http://${config.networking.hostName}:${builtins.toString config.services.lidarr.settings.server.port}";
|
url = "http://${config.networking.hostName}:2003";
|
||||||
target = "_blank";
|
target = "_blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +130,7 @@ in
|
||||||
type = "Prowlarr";
|
type = "Prowlarr";
|
||||||
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg";
|
logo = "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/prowlarr.svg";
|
||||||
tag = "app";
|
tag = "app";
|
||||||
url = "http://${config.networking.hostName}:${builtins.toString config.services.prowlarr.settings.server.port}";
|
url = "http://${config.networking.hostName}:2004";
|
||||||
target = "_blank";
|
target = "_blank";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
214
modules/nixos/services/media/servarr/default.nix
Normal file
214
modules/nixos/services/media/servarr/default.nix
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
{ pkgs, config, lib, namespace, inputs, system, ... }:
|
||||||
|
let
|
||||||
|
inherit (builtins) toString;
|
||||||
|
inherit (lib) mkIf mkEnableOption mkOption types;
|
||||||
|
|
||||||
|
cfg = config.${namespace}.services.media.servarr;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.services.media = {
|
||||||
|
servarr = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "Enable ${name}";
|
||||||
|
debug = mkEnableOption "Use tofu plan instead of tofu apply for ${name} ";
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
};
|
||||||
|
|
||||||
|
rootFolders = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
services =
|
||||||
|
cfg
|
||||||
|
|> lib.mapAttrsToList (service: { enable, port, ... }: (mkIf enable {
|
||||||
|
"${service}" = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
|
||||||
|
environmentFiles = [
|
||||||
|
config.sops.templates."${service}/config.env".path
|
||||||
|
];
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
auth.authenticationMethod = "External";
|
||||||
|
|
||||||
|
server = {
|
||||||
|
bindaddress = "0.0.0.0";
|
||||||
|
port = port;
|
||||||
|
};
|
||||||
|
|
||||||
|
postgres = {
|
||||||
|
host = "localhost";
|
||||||
|
port = "5432";
|
||||||
|
user = service;
|
||||||
|
maindb = service;
|
||||||
|
logdb = service;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
|> lib.mergeAttrsList
|
||||||
|
|> (set: set // {
|
||||||
|
postgres = {
|
||||||
|
ensureDatabases = cfg |> lib.attrNames;
|
||||||
|
ensureUsers = cfg |> lib.attrNames |> lib.map (service: {
|
||||||
|
name = service;
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})
|
||||||
|
;
|
||||||
|
|
||||||
|
systemd =
|
||||||
|
cfg
|
||||||
|
|> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable {
|
||||||
|
tmpfiles.rules = [
|
||||||
|
"d /var/lib/${service}ApplyTerraform 0755 ${service} ${service} -"
|
||||||
|
];
|
||||||
|
|
||||||
|
services."${service}ApplyTerraform" =
|
||||||
|
let
|
||||||
|
terraformConfiguration = inputs.terranix.lib.terranixConfiguration {
|
||||||
|
inherit system;
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
({ config, lib, ... }: {
|
||||||
|
config = {
|
||||||
|
variable = {
|
||||||
|
api_key = {
|
||||||
|
type = "string";
|
||||||
|
description = "${service} api key";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
terraform.required_providers.${service} = {
|
||||||
|
source = "devopsarr/${service}";
|
||||||
|
version = "2.2.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
provider.${service} = {
|
||||||
|
url = "http://127.0.0.1:${toString port}";
|
||||||
|
api_key = lib.tfRef "var.api_key";
|
||||||
|
};
|
||||||
|
|
||||||
|
resource = {
|
||||||
|
"${service}_root_folder" =
|
||||||
|
rootFolders
|
||||||
|
|> lib.imap (i: f: lib.nameValuePair "local${toString i}" { path = f; })
|
||||||
|
|> lib.listToAttrs
|
||||||
|
;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "${service} terraform apply";
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [ "${service}.service" ];
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Sleep for a bit to give the service a chance to start up
|
||||||
|
sleep 5s
|
||||||
|
|
||||||
|
if [ "$(systemctl is-active ${service})" != "active" ]; then
|
||||||
|
echo "${service} is not running"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print the path to the source for easier debugging
|
||||||
|
echo "config location: ${terraformConfiguration}"
|
||||||
|
|
||||||
|
# Copy infra code into workspace
|
||||||
|
cp -f ${terraformConfiguration} config.tf.json
|
||||||
|
|
||||||
|
# Initialize OpenTofu
|
||||||
|
${lib.getExe pkgs.opentofu} init
|
||||||
|
|
||||||
|
# Run the infrastructure code
|
||||||
|
${lib.getExe pkgs.opentofu} \
|
||||||
|
${if debug then "plan" else "apply -auto-approve"} \
|
||||||
|
-var-file='${config.sops.templates."${service}/config.tfvars".path}'
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = service;
|
||||||
|
Group = service;
|
||||||
|
|
||||||
|
WorkingDirectory = "/var/lib/${service}ApplyTerraform";
|
||||||
|
|
||||||
|
EnvironmentFile = [
|
||||||
|
config.sops.templates."${service}/config.env".path
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
|> lib.mergeAttrsList
|
||||||
|
;
|
||||||
|
|
||||||
|
users.users =
|
||||||
|
cfg
|
||||||
|
|> lib.mapAttrsToList (service: { enable, ... }: (mkIf enable {
|
||||||
|
"${service}".extraGroups = [ "media" ];
|
||||||
|
}))
|
||||||
|
|> lib.mergeAttrsList
|
||||||
|
;
|
||||||
|
|
||||||
|
sops =
|
||||||
|
cfg
|
||||||
|
|> lib.mapAttrsToList (service: { enable, ... }: (mkIf enable {
|
||||||
|
secrets."${service}/apikey" = {
|
||||||
|
owner = service;
|
||||||
|
group = service;
|
||||||
|
restartUnits = [ "${service}.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
templates = {
|
||||||
|
"${service}/config.env" = {
|
||||||
|
owner = service;
|
||||||
|
group = service;
|
||||||
|
restartUnits = [ "${service}.service" ];
|
||||||
|
content = ''
|
||||||
|
${lib.toUpper service}__AUTH__APIKEY="${config.sops.placeholder."${service}/apikey"}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
"${service}/config.tfvars" = {
|
||||||
|
owner = service;
|
||||||
|
group = service;
|
||||||
|
restartUnits = [ "${service}.service" ];
|
||||||
|
content = ''
|
||||||
|
api_key = "${config.sops.placeholder."${service}/apikey"}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
|> lib.mergeAttrsList
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
# cfg
|
||||||
|
# |> lib.mapAttrsToList (service: { enable, debug, port, rootFolders, ... }: (mkIf enable {
|
||||||
|
|
||||||
|
# # sops = {
|
||||||
|
# # };
|
||||||
|
# }))
|
||||||
|
# |> lib.mergeAttrsList
|
||||||
|
# ;
|
||||||
|
}
|
||||||
25
modules/nixos/services/observability/uptime-kuma/default.nix
Normal file
25
modules/nixos/services/observability/uptime-kuma/default.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{ pkgs, config, lib, namespace, ... }:
|
||||||
|
let
|
||||||
|
inherit (builtins) toString;
|
||||||
|
inherit (lib) mkIf mkEnableOption;
|
||||||
|
|
||||||
|
cfg = config.${namespace}.services.observability.uptime-kuma;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.${namespace}.services.observability.uptime-kuma = {
|
||||||
|
enable = mkEnableOption "enable uptime kuma";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
services.uptime-kuma = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
PORT = toString 9006;
|
||||||
|
HOST = "0.0.0.0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 9006 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,87 @@
|
||||||
{ pkgs, config, lib, namespace, ... }:
|
{ pkgs, config, lib, namespace, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) toString;
|
inherit (builtins) toString;
|
||||||
inherit (lib) mkIf mkEnableOption;
|
inherit (lib) mkIf mkEnableOption mkOption types getAttrs toUpper concatMapAttrsStringSep;
|
||||||
|
|
||||||
cfg = config.${namespace}.services.security.vaultwarden;
|
cfg = config.${namespace}.services.security.vaultwarden;
|
||||||
|
|
||||||
|
databaseProviderSqlite = types.submodule ({ ... }: {
|
||||||
|
options = {
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "sqlite" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
file = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = '''';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
databaseProviderPostgresql = types.submodule ({ ... }:
|
||||||
|
let
|
||||||
|
urlOptions = lib.${namespace}.options.mkUrlOptions {
|
||||||
|
host = {
|
||||||
|
description = ''
|
||||||
|
Hostname of the postgresql server
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = {
|
||||||
|
default = 5432;
|
||||||
|
example = "5432";
|
||||||
|
description = ''
|
||||||
|
Port of the postgresql server
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol = mkOption {
|
||||||
|
default = "postgres";
|
||||||
|
example = "postgres";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "postgresql" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
sslMode = mkOption {
|
||||||
|
type = types.enum [ "verify-ca" "verify-full" "require" "prefer" "allow" "disabled" ];
|
||||||
|
default = "verify-full";
|
||||||
|
example = "verify-ca";
|
||||||
|
description = ''
|
||||||
|
How to verify the server's ssl
|
||||||
|
|
||||||
|
| mode | eavesdropping protection | MITM protection | Statement |
|
||||||
|
|-------------|--------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| disable | No | No | I don't care about security, and I don't want to pay the overhead of encryption. |
|
||||||
|
| allow | Maybe | No | I don't care about security, but I will pay the overhead of encryption if the server insists on it. |
|
||||||
|
| prefer | Maybe | No | I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it. |
|
||||||
|
| require | Yes | No | I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want. |
|
||||||
|
| verify-ca | Yes | Depends on CA policy | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust. |
|
||||||
|
| verify-full | Yes | Yes | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify. |
|
||||||
|
|
||||||
|
[Source](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
} // (urlOptions |> getAttrs [ "protocol" "host" "port" ]);
|
||||||
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.${namespace}.services.security.vaultwarden = {
|
options.${namespace}.services.security.vaultwarden = {
|
||||||
enable = mkEnableOption "enable vaultwarden";
|
enable = mkEnableOption "enable vaultwarden";
|
||||||
|
|
||||||
|
database = mkOption {
|
||||||
|
type = types.oneOf [
|
||||||
|
(types.addCheck databaseProviderSqlite (x: x ? type && x.type == "sqlite"))
|
||||||
|
(types.addCheck databaseProviderPostgresql (x: x ? type && x.type == "postgresql"))
|
||||||
|
null
|
||||||
|
];
|
||||||
|
default = null;
|
||||||
|
description = '''';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
@ -15,6 +89,8 @@ in
|
||||||
"d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -"
|
"d '/var/lib/vaultwarden' 0700 vaultwarden vaultwarden - -"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# systemd.services.vaultwarden.wants = [ "zitadelApplyTerraform.service" ];
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
vaultwarden = {
|
vaultwarden = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
@ -26,8 +102,6 @@ in
|
||||||
SIGNUPS_ALLOWED = false;
|
SIGNUPS_ALLOWED = false;
|
||||||
DOMAIN = "https://vault.kruining.eu";
|
DOMAIN = "https://vault.kruining.eu";
|
||||||
|
|
||||||
ADMIN_TOKEN = "";
|
|
||||||
|
|
||||||
DATABASE_URL = "postgres://localhost:5432/vaultwarden?sslmode=disable";
|
DATABASE_URL = "postgres://localhost:5432/vaultwarden?sslmode=disable";
|
||||||
|
|
||||||
WEB_VAULT_ENABLED = true;
|
WEB_VAULT_ENABLED = true;
|
||||||
|
|
@ -41,9 +115,6 @@ in
|
||||||
SSO_ORGANIZATIONS_REVOCATION = true;
|
SSO_ORGANIZATIONS_REVOCATION = true;
|
||||||
SSO_AUTHORITY = "https://auth.kruining.eu/";
|
SSO_AUTHORITY = "https://auth.kruining.eu/";
|
||||||
SSO_SCOPES = "email profile offline_access";
|
SSO_SCOPES = "email profile offline_access";
|
||||||
SSO_AUDIENCE_TRUSTED = "^333297815511892227$";
|
|
||||||
SSO_CLIENT_ID = "335178854421299459";
|
|
||||||
SSO_CLIENT_SECRET = "";
|
|
||||||
|
|
||||||
ROCKET_ADDRESS = "::1";
|
ROCKET_ADDRESS = "::1";
|
||||||
ROCKET_PORT = 8222;
|
ROCKET_PORT = 8222;
|
||||||
|
|
@ -53,10 +124,14 @@ in
|
||||||
SMTP_PORT = 587;
|
SMTP_PORT = 587;
|
||||||
SMTP_SECURITY = "starttls";
|
SMTP_SECURITY = "starttls";
|
||||||
SMTP_USERNAME = "chris@kruining.eu";
|
SMTP_USERNAME = "chris@kruining.eu";
|
||||||
SMTP_PASSWORD = "";
|
|
||||||
SMTP_FROM = "chris@kruining.eu";
|
SMTP_FROM = "chris@kruining.eu";
|
||||||
SMTP_FROM_NAME = "Chris' Vaultwarden";
|
SMTP_FROM_NAME = "Chris' Vaultwarden";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environmentFile = [
|
||||||
|
"/var/lib/zitadel/clients/nix_ulmo_vaultwarden"
|
||||||
|
config.sops.templates."vaultwarden/config.env".path
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
postgresql = {
|
postgresql = {
|
||||||
|
|
@ -89,5 +164,54 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
secrets = {
|
||||||
|
"vaultwarden/email" = {
|
||||||
|
owner = config.users.users.vaultwarden.name;
|
||||||
|
group = config.users.users.vaultwarden.name;
|
||||||
|
key = "email/chris_kruining_eu";
|
||||||
|
restartUnits = [ "vaultwarden.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
templates = {
|
||||||
|
"vaultwarden/config.env" = {
|
||||||
|
content = ''
|
||||||
|
SMTP_PASSWORD='${config.sops.placeholder."vaultwarden/email"}';
|
||||||
|
'';
|
||||||
|
owner = config.users.users.vaultwarden.name;
|
||||||
|
group = config.users.groups.vaultwarden.name;
|
||||||
|
};
|
||||||
|
temp-db-output.content =
|
||||||
|
let
|
||||||
|
config =
|
||||||
|
cfg.database
|
||||||
|
|> ({ type, ... }@db:
|
||||||
|
if type == "sqlite" then
|
||||||
|
{ inherit (db) type file; }
|
||||||
|
else if type == "postgresql" then
|
||||||
|
{
|
||||||
|
inherit (db) type;
|
||||||
|
url = lib.${namespace}.strings.toUrl {
|
||||||
|
inherit (db) protocol host port;
|
||||||
|
path = "vaultwarden";
|
||||||
|
query = {
|
||||||
|
sslmode = db.sslMode;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|> concatMapAttrsStringSep "\n" (n: v: "${toUpper n}=${v}")
|
||||||
|
;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
# GENERATED VALUES
|
||||||
|
${config}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ mkShell {
|
||||||
bash
|
bash
|
||||||
sops
|
sops
|
||||||
just
|
just
|
||||||
|
yq
|
||||||
|
pwgen
|
||||||
inputs.clan-core.packages.x86_64-linux.clan-cli
|
inputs.clan-core.packages.x86_64-linux.clan-cli
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -147,15 +147,56 @@
|
||||||
media.enable = true;
|
media.enable = true;
|
||||||
media.homer.enable = true;
|
media.homer.enable = true;
|
||||||
media.nfs.enable = true;
|
media.nfs.enable = true;
|
||||||
|
media.servarr = {
|
||||||
|
# radarr = {
|
||||||
|
# port = 2001;
|
||||||
|
# };
|
||||||
|
|
||||||
|
sonarr = {
|
||||||
|
enable = true;
|
||||||
|
# debug = true;
|
||||||
|
port = 2002;
|
||||||
|
rootFolders = [
|
||||||
|
"/var/media/series"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
lidarr = {
|
||||||
|
enable = true;
|
||||||
|
debug = true;
|
||||||
|
port = 2003;
|
||||||
|
rootFolders = [
|
||||||
|
"/var/media/music"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
prowlarr = {
|
||||||
|
enable = true;
|
||||||
|
debug = true;
|
||||||
|
port = 2004;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
observability = {
|
observability = {
|
||||||
grafana.enable = true;
|
grafana.enable = true;
|
||||||
prometheus.enable = true;
|
prometheus.enable = true;
|
||||||
loki.enable = true;
|
loki.enable = true;
|
||||||
promtail.enable = true;
|
promtail.enable = true;
|
||||||
|
# uptime-kuma.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
security.vaultwarden.enable = true;
|
security.vaultwarden = {
|
||||||
|
enable = true;
|
||||||
|
database = {
|
||||||
|
# type = "sqlite";
|
||||||
|
# file = "/var/lib/vaultwarden/state.db";
|
||||||
|
|
||||||
|
type = "postgresql";
|
||||||
|
host = "localhost";
|
||||||
|
port = 5432;
|
||||||
|
sslMode = "disabled";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
editor = {
|
editor = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue