diff --git a/hosts/home-server/default.nix b/hosts/home-server/default.nix index e8a4eda..f3c7e0a 100644 --- a/hosts/home-server/default.nix +++ b/hosts/home-server/default.nix @@ -40,6 +40,7 @@ in ../../services/wkd.nix ../../services/home-assistant ../../services/matrix + ../../services/immich.nix ../../services/miniflux.nix ../../services/paperless.nix ../../services/nextcloud.nix diff --git a/secrets/immich/db-password.age b/secrets/immich/db-password.age new file mode 100644 index 0000000..951b3b6 Binary files /dev/null and b/secrets/immich/db-password.age differ diff --git a/secrets/immich/env.age b/secrets/immich/env.age new file mode 100644 index 0000000..718ba10 --- /dev/null +++ b/secrets/immich/env.age @@ -0,0 +1,11 @@ +age-encryption.org/v1 +-> ssh-ed25519 OAZQhA inqJ3LPwmFLYYnfuawS0lgr98XC4l+VTmsGXzPfy1VM +CHVm2Z8mVLazAQ1ymhfMqNrY/qlAYeSIsU2DwmIQca8 +-> ssh-ed25519 lJaKnA LhTLdgpRsi3BmQspSqvdnr8J/4WybyBpu9Lvhtb1jyk +XW51R8uatTy9niBELlJjkWXh3saNxRuIQVTBvCPGG9Y +-> ssh-ed25519 72ij7w 68Wzb9LtJPe7WcgViMVD1hhuki9dGmC2bFsvxxmkXmw +NH03Fu3kJop0y4XiXY1Rm7WvFHg+sWI7oJvKnYttD4k +-> @U.p1-grease 9r? @v.; 1zLdC6u +RJMgIa4ri2Dqq4S+dGTyDOA0MJlQvRcvmldt6CeweQ +--- FbkM4UZTqL5ZT0cRM1tMfYBULiV+h0YlAjC/8YdgFB8 +=,~슞0+Fx@~Ui_M2g<' +\) ?/Dseе=;>rf>@Ą9*(p@d'.B> waPމ'E<XUɴyHLRσR[.FqLyޥ ʐ \ No newline at end of file diff --git a/secrets/immich/typesense/env.age b/secrets/immich/typesense/env.age new file mode 100644 index 0000000..1e773d9 --- /dev/null +++ b/secrets/immich/typesense/env.age @@ -0,0 +1,12 @@ +age-encryption.org/v1 +-> ssh-ed25519 OAZQhA wSU2o2QZ09JlsQ9fjqh8/wLvJi30tXHlrQ6UgcYotic +o5UnLub9dUm7rVT5bcanOOQOd/+Laiqhe+CPtRLkz/o +-> ssh-ed25519 lJaKnA eOwn2JPKFv/8h/HEFUn+vnuJt8vQ0ynD7igtWUAqXkA +eGaK6kV1TK03H9RBPB2qwTQXi3XeyhHnAjg73tqghHY +-> ssh-ed25519 72ij7w 3XiCOok7DkQVm48K01F4GtHQAZrqbFFA90lPr/h4hWo +HckvW0PBNT1KJivCqfByz/H+xQJylA2y3OpKnnbdzlQ +-> %U)HsVtW-grease +5L/0ySnJDDEC+gGBhnwVD9Hy8i2Xbb3Dyj4XZZvO77c5A2wQqBEO8lLCBTcPAB7h +m9UOpo654UbPvb0KsA7J9Piw/SM2Wt3oZrBzO/BF5jotKtil5yMjGyHxGg +--- dnAuqgpzLdEXoTiv7hjOFZs2tY5u3/ILDoDJN9YjRes +z#t6Fl*]ѕ03:1F&2UǎlߢY 0kk{}&Yz \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 84ab83f..66943b6 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -67,6 +67,21 @@ in home-pc home-server ]; + "immich/env.age".publicKeys = [ + felschr + home-pc + home-server + ]; + "immich/db-password.age".publicKeys = [ + felschr + home-pc + home-server + ]; + "immich/typesense/env.age".publicKeys = [ + felschr + home-pc + home-server + ]; "firefox/site-data-exceptions.toml.age".publicKeys = [ felschr diff --git a/services/adguardhome.nix b/services/adguardhome.nix index b979727..2cbe302 100644 --- a/services/adguardhome.nix +++ b/services/adguardhome.nix @@ -46,6 +46,16 @@ in protection_enabled = true; filtering_enabled = true; safe_search.enabled = true; + rewrites = [ + { + domain = "felschr.com"; + answer = "home-server.tail05275.ts.net"; + } + { + domain = "*.felschr.com"; + answer = "home-server.tail05275.ts.net"; + } + ]; }; filters = [ { diff --git a/services/immich.nix b/services/immich.nix new file mode 100644 index 0000000..1d65243 --- /dev/null +++ b/services/immich.nix @@ -0,0 +1,187 @@ +{ config, pkgs, ... }: + +let + tag = "v1.88.2"; + dataDir = "/var/lib/immich"; + typesenseDataDir = "/var/lib/immich/typesense/data"; + uploadDir = "${dataDir}/upload"; + dbuser = "immich"; + dbname = "immich"; + dbPasswordFile = config.age.secrets.immich-db-password.path; + ociBackend = config.virtualisation.oci-containers.backend; + containersHost = "localhost"; + domain = "photos.felschr.com"; + + inherit (config.users.users.immich) uid; + inherit (config.users.groups.immich) gid; + + pgSuperUser = config.services.postgresql.superUser; + + immichBase = { + user = "${toString uid}:${toString gid}"; + environment = { + PUID = toString uid; + PGID = toString gid; + NODE_ENV = "production"; + DB_HOSTNAME = containersHost; + DB_PORT = toString config.services.postgresql.settings.port; + DB_USERNAME = dbuser; + DB_DATABASE_NAME = dbname; + REDIS_HOSTNAME = containersHost; + REDIS_PORT = toString config.services.redis.servers.immich.port; + TYPESENSE_HOST = "immich-typesense"; + }; + # only secrets need to be included, e.g. DB_PASSWORD, JWT_SECRET, MAPBOX_KEY + environmentFiles = [ + config.age.secrets.immich-env.path + config.age.secrets.immich-typesense-env.path + ]; + extraOptions = [ + "--runtime-flag=network=host" + "--uidmap=0:65534:1" + "--gidmap=0:65534:1" + "--uidmap=${toString uid}:${toString uid}:1" + "--gidmap=${toString gid}:${toString gid}:1" + "--network=host" + "--add-host=immich-server:127.0.0.1" + "--add-host=immich-microservices:127.0.0.1" + "--add-host=immich-machine-learning:127.0.0.1" + "--add-host=immich-typesense:127.0.0.1" + "--label=io.containers.autoupdate=registry" + ]; + }; +in +{ + age.secrets.immich-env.file = ../secrets/immich/env.age; + age.secrets.immich-db-password.file = ../secrets/immich/db-password.age; + age.secrets.immich-typesense-env.file = ../secrets/immich/typesense/env.age; + + services.postgresql = { + enable = true; + enableTCPIP = true; + ensureDatabases = [ dbname ]; + ensureUsers = [ + { + name = dbuser; + ensureDBOwnership = true; + } + ]; + }; + + services.redis.servers.immich = { + enable = true; + port = 31640; + }; + + systemd.services.immich-init = { + enable = true; + description = "Set up paths & database access"; + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + before = [ + "${ociBackend}-immich-server.service" + "${ociBackend}-immich-microservices.service" + "${ociBackend}-immich-machine-learning.service" + "${ociBackend}-immich-typesense.service" + ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + LoadCredential = [ "db_password:${dbPasswordFile}" ]; + }; + script = '' + mkdir -p ${dataDir} ${uploadDir} ${typesenseDataDir} + echo "Set immich postgres user password" + db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")" + ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${pkgs.postgresql}/bin/psql postgres \ + -c "alter user ${dbuser} with password '$db_password'" + ''; + }; + + virtualisation.oci-containers.containers = { + immich-server = immichBase // { + image = "ghcr.io/immich-app/immich-server:${tag}"; + ports = [ "3001:3001" ]; + entrypoint = "/bin/sh"; + cmd = [ "./start-server.sh" ]; + volumes = [ "${uploadDir}:/usr/src/app/upload" ]; + dependsOn = [ "immich-typesense" ]; + }; + + immich-microservices = immichBase // { + image = "ghcr.io/immich-app/immich-server:${tag}"; + entrypoint = "/bin/sh"; + cmd = [ "./start-microservices.sh" ]; + volumes = [ "${uploadDir}:/usr/src/app/upload" ]; + dependsOn = [ "immich-typesense" ]; + }; + + immich-machine-learning = immichBase // { + image = "ghcr.io/immich-app/immich-machine-learning:${tag}"; + volumes = [ "${uploadDir}:/usr/src/app/upload" ]; + }; + + immich-typesense = { + image = "docker.io/typesense/typesense:0.24.0"; + environment.TYPESENSE_DATA_DIR = "/data"; + environmentFiles = [ config.age.secrets.immich-typesense-env.path ]; + volumes = [ "${typesenseDataDir}:/data" ]; + extraOptions = [ + "--uidmap=0:${toString uid}:1" + "--gidmap=0:${toString gid}:1" + "--network=host" + "--label=io.containers.autoupdate=registry" + ]; + }; + }; + + systemd.services = { + "${ociBackend}-immich-server" = { + requires = [ + "postgresql.service" + "redis-immich.service" + ]; + after = [ + "postgresql.service" + "redis-immich.service" + ]; + }; + + "${ociBackend}-immich-microservices" = { + requires = [ + "postgresql.service" + "redis-immich.service" + ]; + after = [ + "postgresql.service" + "redis-immich.service" + ]; + }; + + "${ociBackend}-immich-machine-learning" = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + }; + }; + + services.nginx.virtualHosts.${domain} = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:3001"; + extraConfig = '' + client_max_body_size 50000M; + ''; + }; + }; + + users.users.immich = { + isSystemUser = true; + group = "immich"; + uid = 980; + }; + + users.groups.immich = { + gid = 977; + }; +}