diff --git a/hosts/elisabeth/guests.nix b/hosts/elisabeth/guests.nix index 4291515..c032678 100644 --- a/hosts/elisabeth/guests.nix +++ b/hosts/elisabeth/guests.nix @@ -12,6 +12,7 @@ giteadomain = "git.${config.secrets.secrets.global.domains.web}"; vaultwardendomain = "pw.${config.secrets.secrets.global.domains.web}"; paperlessdomain = "ppl.${config.secrets.secrets.global.domains.web}"; + immichdomain = "immich.${config.secrets.secrets.global.domains.web}"; ipOf = hostName: lib.net.cidr.host config.secrets.secrets.global.net.ips."${config.guests.${hostName}.nodeName}" config.secrets.secrets.global.net.privateSubnet; in { services.nginx = { @@ -58,6 +59,27 @@ in { ''; }; + upstreams.immich = { + servers."${ipOf "immich"}:3000" = {}; + + extraConfig = '' + zone gitea 64k ; + keepalive 5 ; + ''; + }; + virtualHosts.${immichdomain} = { + forceSSL = true; + useACMEHost = "web"; + locations."/" = { + proxyPass = "http://immich"; + proxyWebsockets = true; + }; + extraConfig = '' + client_max_body_size 1G ; + deny all + ''; + }; + upstreams.adguardhome = { servers."${ipOf "adguardhome"}:3000" = {}; @@ -211,6 +233,9 @@ in { // mkContainer "gitea" { enablePanzer = true; } + // mkContainer "immich" { + enablePanzer = true; + } // mkContainer "samba" { enablePanzer = true; enableRenaultFT = true; diff --git a/hosts/elisabeth/secrets/immich/generated/postgrespasswd.age b/hosts/elisabeth/secrets/immich/generated/postgrespasswd.age new file mode 100644 index 0000000..a33a2ca --- /dev/null +++ b/hosts/elisabeth/secrets/immich/generated/postgrespasswd.age @@ -0,0 +1,18 @@ +age-encryption.org/v1 +-> X25519 uQckpLpQc6L2KMOBzjC3lgcRAmXtNHERJBcD33tM0jI +qc++8oENAWvSYUhsNvbSefoJ6rd56WRlApfRgvHRuFg +-> piv-p256 XTQkUA AjIw8UqwQZEUr/EPpT8CWI2P5mFjo8fepPh80tEGF/Yr +4LhH/+lZDxTk/V8F+uWu3652JwTd7YkEbuLj20VkNJQ +-> piv-p256 ZFgiIw A6gCOLgGeT61yRRZisfi6v0wvmrAz2vza52E/JTviyQO +sezvuVxnqor+WwIJYHASFDJnPquOe43gZb3gKulAEzA +-> piv-p256 5vmPtQ A90ZF04slB5oVa1wT5gQgyHJMJBws/pC5D5JK3vvUs4s +hHhmKV7dfhc/wTr910/viLlljou5zhG/xKDyetFBq0Y +-> piv-p256 ZFgiIw AjA3IHUjqd5f2oazyGV2X5QIVGyRLdtTqmGvkRP2MEjz +oO++dTfOmkLU8xyTY70mvOnX3V3eWZvJtKLzwYKE2w8 +-> a1e"-grease 6fF8K3QU +UnlhF8+Uqi3r08EtXEFJOooq0qKpKXj5lKkBx5UdIfRzL/hQ3Fa3FOrqnv7c9kbl +ORCZ0XkX3oIiOW7AkJ4kQGwD +--- 4D7N/t7MmD+b0jtnfChSdW5yo9OoO/djgvVNL2ulhIQ +$&1+vi\τb%/>";mS+q +ͼ-YN +0vî[ÎLe9Z! \ No newline at end of file diff --git a/hosts/elisabeth/secrets/immich/generated/redispasswd.age b/hosts/elisabeth/secrets/immich/generated/redispasswd.age new file mode 100644 index 0000000..c9ce007 --- /dev/null +++ b/hosts/elisabeth/secrets/immich/generated/redispasswd.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> X25519 BkYxjAbKQaQgVcGvDxk/ri/zZM0sS0FMqkfpV4RfvQY +E/uSI6TbDna6eUBrxCMkn60OdG6PKbOKU2z4AhiOGG8 +-> piv-p256 XTQkUA Agg7UC1VD2/tVEos1xf01dYoEU7G/MDawQAPKKLV1kkd +Eiq7oNJb24i8WVimVKmZJT7d4Y7xLun7Bd0nFLx+y6c +-> piv-p256 ZFgiIw AkOsNsFUAc7ivk7Oy0p9B6RbLgnbYQBC7LxVCOhSwpW+ +eSv3RwntC/YPXYvYfQu/YLNv5i82NN9I9mBO+O+4Dlk +-> piv-p256 5vmPtQ A82tmdg/DaheCyDtiPgoMJC47fmVRPdjqmyDgqY3QorA +R/r5HhVSxgpHiCsvvZFadcQMGkk1ivygf+Q6y00/+gA +-> piv-p256 ZFgiIw A+q6l6gnwWQweNUvUDnf3jQ0gbO1543NULl8Ht24u7ZH +p/TX92gED+6O2jo9O7g1p+VSYoVTVjbjtMD7vQ5LtnA +-> #b!8--grease +JO/hQWKVdfndshTCfe0SnxADKqm/6htoeyivmK60hiXXqZJcrhGnNF4aoY2TIYQ/ +Uuu4WUWr0zggK0iSMqrcA8yp7Tt6KFbQ8c2JVq1KcecsWbRBXSHFdvOh +--- RwweJRo9SY18ZvLtGpOE7Mh3rWElaSJe1kepjAYn7OQ +(qӂjcw78ÇUWn]G_/aTOwUem&n%;tǡRIQ GV \ No newline at end of file diff --git a/modules/services/immich.nix b/modules/services/immich.nix new file mode 100644 index 0000000..75ec25c --- /dev/null +++ b/modules/services/immich.nix @@ -0,0 +1,192 @@ +# Auto-generated using compose2nix v0.1.6. +{ + pkgs, + config, + ... +}: let + version = "v1.93.3"; + environment = { + DB_DATABASE_NAME = "immich"; + DB_HOSTNAME = "immich_postgres"; + DB_PASSWORD_FILE = config.age.secrets.postgrespasswd.path; + DB_USERNAME = "postgres"; + IMMICH_VERSION = "${version}"; + REDIS_HOSTNAME = "immich_redis"; + REDIS_PASSWORD_FILE = config.age.secrets.redispasswd.path; + UPLOAD_LOCATION = upload_folder; + }; + upload_folder = "/panzer/immich"; + pgdata_folder = "/state/immich/pgdata"; + model_folder = "/state/immich/modeldata"; + + serviceConfig = { + serviceConfig = { + Restart = "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; +in { + networking.firewall.allowedTCPPorts = [2283]; + systemd.tmpfiles.settings = { + "10-immich" = { + ${upload_folder}.d = { + mode = "0770"; + }; + ${pgdata_folder}.d = { + mode = "0770"; + }; + ${model_folder}.d = { + mode = "0770"; + }; + }; + }; + age.secrets.postgrespasswd = { + generator.script = "alnum"; + }; + age.secrets.redispasswd = { + generator.script = "alnum"; + }; + # Runtime + virtualisation.podman = { + enable = true; + autoPrune.enable = true; + dockerCompat = true; + defaultNetwork.settings = { + # Required for container networking to be able to use names. + dns_enabled = true; + }; + }; + virtualisation.oci-containers.backend = "podman"; + + # Containers + virtualisation.oci-containers.containers."immich_machine_learning" = { + image = "ghcr.io/immich-app/immich-machine-learning:${version}"; + inherit environment; + volumes = [ + "${model_folder}:/cache:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-machine-learning" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_machine_learning" = serviceConfig; + virtualisation.oci-containers.containers."immich_microservices" = { + image = "ghcr.io/immich-app/immich-server:${version}"; + inherit environment; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "${upload_folder}:/usr/src/app/upload:rw" + ]; + cmd = ["start.sh" "microservices"]; + dependsOn = [ + "immich_postgres" + "immich_redis" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-microservices" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_microservices" = + serviceConfig + // { + unitConfig.UpheldBy = [ + "podman-immich_postgres.service" + "podman-immich_redis.service" + ]; + }; + virtualisation.oci-containers.containers."immich_postgres" = { + image = "tensorchord/pgvecto-rs:pg14-v0.1.11@sha256:0335a1a22f8c5dd1b697f14f079934f5152eaaa216c09b61e293be285491f8ee"; + environment = { + POSTGRES_DB = environment.DB_DATABASE_NAME; + POSTGRES_PASSWORD_FILE = environment.DB_PASSWORD_FILE; + POSTGRES_USER = environment.DB_USERNAME; + }; + volumes = [ + "${pgdata_folder}:/var/lib/postgresql/data:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=database" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_postgres" = serviceConfig; + virtualisation.oci-containers.containers."immich_redis" = { + image = "redis:6.2-alpine@sha256:c5a607fb6e1bb15d32bbcf14db22787d19e428d59e31a5da67511b49bb0f1ccc"; + log-driver = "journald"; + extraOptions = [ + "--network-alias=redis" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_redis" = serviceConfig; + virtualisation.oci-containers.containers."immich_server" = { + image = "ghcr.io/immich-app/immich-server:${version}"; + inherit environment; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "${upload_folder}:/usr/src/app/upload:rw" + ]; + ports = [ + "2283:3001/tcp" + ]; + cmd = ["start.sh" "immich"]; + dependsOn = [ + "immich_postgres" + "immich_redis" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-server" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_server" = + serviceConfig + // { + unitConfig.UpheldBy = [ + "podman-immich_postgres.service" + "podman-immich_redis.service" + ]; + }; + + # Networks + systemd.services."podman-network-immich-default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "${pkgs.podman}/bin/podman network rm -f immich-default"; + }; + script = '' + podman network inspect immich-default || podman network create immich-default --opt isolate=true + ''; + partOf = ["podman-compose-immich-root.target"]; + wantedBy = ["podman-compose-immich-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-immich-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; +} diff --git a/modules/services/paperless.nix b/modules/services/paperless.nix index 41fd77b..c40d516 100644 --- a/modules/services/paperless.nix +++ b/modules/services/paperless.nix @@ -20,7 +20,7 @@ in { }; services.restic.backups = { main = { - inherit (config.services.paperless) user; + user = "root"; timerConfig = { OnCalendar = "06:00"; Persistent = true; @@ -58,6 +58,7 @@ in { }; inherit (cfg) environment; requiredBy = ["restic-backups-main.service"]; + before = ["restic-backups-main.service"]; }; networking.firewall.allowedTCPPorts = [3000]; @@ -101,4 +102,12 @@ in { mode = "0750"; } ]; + environment.persistence."/state".directories = [ + { + directory = paperlessBackupDir; + user = "paperless"; + group = "paperless"; + mode = "0770"; + } + ]; } diff --git a/modules/services/vaultwarden.nix b/modules/services/vaultwarden.nix index dd8c3e9..f281847 100644 --- a/modules/services/vaultwarden.nix +++ b/modules/services/vaultwarden.nix @@ -28,7 +28,7 @@ in { }; services.restic.backups = { main = { - user = "vaultwarden"; + user = "root"; timerConfig = { OnCalendar = "06:00"; Persistent = true; @@ -86,4 +86,12 @@ in { StateDirectory = lib.mkForce "vaultwarden"; RestartSec = "600"; # Retry every 10 minutes }; + environment.persistence."/state".directories = [ + { + directory = config.services.vaultwarden.backupDir; + user = "vaultwarden"; + group = "vaultwarden"; + mode = "0770"; + } + ]; } diff --git a/secrets/secrets.nix.age b/secrets/secrets.nix.age index 93dd8b3..d4e15b2 100644 Binary files a/secrets/secrets.nix.age and b/secrets/secrets.nix.age differ diff --git a/ßa b/ßa new file mode 100644 index 0000000..a3ab360 --- /dev/null +++ b/ßa @@ -0,0 +1,239 @@ +# Auto-generated using compose2nix v0.1.6. +{ + pkgs, + lib, + config, + ... +}: let + version = "v1.93.3"; + environment = { + DB_DATABASE_NAME = "immich"; + DB_HOSTNAME = "immich_postgres"; + DB_PASSWORD_FILE = config.age.secrets.postgrespasswd.path; + DB_USERNAME = "postgres"; + IMMICH_VERSION = "${version}"; + REDIS_HOSTNAME = "immich_redis"; + REDIS_PASSWORD_FILE = config.age.secrets.redispasswd.path; + UPLOAD_LOCATION = "./library"; + }; + upload_folder = "/panzer/immich"; +in { + networking.firewall.openTCPPorts = [ 2283]; + systemd.tmpfiles.settings = { + "10-immich".${upload_folder}.d = { + mode = "0770"; + }; + }; + age.secrets.postgrespasswd = { + generator.script = "alnum"; + }; + age.secrets.redispasswd = { + generator.script = "alnum"; + }; + # Runtime + virtualisation.podman = { + enable = true; + autoPrune.enable = true; + dockerCompat = true; + defaultNetwork.settings = { + # Required for container networking to be able to use names. + dns_enabled = true; + }; + }; + virtualisation.oci-containers.backend = "podman"; + + # Containers + virtualisation.oci-containers.containers."immich_machine_learning" = { + image = "ghcr.io/immich-app/immich-machine-learning:${version}"; + inherit environment; + volumes = [ + "model-cache:/cache:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-machine-learning" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_machine_learning" = { + serviceConfig = { + Restart = lib.mkOverride 500 "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_microservices" = { + image = "ghcr.io/immich-app/immich-server:${version}"; + inherit environment; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "${upload_folder}:/usr/src/app/upload:rw" + ]; + cmd = ["start.sh" "microservices"]; + dependsOn = [ + "immich_postgres" + "immich_redis" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-microservices" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_microservices" = { + serviceConfig = { + Restart = lib.mkOverride 500 "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + unitConfig.UpheldBy = [ + "podman-immich_postgres.service" + "podman-immich_redis.service" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_postgres" = { + image = "tensorchord/pgvecto-rs:pg14-v0.1.11@sha256:0335a1a22f8c5dd1b697f14f079934f5152eaaa216c09b61e293be285491f8ee"; + environment = { + POSTGRES_DB = environment.DB_DATABASE_NAME; + POSTGRES_PASSWORD_FILE = environment.DB_PASSWORD_FILE; + POSTGRES_USER = environment.DB_USERNAME; + }; + volumes = [ + "pgdata:/var/lib/postgresql/data:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=database" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_postgres" = { + serviceConfig = { + Restart = lib.mkOverride 500 "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_redis" = { + image = "redis:6.2-alpine@sha256:c5a607fb6e1bb15d32bbcf14db22787d19e428d59e31a5da67511b49bb0f1ccc"; + log-driver = "journald"; + extraOptions = [ + "--network-alias=redis" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_redis" = { + serviceConfig = { + Restart = lib.mkOverride 500 "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_server" = { + image = "ghcr.io/immich-app/immich-server:${version}"; + inherit environment; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "${upload_folder}:/usr/src/app/upload:rw" + ]; + ports = [ + "2283:3001/tcp" + ]; + cmd = ["start.sh" "immich"]; + dependsOn = [ + "immich_postgres" + "immich_redis" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-server" + "--network=immich-default" + ]; + }; + systemd.services."podman-immich_server" = { + serviceConfig = { + Restart = lib.mkOverride 500 "always"; + }; + after = [ + "podman-network-immich-default.service" + ]; + requires = [ + "podman-network-immich-default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + unitConfig.UpheldBy = [ + "podman-immich_postgres.service" + "podman-immich_redis.service" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-immich-default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "${pkgs.podman}/bin/podman network rm -f immich-default"; + }; + script = '' + podman network inspect immich-default || podman network create immich-default --opt isolate=true + ''; + partOf = ["podman-compose-immich-root.target"]; + wantedBy = ["podman-compose-immich-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-immich-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; +}