jupiter had a leftover docker-compose Immich on the RAID (/mnt/data/Immich, 9.9G) that survived the NixOS install. Native module now, media at /mnt/data/AppData/immich, caddy vhost on 2283 with a 50GB body limit (caddy's default rejects video uploads). The package comes from nixpkgs-unstable, the module from the 26.05 pin: 26.05 ships immich 2.7.5, but that database was last written by 3.0.0 and migrations only run forward -- corrupted migrations: previously executed migration 1776217577402-DropAuditTable is missing Safe because the two module files are byte-identical at these revisions; services/media/immich.nix carries the diff command to re-check on a bump. Drop the input once the stable pin ships >= 3.0.0. immich needs group "users" only to traverse /mnt/data/AppData (drwx--x---); its own dir stays 0700 immich:immich. mediaLocation is outside /var/lib, so the module's tmpfiles entry only ADJUSTS it -- add a rule that creates it. scripts/immich-import-legacy-db does the database half: boots a copy of the legacy PGDATA under the matching image (PG14 + vchord 0.3.0 + pgvector 0.8.1), dumps it with the local pg_dump 17, restores into a scratch DB, fixes ownership, and only swaps after confirmation. Never touches the original. The old cluster ran VectorChord, not pgvecto.rs, so the smart search and face embeddings survive -- no ML re-run. Imported: 666 assets, 25 people, 647 clip + 359 face embeddings, 2 users.
63 lines
3.0 KiB
Nix
63 lines
3.0 KiB
Nix
{ pkgs, inputs, ... }:
|
|
|
|
# Immich photo/video library. Native nixpkgs module (not the upstream compose
|
|
# stack) — it owns its own postgres (with the pgvector + vectorchord extensions
|
|
# it needs for search) and a unix-socket redis, so nothing else is required here.
|
|
#
|
|
# Storage: everything lives under /mnt/data/AppData/immich, which is the media
|
|
# store MIGRATED from the old ZimaOS/CasaOS install's UPLOAD_LOCATION
|
|
# (/mnt/data/Immich/upload — same layout: library/ upload/ thumbs/
|
|
# encoded-video/ profile/ backups/). See scripts/immich-import-legacy-db for the
|
|
# matching database import. The postgres cluster itself stays on the OS disk.
|
|
#
|
|
# ⚠️ The immich DB is the only copy of albums/faces/dates — the files alone
|
|
# can't rebuild it. It joins the other unbacked databases on this network.
|
|
let
|
|
# The PACKAGE comes from nixpkgs-unstable (3.0.3); the MODULE comes from the
|
|
# 26.05 pin (which ships 2.7.5). That combination is safe because the two
|
|
# module files are byte-identical — verified by diffing them at the revisions
|
|
# in flake.lock. RE-CHECK THAT DIFF on any input bump:
|
|
# diff <(nixpkgs)/nixos/modules/services/web-apps/immich.nix \
|
|
# <(unstable)/nixos/modules/services/web-apps/immich.nix
|
|
#
|
|
# Why: jupiter's imported database was last written by immich 3.0.0, and
|
|
# immich runs its migrations forward only — 2.7.5 refuses to start against it
|
|
# with "corrupted migrations: previously executed migration
|
|
# 1776217577402-DropAuditTable is missing". Drop this override once nixos-26.11
|
|
# (or whatever the pin becomes) ships >= 3.0.0.
|
|
unstable = import inputs.nixpkgs-unstable {
|
|
inherit (pkgs.stdenv.hostPlatform) system;
|
|
};
|
|
in
|
|
{
|
|
services.immich = {
|
|
enable = true;
|
|
# Both the server and immich-machine-learning follow this: the module takes
|
|
# the ML service from cfg.package.machine-learning (passthru).
|
|
package = unstable.immich;
|
|
# Listens on all interfaces: :2283 stays closed on the LAN (no
|
|
# openFirewall), reachable over tailscale0 and via localhost (caddy).
|
|
host = "0.0.0.0";
|
|
port = 2283;
|
|
mediaLocation = "/mnt/data/AppData/immich";
|
|
machine-learning.enable = true;
|
|
# Hardware transcoding would need the iGPU passed in explicitly, e.g.
|
|
# accelerationDevices = [ "/dev/dri/renderD128" ]; the default [ ] means
|
|
# PrivateDevices=yes and CPU-only transcode. The ZimaBlade's Celeron does
|
|
# this slowly but it only runs on upload.
|
|
};
|
|
|
|
# /mnt/data/AppData is drwx--x--- darman:users — immich needs group "users"
|
|
# just to TRAVERSE into its own media dir. The dir itself stays 0700
|
|
# immich:immich (the module's tmpfiles rule re-asserts that every rebuild,
|
|
# and UMask=0077 keeps new files private), so this grants nothing else.
|
|
users.users.immich.extraGroups = [ "users" ];
|
|
|
|
# mediaLocation is outside /var/lib, so the module won't create it — its own
|
|
# tmpfiles entry only ADJUSTS an existing dir. Harmless no-op after the
|
|
# legacy import, which puts the real store here.
|
|
systemd.tmpfiles.rules = [
|
|
"d /mnt/data/AppData/immich 0700 immich immich -"
|
|
];
|
|
}
|