Immich
- Source: https://github.com/immich-app/immich
- License: AGPL-3
- Alternatives: PhotoPrism, Ente Photos, LibrePhotos, Photoview, Nextcloud Photos
Getting Started
Immich is a complementary service: nothing else in the stack talks to it, so it can run on another host. On this one it starts with everything else.
Containers
| Container | Role |
|---|---|
immich-server | API, plus the "microservices" background-job workers (thumbnails, EXIF, transcode) — one combined Node process |
immich-machine-learning | CLIP smart search, facial recognition, OCR — a separate FastAPI process the server calls per job |
immich-postgres | Postgres + the vchord vector extension (ghcr.io/immich-app/postgres:*-vectorchord*) — a stock pgvector image cannot serve immich's indexes |
immich-redis | Job queue (imports, thumbnails) — Valkey, no persisted data |
immich-machine-learning failing is invisible from immich-server's own health: the API keeps
answering /api/server/ping and jobs that need the ML worker just fail one at a time with
"fetch failed" in the server's logs. ./existential.sh run immich test probes it directly.
GPU acceleration
immich-machine-learning ships a different image tag per backend (unlike ollama/whisperx,
there's no single binary that runtime-detects the card), wired the same way as every GPU
service in this stack via EXIST_GPU_VENDOR. The nvidia
-cuda tag is the default; a non-nvidia EXIST_GPU_VENDOR swaps in the -rocm tag (AMD, group
- device passthrough — no
-cuda-sized download you can't use) or the plain CPU tag (none). Image sizes, for context: CPU 1.85 GB,-cuda7.71 GB,-rocm31.7 GB.
Hardware-accelerated video transcoding (immich-server, not the ML worker) is a separate,
manual opt-in — see the commented extends: hwaccel.transcoding.yml block in
docker-compose.exist.yml. It isn't vendor-wired: NVENC/VAAPI need a different capability set
than the ML reservation, and software transcoding already works with no extra setup.
Access
https://immich.<domain> (Caddy) or http://immich-server:2283 on the exist network.
immich-machine-learning is internal only — no Caddy hostname.
Notes
Version. IMMICH_VERSION in .env defaults to v3, matching upstream's own current
default. The server only talks to the matching major version of the mobile app — don't jump
this ahead of what your phones have installed, and see
immich's upgrade guide before crossing a major
version on an existing install (a fresh install has no such constraint).
Storage is declared like every other service. The photo library is the immich_data
volume and the Postgres data directory is immich_pg_data, both in the compose file's
x-exist-volumes block, both under volumes/. immich_data is marked nfs: true, so with a NAS
(Storage) turning Immich on asks for its folder there
(EXIST_NFS_PATH_IMMICH_DATA), and Compose mounts it.
immich_pg_data is marked db: true, which keeps it off NFS; Immich upstream does not support
a network share for the database, and marking a volume both db and nfs is a hard error in
./existential.sh validate conventions rather than a comment nobody reads.
This used to be two raw host paths (UPLOAD_LOCATION, DB_DATA_LOCATION) set in .env, with
the database living in a second volume root called volumes_local/. Those keys no longer do
anything. If you are upgrading, exist.initial.sh detects data at the old paths and stops the
run with the two mv commands to run — nothing is moved for you, because a script that
relocates a photo library unattended is a script that can lose one.
Photos you already have are the immich_share volume, mounted read-only at
/mnt/media/external_photos. With a NAS, turning Immich on asks for that folder
(EXIST_NFS_SHARE_IMMICH); without one it is volumes/immich_share. The mount is read-only,
so the NAS folder only has to be readable by EXIST_PUID:EXIST_PGID.
Setup is automatic with the photos flow (./existential.sh flow on photos). Its
immich_setup migration creates the admin from EXIST_EMAIL and EXIST_PASSWORD, skips
Immich's onboarding wizard, and adds /mnt/media/external_photos as an External Library, which
Immich rescans nightly. An Immich that already has an admin is left alone. Turned on with
service on instead, Immich opens on its sign-up page and the library is added by hand under
Administration → External Libraries.
Backup. Copy immich-db-backup-{nightly,weekly}.md and
immich-volume-backup-{nightly,weekly}.md from services/automation-backup/cron.example/
into cron/ and restart automation-backup. Both reach the data because automation-backup
mounts volumes/ wholesale, and that is where both volumes live. A library on the NAS is not
under volumes/: the volume backup skips it, and the NAS's own snapshots or replication are
its backup.
Containers run as the host user (EXIST_PUID/EXIST_PGID), including immich-server and
immich-machine-learning — both images run root by default but need no capability it grants;
this was verified end to end (upload → thumbnail → EXIF → CLIP → face pipeline) as a non-root
user against a freshly-created library directory.