From 595c20a0a133e49eb37fe87a869d8cd22e2c4b38 Mon Sep 17 00:00:00 2001 From: Goyban Date: Wed, 23 Sep 2026 15:12:08 +0200 Subject: [PATCH] added Immich --- README.md | 2 + immich/.env.example | 40 +++++++++++++++ immich/README.md | 121 ++++++++++++++++++++++++++++++++++++++++++++ immich/compose.yaml | 76 ++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 immich/.env.example create mode 100644 immich/README.md create mode 100644 immich/compose.yaml diff --git a/README.md b/README.md index facd3b9..c626a27 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ handled separately — see [Exposing services](#exposing-services). | [gluetun](gluetun/) | 9880 | VPN kill switch with qBittorrent + pyload-ng inside it, plus HTTP/SOCKS proxies | | [heimdall](heimdall/) | 4080 | Dashboard / start page for the other services | | [httpserver](httpserver/) | 8083 | Bare nginx file share. Plain HTTP on purpose — works with very old clients | +| [immich](immich/) | 2283 | Photo and video library with local ML search. Four containers, Postgres + VectorChord | +| [librespeed](librespeed/) | 3011 | Self-hosted speed test. Measures the path to your box, not your ISP | | [jellyfin](jellyfin/) | 8096 | Media streaming. Also 7359/udp for auto-discovery | | [openwebui](openwebui/) | 3033 | LLM chat UI. One `OWUI_TAG` switches between CPU, CUDA and bundled-Ollama | | [overleaf](overleaf/) | 8020 | LaTeX editor. Four containers: app, Mongo (replica set), Redis, init | diff --git a/immich/.env.example b/immich/.env.example new file mode 100644 index 0000000..7166fcc --- /dev/null +++ b/immich/.env.example @@ -0,0 +1,40 @@ +# Copy to .env and adjust. .env is git-ignored; this file is not. +# +# Immich is the one stack here where .env is NOT optional: compose.yaml +# has `env_file: .env` on two services, so Compose refuses to start +# without the file existing. It is also the application's own config, not +# just a few variables Compose interpolates. +# +# This mirrors upstream's example.env for the pinned release. When you +# update compose.yaml, re-fetch this too: +# curl -fLO https://github.com/immich-app/immich/releases/latest/download/example.env + +# ─── Storage ──────────────────────────────────────────────────────── +# Your photos and videos. Mounted at /data inside the container -- do not +# change the container side, only this. This is the directory to back up. +UPLOAD_LOCATION=./library + +# The Postgres data directory. Network shares (NFS/SMB) are NOT supported +# here and will corrupt the database; it must be local storage. +DB_DATA_LOCATION=./postgres + +# ─── Version ──────────────────────────────────────────────────────── +# Pin the MAJOR. Immich ships breaking changes between majors and the +# compose file changes with them, so leaving this unset (which resolves +# to `release`) means an unattended `docker compose pull` can jump a +# major and leave your compose file behind. See the README. +IMMICH_VERSION=v3 + +# ─── Database ─────────────────────────────────────────────────────── +# Change this. Letters and digits only -- no special characters or +# spaces, they break the connection string. +# openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 32 +DB_PASSWORD= + +# ─── Optional ─────────────────────────────────────────────────────── +# Uncomment and set to a tz identifier if the server clock isn't right. +#TZ=Europe/Paris + +# ─── Leave these alone ────────────────────────────────────────────── +DB_USERNAME=postgres +DB_DATABASE_NAME=immich diff --git a/immich/README.md b/immich/README.md new file mode 100644 index 0000000..2a86194 --- /dev/null +++ b/immich/README.md @@ -0,0 +1,121 @@ +# Immich + +Self-hosted photo and video library. Phone backup that runs on your own disk, +with the parts that make Google Photos worth using — face and object search, +places, memories, shared albums — done locally by a machine-learning container. + +The heaviest stack in this repo: four containers, a Postgres with vector +extensions, and a model cache. Budget a few GB of RAM and expect the first +import to work the CPU hard. + +## Quick start + +```bash +cp .env.example .env +$EDITOR .env # UPLOAD_LOCATION, DB_DATA_LOCATION, DB_PASSWORD +docker compose up -d +``` + +Open `http://:2283` and register. **The first account created is the +admin** — there is no separate bootstrap step, so do this before the port is +reachable by anyone else. + +## Ports + +| Port | Proto | Purpose | +|------|-------|---------| +| 2283 | http | Web UI, mobile app API, everything | + +Postgres and Valkey are reachable only on the stack's internal network. + +## Why it looks like this + +**This file is upstream's, verbatim, and should stay that way.** Immich ships a +`docker-compose.yml` with each release, pins two of its images *by digest*, and +changes the file between versions — the mount path, the Postgres extension +build, added `shm_size`. Local edits turn every update into a three-way merge. +The repo convention of `${VAR:-default}` is deliberately **not** applied here for +the same reason: keeping it byte-identical to upstream means you can re-fetch and +`diff` to see exactly what a release changed. + +```bash +curl -fLo /tmp/immich-new.yml \ + https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml +diff -u compose.yaml /tmp/immich-new.yml +``` + +Pinned at **v3.2.2** (released 2026-09-15). + +**Four containers.** `immich-server` is the API and web UI. `immich-machine-learning` +runs the CLIP and face-detection models — it is separate because it is the part +you might want on a GPU, and because its model cache is a named volume rather +than something you back up. `database` is Postgres with **VectorChord**, which +is what makes similarity search possible; a stock Postgres image will not work. +`redis` is Valkey, the Redis fork, used as the job queue. + +**`env_file: .env` is a hard dependency.** Unlike every other stack here, where +`.env` is optional because the compose file carries defaults, Compose will refuse +to start this one if the file does not exist. + +**Images are digest-pinned** for Valkey and Postgres +(`@sha256:…`), so those two are byte-identical to what upstream tested against +this release. The Immich images themselves float on `${IMMICH_VERSION:-release}` +— which is why pinning `IMMICH_VERSION` in `.env` matters. + +## Gotchas + +- **Pin `IMMICH_VERSION` to a major.** Unset, it resolves to `release`, so a + routine `docker compose pull` can carry you across a major version while your + compose file stays behind. Upstream's own `example.env` now sets + `IMMICH_VERSION=v3` for exactly this reason. +- **The container media path changed, and it is easy to miss.** Older compose + files mount `${UPLOAD_LOCATION}:/usr/src/app/upload`; since **v1.140.0** it is + `${UPLOAD_LOCATION}:/data`, because `IMMICH_MEDIA_LOCATION` now defaults to + `/data`. Run a new image against an old compose file and the server looks for + your library at an unmounted path — it appears empty, and anything written + goes into the container and is lost when it is recreated. If you are adopting + this file over an older one, that mount line is the one to check first. +- **`DB_DATA_LOCATION` must be local storage.** Not NFS, not SMB, not a + network share of any kind. Postgres on a network filesystem corrupts, and + upstream says so in the example env. +- **Upgrading Postgres extensions is a real migration.** The image here is + `14-vectorchord0.4.3-pgvectors0.2.0`; moving from an older VectorChord build + makes Immich alter the database on first boot, which "can take seconds to + minutes". Back up `DB_DATA_LOCATION` before any version jump, and read + [the upgrade notes](https://docs.immich.app/install/upgrading/). +- **`shm_size: 128mb` on the database is load-bearing**, not decoration. + Postgres uses shared memory for larger queries and Docker's 64 MB default is + not enough here. Upstream's upgrade guide lists adding it as a required step. +- **`DB_STORAGE_TYPE: 'HDD'` is commented out** in the database environment. + Uncomment it if the Postgres data directory sits on a spinning disk — it tunes + the planner's assumptions about random reads. Leave it alone on SSD or NVMe. +- **Back up `UPLOAD_LOCATION` and `DB_DATA_LOCATION` together.** The files are + the photos; the database holds every album, face cluster, share and edit. + Either alone is most of a disaster. +- **The first import is expensive.** Machine learning runs over the whole + library once. On a low-power box expect it to saturate the CPU for hours — + the same caveat as [copyparty](../copyparty/README.md) on an N100, and worth + scheduling rather than discovering. + +## Exposing it + +The mobile apps talk to the same port as the browser, so whatever you put in +front has to work for both. + +- [Reverse proxy with Caddy](../docs/reverse-proxy.md) — the normal route. + Raise the upload body limit or large videos fail on the way in. +- [Cloudflare Tunnel](../cloudflare-tunnel/) — **the ~100 MB body cap is exactly + the wrong limit for this.** A phone's 4K video will exceed it. Read + [the limits](../docs/cloudflare-tunnel.md) first. +- [Single sign-on with Authentik](../docs/authentik-sso.md) — Immich speaks OIDC + natively, so use that rather than forward auth, which would break every mobile + client. Keep a local admin account until the flow is proven. + +## Links + +- Install guide: +- Release compose file: +- Environment variables: +- Upgrading: +- Hardware-accelerated ML: +- Compose builder: diff --git a/immich/compose.yaml b/immich/compose.yaml new file mode 100644 index 0000000..7449868 --- /dev/null +++ b/immich/compose.yaml @@ -0,0 +1,76 @@ +# +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose +# +# Make sure to use the docker-compose.yml of the current release: +# +# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml +# +# The compose file on main may not be compatible with the latest release. + +name: immich + +services: + immich-server: + container_name: immich_server + image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} + # extends: + # file: hwaccel.transcoding.yml + # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding + volumes: + # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file + - ${UPLOAD_LOCATION}:/data + - /etc/localtime:/etc/localtime:ro + env_file: + - .env + ports: + - '2283:2283' + depends_on: + - redis + - database + restart: always + healthcheck: + disable: false + + immich-machine-learning: + container_name: immich_machine_learning + # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag. + # Example tag: ${IMMICH_VERSION:-release}-cuda + image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} + # extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration + # file: hwaccel.ml.yml + # service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable + volumes: + - model-cache:/cache + env_file: + - .env + restart: always + healthcheck: + disable: false + + redis: + container_name: immich_redis + image: docker.io/valkey/valkey:9@sha256:70739f85ad2ee01a726a965584a0f94895f01b0c60b3cc8b0aeef11eaa6888cf + healthcheck: + test: redis-cli ping | grep -q PONG || exit 1 + restart: always + + database: + container_name: immich_postgres + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' + # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs + # DB_STORAGE_TYPE: 'HDD' + volumes: + # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data + shm_size: 128mb + restart: always + healthcheck: + disable: false + +volumes: + model-cache: