Photo management with Immich

#| file: services/immich/default.nix
{
  config,
  lib,
  ...
}:
let
  cfg = config.mjm.immich;
in
{
  options.mjm.immich = {
    enable = lib.mkEnableOption "immich";
  };

  config = lib.mkIf cfg.enable {
    <<config>>
  };

  _class = "nixos";
}

Immich is a self-hosted photo management app. Every photo I take with my phone gets uploaded automatically to Immich.

Service settings

mjm.services.immich = {
  <<service-settings>>
};
#| id: service-settings
http = {
  port = config.services.immich.port;
  health.path = "/api/server/ping";

  ingress = {
    subdomain = "photos";
    authMode = "oidc";
    oidc = {
      name = "Immich";
      clientId = "w0lXREOW6t5xMV1LJLeqMALEjBpTTT86B9iH9GivjSB7UFKP0StzL5A2ahTOrvEy";
      clientSecret = "$argon2id$v=19$m=65536,t=3,p=4$Qx7s806gOeZU4Ys7kwsYnw$QPtiSJzNROTerIM+JOhfYTw/qEgZ7gu9rACW1xN446o";
      tokenEndpointAuthMethod = "client_secret_post";
      redirectUris = [
        "https://photos.midna.dev/auth/login"
        "https://photos.midna.dev/user-settings"
        "app.immich:///oauth-callback"
      ];
    };
  };
};

Immich is exposed externally at https://photos.midna.dev. It has a "ping" endpoint that Consul uses to check its health.

Immich supports OpenID Connect for logging in, which works both in the browser and from the mobile apps.

#| id: service-settings
secrets.enable = true;
postgresql.enable = true;

Immich uses PostgreSQL for its database and uses Vault to store its OIDC client secret.

Config

services.immich.enable = true;

The Immich service of course needs to be enabled.

services.immich.mediaLocation = "/mnt/immich";
microvm.shares = [
  {
    proto = "virtiofs";
    tag = "icloud-photos";
    source = "/mnt/slow/media/photos";
    mountPoint = "/mnt/icloud-photos";
  }
  {
    proto = "virtiofs";
    tag = "immich";
    source = "/mnt/slow/media/immich";
    mountPoint = "/mnt/immich";
  }
];

Immich stores its media on mirrored HDDs from my NAS. The main storage is mounted into the microVM as /mnt/immich. When I still used an iPhone, my photos would be uploaded to iCloud, and then I would run icloudpd to download them to a directory on the NAS. That directory is mounted into the microVM as /mnt/icloud-photos, and in Immich I've configured that directory as an external library location.

services.immich.database.enable = false;

This might look weird, but it just means that Immich's database is not configured automatically on the same machine: it's using the shared PostgreSQL microVM for its host. No other database configuration is needed.

services.immich.settings.server.externalDomain = "https://photos.midna.dev";

Immich needs to know the URL it's hosted at.

services.immich.settings.oauth = {
  enabled = true;
  buttonText = "Login with Authelia";
  clientId = config.mjm.services.immich.http.ingress.oidc.clientId;
  clientSecret._secret = "/run/immich-creds.sock";
  issuerUrl = "https://auth.midna.dev/";
};
mjm.spire.creds.immich.aliases."immich-server.service/1__run_immich-creds.sock" =
  "immich/managed/oidc_client_secret";

These settings configure Immich for OpenID Connect through Authelia. The handling for the client secret is jankier than I would like: it relies on knowing that the NixOS module for Immich uses utils.genJqSecretsReplacement with systemd credentials, the fact that the client secret is the first and only secret in the config, and the particular naming for the systemd credentials generated. So that's pretty brittle, but I don't have a better idea for how to do it right now.

services.immich.machine-learning.enable = false;

I'm not especially interested in Immich detecting faces or objects for me, so I disable the machine learning stuff to reduce resources.

Proxy Information
Original URL
gemini://midna.dev/homelab/services/immich/
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
92.465349 milliseconds
Gemini-to-HTML Time
0.231078 milliseconds

This content has been proxied by September (UNKNO).