Cloud photo storage is one of the sneakiest vendor lock-ins in modern tech. You start with a free 15GB tier, and before you know it, you are paying a monthly subscription to Google One or Apple iCloud because your library of 4K family videos and lab project photos has exceeded 2 Terabytes. Beyond the recurring cost, the idea of a hyperscaler scanning your personal photo library for ad targeting and model training never sits right. When our family photo archive crossed 65,000 assets, I migrated everything to self-hosted Immich on Docker.
Immich has become the definitive self-hosted photo platform. It isn’t just a static web gallery like Nextcloud Photos or older PHP albumsβit replicates the seamless, lightning-fast experience of Google Photos. You get automatic mobile background sync, facial clustering, interactive map views, and natural language semantic search. Here is how I deployed Immich on docker02, tuned its machine learning pipeline, and laid out the storage architecture for peak performance.
1. The Architecture: Microservices Behind the Lens
Immich isn’t a single monolithic binary. It is engineered as a distributed microservices stack designed to offload heavy computational workloads (like face recognition and video transcoding) from the web server:
- immich-server: The core API engine (Node.js/NestJS) that handles asset uploads, user authentication, thumbnail serving, and library metadata.
- immich-machine-learning: A Python microservice running ONNX runtime models. It handles facial recognition via InsightFace and natural language search embeddings via CLIP.
- immich-postgres: A specialized PostgreSQL 14 instance compiled with
pgvectorsandvectorchord. This allows the database to store high-dimensional machine learning vector embeddings directly alongside traditional SQL metadata and perform sub-second nearest-neighbor search. - immich-redis (Valkey 9): A dedicated Valkey instance acting as an in-memory queue manager for background jobs (thumbnail generation, video encoding, face detection, EXIF extraction).
2. Production Docker Compose Configuration
Here is our battle-tested compose configuration running on docker02 (IP 10.1.20.19, port 2283):
# /opt/stacks/immich/docker-compose.yml excerpt
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:
volumes:
- :/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
restart: always
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:
volumes:
- model-cache:/cache
environment:
# Prevent PyTorch/ONNX from saturating all host CPU cores
- OMP_NUM_THREADS=2
- RAY_ACCELERATOR=cpu
deploy:
resources:
limits:
cpus: '3.0'
memory: 4096M
restart: always
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9
restart: always
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD:
POSTGRES_USER:
POSTGRES_DB:
POSTGRES_INITDB_ARGS: '--data-checksums'
volumes:
- :/var/lib/postgresql/data
shm_size: 256mb
restart: always
volumes:
model-cache:
3. Conquering the ML Core Starvation Problem
The Big Gotcha: When you bulk-import 65,000 photos, the machine learning container will enthusiastically spawn threads for every logical CPU core on the host. In our lab, this pegged the CPU at 100%, causing Zabbix alert storms and making other containers sluggish.
To prevent machine learning tasks from choking the Docker host, two critical settings were applied:
- Thread Concurrency Throttling: We set
OMP_NUM_THREADS=2in the environment variables. This forces the ONNX Runtime / OpenMP thread pool to restrict facial recognition inference to two worker threads per task instead of flooding the entire vCPU pool. - Docker Resource Capping: We placed a hard cap of
3.0CPUs and4GBRAM on theimmich-machine-learningcontainer. If an intensive batch of 4K video frames enters the facial detection queue, it runs quietly in the background without starving the web server or database.
4. Storage Tiering: NVMe vs. Mass Storage
A common mistake in self-hosting photo libraries is putting the database on spinning rust or over an NFS network mount. Immich generates massive amounts of small files: square thumbnails, micro-previews, blurhash strings, and high-dimensional vector embeddings.
We separated storage into two distinct performance tiers:
- Fast IOPS Tier (Local NVMe/SSD): The PostgreSQL database (
/var/lib/postgresql/data) and Valkey in-memory cache live on fast local SSD storage ondocker02. This ensures that querying timeline grids with 50,000 items renders in under 50 milliseconds. - Bulk Storage Tier (High-Capacity SAN/NAS): The original RAW files, JPEG originals, and transcoded MP4 video proxies (
UPLOAD_LOCATION) map to our redundant high-capacity storage pools.
5. The Magic of Semantic Vector Search
The feature that completely won over my family is Immich’s natural language semantic search. Because every photo is processed through a local CLIP (Contrastive Language-Image Pre-Training) model, you can search your library using concepts rather than tags:
- “Dog playing in the snow” β Instantly brings up winter photos from five years ago.
- “Receipts and invoices” β Surfaces photos of hardware receipts and shipping labels.
- “Server rack with blue Ethernet cables” β Pulls up historic wiring photos from my homelab setup.
All of this happens locally inside the Docker container. Not a single byte of biometric facial data or photo content ever touches an external third-party server.
6. Seamless Mobile Experience
The Immich iOS and Android apps work flawlessly. Whenever our phones connect to our home Wi-Fi (NET-WORKSTATIONS-VLAN10), new photos immediately sync to the server in the background. When we are on the road, our phones connect through our persistent WireGuard VPN tunnel back to our UniFi Dream Machine, allowing instant photo backups from hotel Wi-Fi or cellular.
We’ve cut out the monthly cloud storage tax, reclaimed complete privacy over our family memories, and built a photo management platform that is faster than the commercial clouds.