Immich
Self-hosted photo and video backup with ML-powered search.
The stack
Generated output
services:
immich-server:
image: ghcr.io/immich-app/immich-server:v1.119.0@sha256:24df1172544370826349159692d177ba22ca773c81857d36996a254c08422b95
restart: unless-stopped
depends_on:
- immich-redis
- immich-postgres
environment:
DB_HOSTNAME: immich-postgres
DB_USERNAME: postgres
DB_PASSWORD: ${DB_PASSWORD}
DB_DATABASE_NAME: immich
REDIS_HOSTNAME: immich-redis
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
healthcheck:
test:
- CMD
- /bin/sh
- -c
- curl -fSs http://127.0.0.1:2283/api/server-info/ping
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:v1.119.0@sha256:fa558ae8752eae335c5bfafeb5716dc60d0882f026350adb45e18ab28fbd36ae
restart: unless-stopped
volumes:
- model-cache:/cache
immich-redis:
image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8
restart: unless-stopped
healthcheck:
test: redis-cli ping || exit 1
interval: 30s
timeout: 5s
retries: 3
immich-postgres:
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: postgres
POSTGRES_DB: immich
POSTGRES_INITDB_ARGS: --data-checksums
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
interval: 5m
timeout: 10s
retries: 5
start_period: 5m
volumes:
model-cache: {}
# Caddyfile for Immich
# Replace immich.example.com with your real domain. Caddy issues TLS automatically.
immich.example.com {
encode zstd gzip
reverse_proxy immich-server:2283
}
# Traefik labels for Immich
# Merge into the immich-server service in docker-compose.yml.
# Assumes an external Traefik network named "proxy" and a
# certresolver named "letsencrypt". Replace immich.example.com.
services:
immich-server:
labels:
traefik.enable: "true"
traefik.http.routers.immich-postgres.rule: Host(`immich.example.com`)
traefik.http.routers.immich-postgres.entrypoints: websecure
traefik.http.routers.immich-postgres.tls: "true"
traefik.http.routers.immich-postgres.tls.certresolver: letsencrypt
traefik.http.services.immich-postgres.loadbalancer.server.port: "2283"
networks:
- proxy
- default
networks:
proxy:
external: true
Replace immich.example.com with your domain. Generate secrets below.
Environment
.env with generated secrets
Secrets are generated in your browser via crypto.getRandomValues. Nothing is sent anywhere.
Server-rendered .env template
# Host directory for uploaded photos and videos. UPLOAD_LOCATION=./library # Host directory for Postgres data. Use a fast local disk, not NFS. DB_DATA_LOCATION=./postgres # PostgreSQL password used by both Postgres and Immich. DB_PASSWORD=
About
What is Immich?
Immich is a high-performance, self-hosted photo and video backup solution. It auto-uploads your phone library through native iOS and Android apps, organizes content into albums, deduplicates uploads, and runs on-device machine learning for object detection, face recognition, and natural-language search. This stack runs the official Immich server with the recommended pgvecto.rs-enabled Postgres image for vector search, plus Redis for the job queue. Image tags are pinned to specific versions — Immich's database schema changes between releases, so unattended upgrades are a known source of data corruption. The compose file does not expose Postgres or Redis to the host: the reverse proxy block is the only public surface. Use the Caddyfile or Traefik labels in this stack to terminate TLS on `immich.example.com` and forward to the immich-server on port 2283.
Requirements
Before you start
- 4 GB RAM minimum (8 GB recommended if you enable ML features).
- Docker 24+ and Docker Compose v2.
- A fast local disk for the Postgres volume — avoid NFS or SMB shares.
- CPU with AVX/AVX2 if you plan to use the machine-learning container.
Deploy
How to deploy
- Create the upload and database directories before first boot: `mkdir -p library postgres`.
- Generate a database password via the "Generate secrets" button and copy the .env file.
- Bring the stack up: `docker compose up -d`.
- Wait ~30 seconds for the database migration to complete.
- Open `https://immich.example.com` (or the port if you skip the proxy) and create the admin account in the first-run wizard.
- Install the mobile app and point it at the same URL.
Errors
Common errors & fixes
Immich logs show "FATAL: password authentication failed for user postgres".
You changed DB_PASSWORD after first boot. Postgres stores the original password in its data volume. Either restore the old password in .env or delete the postgres data directory and start fresh.
Mobile app uploads stall at 0%.
Check that the reverse proxy is forwarding the full body — Caddy does this by default, Traefik needs `transport.respondingTimeouts` if you proxy huge files. The container itself has no upload size limit.
"failed to load vectors extension" on first start.
You are not using the pgvecto.rs image. Plain `postgres:14` will not work — keep the `tensorchord/pgvecto-rs` image pinned in this compose file.
Healthcheck on Postgres fails with "checksum failure count is N".
Disk corruption on the Postgres volume. Stop the stack, snapshot the volume, and restore from the most recent backup. Do not ignore this error.
Limitations
Honest limitations
- Immich does not yet have a stable API — pin both image tags together and read the release notes before upgrading.
- No native external sharing — links are public-by-token, no per-user ACL.
- Face recognition results are not exportable; they live in the database only.
FAQ
Frequently asked
Why is the Postgres image pinned to pgvecto.rs and not the official Postgres image?+
Immich uses vector similarity search for "find similar photos" and CLIP search. That requires the pgvecto.rs extension, which is not in the upstream postgres image. The pinned tag is the version Immich currently tests against.
Can I run this on a Raspberry Pi?+
The server and database run on arm64, but machine-learning is heavy. On a Pi 4 you should disable the ML container or expect long indexing queues.
Do I need both Caddy and Traefik?+
No — pick one. The two reverse-proxy outputs are alternatives, not complements. Use whichever you already run.
How do I back this up?+
Two things: (1) the `UPLOAD_LOCATION` directory which holds originals, and (2) a `pg_dump` of the immich database for albums, faces, and metadata. The model cache is regeneratable.
Why no `latest` tag?+
Immich ships breaking database migrations regularly. Pulling `latest` after a long uptime risks running a server against an older schema. Pin and upgrade deliberately.
Related