{ pkgs, lib, ... }: let immich_version = "release"; storage_dir = "/mnt/storage/syncthing"; immich_data_dir = "/mnt/storage/immich"; volumeServices = names: ( lib.lists.foldl (services: name: services // { "podman-volume-immich_${name}" = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' podman volume inspect immich_${name} || podman volume create immich_${name} ''; partOf = [ "podman-compose-immich-root.target" ]; wantedBy = [ "podman-compose-immich-root.target" ]; }; } ) {} names ); containerServices = services: ( lib.lists.foldl (acc: { name, volumes ? [], ... }: let volume_services = map (volume: "podman-volume-immich_${volume}.service") volumes; dependent_services = [ "podman-network-immich_default.service" ] ++ volume_services; in acc // { "podman-immich_${name}" = { serviceConfig = { Restart = lib.mkOverride 500 "always"; }; after = dependent_services; requires = dependent_services; partOf = [ "podman-compose-immich-root.target" ]; wantedBy = [ "podman-compose-immich-root.target" ]; }; } ) {} services ); in { # Containers virtualisation.oci-containers.containers = { "immich_machine_learning" = { image = "ghcr.io/immich-app/immich-machine-learning:${immich_version}"; environmentFiles = [ "/var/secrets/immich.env" ]; volumes = [ "immich_model_cache:/cache:rw" ]; log-driver = "journald"; extraOptions = [ "--network-alias=immich-machine-learning" "--network=immich_default" ]; }; "immich_microservices" = { image = "ghcr.io/immich-app/immich-server:${immich_version}"; environmentFiles = [ "/var/secrets/immich.env" ]; volumes = [ "/etc/localtime:/etc/localtime:ro" "${immich_data_dir}:/usr/src/app/upload:rw" "${storage_dir}:${storage_dir}:ro" ]; cmd = [ "start.sh" "microservices" ]; dependsOn = [ "immich_postgres" "immich_redis" ]; log-driver = "journald"; extraOptions = [ "--network-alias=immich-microservices" "--network=immich_default" ]; }; "immich_postgres" = { image = "registry.hub.docker.com/tensorchord/pgvecto-rs:pg14-v0.2.0"; environmentFiles = [ "/var/secrets/immich.env" ]; environment = { POSTGRES_INITDB_ARGS = "--data-checksums"; }; volumes = [ "immich_db_data:/var/lib/postgresql/data:rw" ]; cmd = [ "postgres" "-c" "shared_preload_libraries=vectors.so" "-c" "search_path=\"$user\", public, vectors" "-c" "logging_collector=on" "-c" "max_wal_size=2GB" "-c" "shared_buffers=512MB" "-c" "wal_compression=on" ]; log-driver = "journald"; extraOptions = [ "--network-alias=database" "--network=immich_default" ]; }; "immich_redis" = { image = "registry.hub.docker.com/library/redis:6.2-alpine"; environmentFiles = [ "/var/secrets/immich.env" ]; log-driver = "journald"; extraOptions = [ "--network-alias=redis" "--network=immich_default" ]; }; "immich_server" = { image = "ghcr.io/immich-app/immich-server:${immich_version}"; environmentFiles = [ "/var/secrets/immich.env" ]; volumes = [ "/etc/localtime:/etc/localtime:ro" "${immich_data_dir}:/usr/src/app/upload:rw" "${storage_dir}:${storage_dir}:ro" ]; 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 = (containerServices [ { name = "machine_learning"; volumes = [ "model_cache" ]; } { name = "postgres"; volumes = [ "db_data" ]; } { name = "redis"; } { name = "server"; } { name = "microservices"; } ]) // { # Networks "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 ''; partOf = [ "podman-compose-immich-root.target" ]; wantedBy = [ "podman-compose-immich-root.target" ]; }; } // (volumeServices [ "db_data" "model_cache" ]); # 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" ]; }; services.caddy.virtualHosts = { "img.freun.dev".extraConfig = '' reverse_proxy localhost:2283 ''; }; fileSystems."${immich_data_dir}" = { device = "//u407959.your-storagebox.de/backup/immich"; fsType = "cifs"; options = let # this line prevents hanging on network split automount_opts = "x-systemd.automount,auto,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s"; in ["${automount_opts},credentials=/var/secrets/smb-storage"]; }; environment.systemPackages = [ pkgs.cifs-utils ]; }