mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:46 +01:00
services.homepage-dashboard runs the nixpkgs package with DynamicUser, replacing the :latest container, its system user and the activation script that rsynced the YAML into place. The YAML stays as is and is linked into /etc/homepage-dashboard. Assisted-by: Claude Code (claude-opus-5-5)
6.0 KiB
6.0 KiB
Immich (photo platform)
Design for the homelab photo platform on youko. Implementation lives in ./default.nix; this
document is the agreed design and the rationale.
Goal
- Browse and search the existing 36G phone gallery (
/data/fileshare/mydata/DCIM) in place, with ML (faces, objects) and duplicate detection. - Keep the footprint reasonable next to the k3s microVMs on the same host.
Non-goals (for now)
- Scheduled phone background backup (the mobile app is used for manual uploads).
- Multi-user / family accounts and public share links.
- Off-host/off-site copy (see Backup).
Decisions
- App: Immich via the nixpkgs
services.immichmodule — a native systemd service, not containers. - Database: reuse the host PostgreSQL 16 (
services.postgresql) instead of a bundled DB container. - Cache: a single shared Valkey instance (
services.redis.package = pkgs.valkey), reused by Immich and future services over loopback TCP — not a private per-service Redis, and not Dragonfly (its Lua handling breaks Immich's BullMQ job queue; see Risks). - Existing photos: exposed as a read-only Immich External Library, not imported/copied (no 36G duplication).
- ML: enable it, running on CPU initially.
Architecture
services.immich(server) on youko;host = "127.0.0.1",port = 2283.services.immich.database.enable = trueandcreateDB = true: the module addspgvector+vectorchordtoservices.postgresql, setsshared_preload_libraries = [ "vchord.so" ], and creates theimmichdatabase/user. Confirmed present in this flake's nixpkgs: PG 16.15,pgvector0.8.6,vectorchord1.1.1,immich3.2.4.services.immich.redis.enable = falsewithhost = "127.0.0.1"andport = 6379: Immich uses the shared Valkey instance (../valkey.nix) over loopback TCP; the module then passesREDIS_HOSTNAME/REDIS_PORT.services.immich.machine-learning.enable = true(CPU;accelerationDevicesleft unset — AMD iGPU ML is unreliable, revisit/dev/dri/renderD128later).- Caddy vhost
immich.writefor.fun->http://127.0.0.1:2283. Unlike the other vhosts (privateecc-ca), this one is reached by the Immich mobile app, so it gets a publicly-trusted Let's Encrypt cert viasecurity.acme(lego + Cloudflare DNS-01); see../caddy.nix. - A
homepageentry on the dashboard.
Storage
mediaLocation = "/data/apps/immich"(the encrypted HDD; ~3.5T free), created by tmpfiles asd /data/apps/immich 0750 immich immich. This holds the managed library and generated thumbnails/transcodes.- The Immich database lives in the Postgres data directory (
/var/lib/postgresql/16), i.e. under@persistent.
Photo sources and permissions
- The DCIM tree is
0700, owned bysftpgo(sftpgo:postgres-exporter). Grant theimmichuser read access with a recursive and default ACL (u:immich:rX+d:u:immich:rX) so existing and future files stay readable, without loosening the tree for everyone. - In the Immich admin UI, add an External Library pointing at the DCIM path and run a scan. Files stay read-only to Immich; albums/faces metadata is stored in the database.
Backup and disaster recovery
- Database: already covered by the existing youko restic backup, which snapshots
/btr_pool/@persistent(Postgres data lives there). - Media and existing photos: both live on the HDD already. A local HDD->HDD backup adds no protection, so none is added here. This is deferred to the planned cloud copy, which will cover the photo library.
- Note for the cloud-copy work: a raw btrfs snapshot of a running Postgres is only
crash-consistent; prefer a
pg_dumpinto the cloud set for a clean restore. That set should includemediaLocationand the external-library source. - One-time disruption: adding the extensions and
shared_preload_librariesrestarts PostgreSQL (affects theplaygroundDB only).
Monitoring
- Enabled:
IMMICH_API_METRICS_PORT=8081+IMMICH_MICROSERVICES_METRICS_PORT=8082(OTEL metrics,IMMICH_TELEMETRY_INCLUDE=all). VictoriaMetrics scrapes them on this host (monitoring/victoriametrics.nix); caddy does not proxy them. - Note: Immich has no bind-host option for these listeners, so they bind all interfaces and are
LAN-reachable like
node_exporter:9100. Add a firewall rule if that must be loopback-only.
Risks
- Immich's job queue (BullMQ) needs a Redis that tolerates Lua scripts accessing dynamically-built
keys; Dragonfly rejected these (
script tried accessing undeclared key) and every upload returned 500. We therefore run Valkey (services.redis), Redis-compatible and the supported backend.
Resource budget
- Immich server + ML worker + Valkey: roughly 2-4G RAM, alongside the k3s microVMs.
Rollout
Applied — immich runs on youko. The steps below are the procedure as deployed:
../valkey.nix: the shared Valkey instance../default.nix: theservices.immichconfiguration (server, database, machine-learning, tmpfiles).../caddy.nix: theimmich.writefor.funvhost.../homepage/services.yaml: dashboard entry.- Preview and deploy:
just youko dry-activate, thenjust youko. - Register the first user (becomes admin), disable open signup, add the external library, trigger a scan.
Validation
just fmt;just testpasses;nix buildthe youko toplevel.- Postgres:
\dxlistsvectorchord/pgvector;SHOW shared_preload_librariesincludesvchord.so; theimmichDB exists. - Units:
immich-server,immich-machine-learning,redis-shared,postgresqlare active. curl 127.0.0.1:2283andhttps://immich.writefor.funrespond; login works; the external library scan indexes the DCIM photos and ML search returns results.
Future
- iGPU ML (
/dev/dri/renderD128) if it proves reliable. immich-public-proxy/immich-kiosk/immichframefor sharing and dashboards.- Mobile apps and phone auto-backup.
pg_dumpof the Immich DB into the cloud backup set.