We generate and store more personal data than ever before. Cloud-hosted photo platforms like Google Photos and Apple iCloud provide convenient photo backups, but they carry long-term risks. Monthly subscription fees add up over time, and automated scanning algorithms analyze your private photos to train AI models or run automated account checks.
For self-hosting enthusiasts, Immich represents the ultimate solution. It is a high-performance, open-source photo and video management system designed to be hosted locally. Immich matches the user interface, speed, and features of commercial cloud storage platforms—providing native mobile apps, automatic backups, and local machine learning capabilities.
In this comprehensive tutorial, we will explore the architecture of Immich, walk through a step-by-step Docker Compose deployment, configure database setups, implement secure remote access using Caddy reverse proxies, configure hardware transcoding acceleration, and import existing photo libraries using the immich-cli.
1. Deconstructing the Immich Microservices Architecture
Immich is built using a modern microservices architecture. Instead of running as a single large application, it splits workloads across specialized containers to ensure fast photo loading, video transcoding, and machine learning performance.
Immich Core System Architecture:
[ Client Applications (Web UI / Mobile App) ]
│
▼ (Port 2283)
[ Immich Server ]
(API / Node.js)
│ │ │
┌──────────────────┘ │ └──────────────────┐
▼ ▼ ▼
[ PostgreSQL + pgvector ] [ Redis Cache ] [ Immich Machine Learning ]
(Metadata & Vectors) (Job Queuing) (CLIP / Facial AI)
1. Immich Server (NestJS Engine)
The primary backend container runs a Node.js web application built on the NestJS framework. It acts as the centralized coordinator for the entire system:
- API Gateway: Routes incoming HTTP requests from the Web UI and mobile clients.
- Upload Pipeline: Handles multi-part file stream uploads. When you upload a photo or video, the server writes it to a temporary directory, extracts file system metadata, and pipes the raw bytes to your permanent storage path.
- Static Asset Serving: Handles authorized delivery of media assets, converting raw HEIC/RAW photos into lightweight webp thumbnails on-the-fly when requested by web clients.
2. Immich Machine Learning (Python AI Service)
A Python service that runs modern deep learning models locally, offloading complex inference tasks from the main Node.js server:
- Facial Recognition (InsightFace): Utilizes a ResNet-based Convolutional Neural Network (CNN) trained on the buffalo_l dataset. When a photo is uploaded, it crops located human faces, extracts a 512-dimensional floating-point vector (embedding) representing facial geometry, and passes it to the database for matching.
- Semantic Search (CLIP): Stands for Contrastive Language-Image Pre-Training. Developed by OpenAI, it maps text descriptions and image pixels to a single shared vector space. When you query “cats in the snow,” CLIP converts your text into a text embedding vector, and Immich compares it to your photo embedding vectors, showing matching photos even if they lack textual metadata.
3. PostgreSQL & pgvector Vector Database
Stores all metadata (filenames, user profiles, albums, geographic coordinates). The standard database is extended with the pgvector plugin, transforming Postgres into a vector database:
- Vector Indexing: Stores the 512-dimensional embeddings of faces and images.
- Mathematical Matching: Runs cosine similarity searches (using the
<=>operator) or L2 distance calculations directly inside SQL queries to find faces similar to a target image or matches for a text search. It uses Hierarchical Navigable Small World (HNSW) indexes to keep search speeds fast as your library grows.
4. Redis Cache & BullMQ Job Queue
Acts as the central message queue and broker:
- Job Queues: Background tasks (generating thumbnails via the high-speed
sharplibrary, extracting EXIF coordinates viaexiftool, and transcoding videos viaffmpeg) are packaged as jobs and pushed to Redis. - Worker Coordination: Redis distributes these jobs across available CPU threads, preventing the NestJS server from blocking and keeping the user interface responsive during heavy bulk uploads.
2. System Prerequisites & Hardware Planning
Because Immich runs machine learning operations locally, it requires more resources than a simple file server:
- CPU: Multi-core x86_64 or ARM64 processor.
- RAM: Minimum 4 GB (8 GB is highly recommended if you have a large library, as face recognition models require significant memory).
- Storage: Fast NVMe/SATA SSDs for the Immich database configuration and metadata cache, combined with high-capacity HDD storage for your actual media files.
- Software: A clean installation of Docker and Docker Compose running on a stable Linux environment (like Ubuntu Server LTS).
3. Step-by-Step Installation & Docker Configuration
We will deploy Immich using Docker Compose. Create a dedicated directory on your server:
mkdir -p ~/immich-server
cd ~/immich-server
The Production Docker Compose File
Rather than downloading a pre-packaged script, creating your own docker-compose.yml file gives you complete control over your service paths, database passwords, and resource limits. Create the file:
nano docker-compose.yml
Paste the following configuration into the editor:
version: "3.8"
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:release
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /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:release
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
redis:
container_name: immich_redis
image: docker.io/redis:6.2-alpine
restart: always
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_DB: immich
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
restart: always
volumes:
pgdata:
model-cache:
Configure the Environment Variables (.env)
The environment configuration file stores your credentials and path definitions. Create the .env file:
nano .env
Add the following environment variables to configure your Immich instance:
# Core Storage Path
UPLOAD_LOCATION=/home/suresh/immich-library
# Database Credentials
DB_PASSWORD=MySecurePassword99!
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
DB_HOSTNAME=database
# Caching Services
REDIS_HOSTNAME=redis
# Machine Learning Orchestration
IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
MACHINE_LEARNING_WORKERS=1
# Transcoding Performance Constraints
FFMPEG_THREADS=2
# Environment Target
NODE_ENV=production
Explaining the Environmental Parameters
UPLOAD_LOCATION: The host directory where all uploaded photos, transcoded videos, and cached thumbnails will be stored.DB_PASSWORD/DB_USERNAME/DB_DATABASE_NAME: Cryptographic credentials used by the NestJS container to authenticate with the PostgreSQL database container.DB_HOSTNAME&REDIS_HOSTNAME: The network alias names of the database and redis containers. These correspond to the service definitions in thedocker-compose.ymlfile.IMMICH_MACHINE_LEARNING_URL: The endpoint where the main NestJS API server sends images to the python container for facial embeddings and semantic search calculations.MACHINE_LEARNING_WORKERS: Limits the number of CPU worker threads in the machine learning container. Setting this to1is highly recommended for lower-resource servers (like Raspberry Pi 4/5 or dual-core VPS nodes) to prevent out-of-memory (OOM) lockups.FFMPEG_THREADS: Controls the maximum CPU threads allocated toffmpegvideo transcoding tasks. Limiting this prevents video processing tasks from saturating your server’s CPU during bulk uploads.
Make sure the directory you specified in UPLOAD_LOCATION exists and has the correct write permissions for the user (UID 1000 in our container config):
mkdir -p /home/suresh/immich-library
sudo chown -R 1000:1000 /home/suresh/immich-library
4. Configuring Hardware Transcoding Acceleration
When you upload high-definition video files (e.g., Apple HEVC files or 4K videos), the server needs to transcode them into web-friendly formats to ensure smooth playback in browsers and apps. You can configure hardware acceleration to offload this process from your CPU to a dedicated GPU.
Enabling Intel QuickSync (QSV)
If your server uses an Intel processor with integrated graphics (Intel HD/UHD Graphics), you can pass the GPU device directly to the main server container:
- Open
docker-compose.yml. - Add a
devicesblock to theimmich-serverservice configuration:immich-server: ... devices: - /dev/dri:/dev/dri
Enabling NVIDIA CUDA GPU Acceleration
If you have a dedicated NVIDIA graphics card installed:
- Verify the NVIDIA Container Toolkit is installed on your host OS.
- Configure the
immich-machine-learningservice in yourdocker-compose.ymlto request GPU access:immich-machine-learning: ... deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu]
5. Launching the Server
Start the stack in detached background mode:
sudo docker compose up -d
Monitor the startup process and download logs to verify all containers initialize correctly:
sudo docker compose logs -f
6. Accessing the Dashboard & Admin Configuration
Once the logs show that the database migrations have completed, access the web dashboard:
- Open your web browser and navigate to:
http://<YOUR_SERVER_IP>:2283 - Click Get Started.
- Register your Administrator Account by entering your name, email address, and a secure password.
- Log into your dashboard.
First Setup Checklist:
[ Create Admin Account ] ──► [ Define User Profiles ] ──► [ Enable ML Models ]
7. Configuring a Secure Reverse Proxy (Caddy)
To access your photos securely from the internet, you should configure a reverse proxy to route traffic through HTTPS. Using Caddy simplifies this by handling Let’s Encrypt SSL certificates automatically.
Secure Connection Routing:
[ Mobile Phone App ] ──► HTTPS (Port 443) ──► [ Caddy Server ] ──► HTTP (Port 2283) ──► [ Immich Server ]
- Install Caddy on your server.
- Point your domain name (e.g.,
photos.yourdomain.com) to your server’s public IP address. - Edit your Caddyfile configuration:
sudo nano /etc/caddy/Caddyfile - Add the following reverse proxy block:
photos.yourdomain.com { reverse_proxy localhost:2283 } - Restart Caddy to apply the changes and enable HTTPS:
sudo systemctl restart caddy
8. Importing Existing Libraries via CLI
If you have a large library of existing photos stored on an external drive or NAS, you can import them into Immich without copying them manually. Use the official immich-cli tool to import your media:
1. Generate an API Key
- Log into your Immich web dashboard.
- Go to User Settings > API Keys.
- Click Create API Key, copy the generated token, and keep it secure.
2. Run the Import CLI Command
You can execute the import tool globally by installing it via NPM, or run it directly on-the-fly using npx.
Option A: Run directly using npx
This is the recommended method as it ensures you are always using the CLI version that matches your server’s active release:
npx @immich/cli upload \
--key "YOUR_API_KEY_HERE" \
--server "http://192.168.1.100:2283/api" \
--recursive \
/mnt/data/my-photo-archive
Option B: Install the CLI globally
npm install -g @immich/cli
Verify the installation:
immich --help
Advanced CLI Import Options
The immich-cli tool provides several flags to customize how your files are ingested:
- Dry-Run Validation (
--dry-run): Performs a validation scan of the directory, counting matching photos and reporting potential errors without uploading any assets to the server:immich upload --key "YOUR_KEY" --server "http://IP:2283/api" --dry-run --recursive /path/to/photos - Upload Directly to a Named Album (
--album): Automatically creates a new album (or uploads to an existing one) containing all the synchronized photos:immich upload --key "YOUR_KEY" --server "http://IP:2283/api" --album "Family Reunion 2025" --recursive /path/to/photos - Import Existing Directory Structure (
--import): Attempts to preserve your local folder hierarchies as album arrangements inside Immich. - Skip Confirmation Prompts (
--yes): Bypasses all prompt checks during execution, which is helpful when integrating the import command into automated shell scripts.
9. Backing Up Your Immich Infrastructure
A home server deployment is only as reliable as its backup strategy. If a disk drive fails, you risk losing both your photo media files and the database relationships (like face groupings and album tags). Set up a regular backup schedule:
1. Database Hot Backups
Because Immich relies on PostgreSQL, copying the raw data folders while the container is running can result in file corruption. Instead, run a hot backup using pg_dumpall:
# Dump the entire database to a compressed SQL file
docker exec -t immich_postgres pg_dumpall -c -U postgres | gzip > ~/immich-server/backups/database_backup_$(date +%F).sql.gz
2. Media Directory Backups
Backup the primary upload folder (UPLOAD_LOCATION) using rsync or an incremental backup utility (like BorgBackup or Restic) to a remote storage server or external backup drive:
# Run incremental sync backup
rsync -avz --delete /home/suresh/immich-library/ /mnt/backup-drive/immich-library/
3. Database Vector Index Optimization (Vacuuming & Reindexing)
As you upload, delete, and organize photos, the pgvector HNSW indexes inside your PostgreSQL database can become fragmented over time, degrading face-matching and semantic search performance. Periodically run a vacuum and analyze check to clean up dead rows:
# Run VACUUM and ANALYZE inside the container
docker exec -t immich_postgres psql -U postgres -d immich -c "VACUUM ANALYZE;"
For large media archives, force PostgreSQL to rebuild the vector indexes to reclaim storage space and optimize query execution paths:
# Rebuild the pgvector HNSW index
docker exec -t immich_postgres psql -U postgres -d immich -c "REINDEX DATABASE immich;"
4. Complete Backup Automation Script
Create a shell script named backup_immich.sh to automate this workflow:
#!/bin/bash
BACKUP_DIR="/home/suresh/immich-server/backups"
TIMESTAMP=$(date +%F)
mkdir -p "$BACKUP_DIR"
# 1. Backup the DB
docker exec -t immich_postgres pg_dumpall -c -U postgres | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"
# 2. Backup the Configs
cp /home/suresh/immich-server/.env "$BACKUP_DIR/config_$TIMESTAMP.env"
cp /home/suresh/immich-server/docker-compose.yml "$BACKUP_DIR/compose_$TIMESTAMP.yml"
# 3. Sync media files
rsync -a --delete /home/suresh/immich-library/ /mnt/backup-drive/immich-library/
# 4. Retain only the last 7 days of DB backups
find "$BACKUP_DIR" -type f -name "db_*.sql.gz" -mtime +7 -delete
Make the script executable and schedule it to run daily using cron:
chmod +x backup_immich.sh
# Add to crontab: 0 2 * * * /home/suresh/immich-server/backup_immich.sh
10. Troubleshooting & Maintenance
Use this matrix to identify and resolve common issues:
| Issue / Symptom | Primary Cause | Troubleshooting / Diagnostic Action |
|---|---|---|
| Containers killed with OOM error | Low memory during machine learning tasks | Edit your .env file and limit worker threads: MACHINE_LEARNING_WORKERS=1. Ensure swap memory is configured. |
| Videos fail to play in browser | Transcoding failed or is disabled | Check the ffmpeg container logs. Go to Administration > Job Settings and run the Video Transcoding queue. |
| Mobile backup halts in the background | OS battery optimization terminated process | Go to Android battery settings for Immich, change optimization policy to “Unrestricted”, and enable background data. |
| Facial recognition is not grouping people | ML container still processing library | Check the status of face detection jobs in Administration > Jobs. Wait for the queue to clear. |
| Web interface shows “Server Offline” | PostgreSQL database failed to start | Check database logs: docker logs immich_postgres. Ensure the disk volume has correct permissions. |
11. Photo Management Alternatives
The table below compares Immich with other popular self-hosted photo galleries:
| Feature / Metric | Immich | Photoprism | Lychee |
|---|---|---|---|
| Language / Stack | Node.js / NestJS | Go / Tensorflow | PHP / Laravel |
| Native Mobile App | Yes (iOS & Android) | No (Progressive Web App) | No |
| Facial Recognition | Yes (InsightFace) | Yes (Tensorflow) | No |
| Semantic Search | Yes (CLIP) | Yes | No |
| Database Backend | PostgreSQL + pgvector | MariaDB / SQLite | MySQL / PostgreSQL |
| Multi-User Sharing | Yes (Full timeline sharing) | Limited | Basic |
12. Conclusion
Deploying Immich gives you a private, secure, and self-hosted photo management platform. By storing your photos locally and securing access via a Caddy reverse proxy, you maintain complete ownership of your media library and data privacy.
Ready to secure the rest of your self-hosted infrastructure? Read our step-by-step guides on configuring a secure Vaultwarden Password Manager or managing your private cloud storage with Nextcloud today!
Frequently Asked Questions (FAQ)
Q1: What is Immich? A: Immich is a high-performance, open-source photo and video management system designed to be self-hosted locally. It serves as a privacy-friendly alternative to commercial cloud platforms like Google Photos and Apple iCloud.
Q2: What are the hardware requirements to self-host Immich? A: Immich runs machine learning operations locally, so it requires a multi-core x86_64 or ARM64 processor, at least 4 GB of RAM (8 GB recommended for facial recognition), and fast SSD storage for its database alongside high-capacity storage for media.
Q3: Can Immich perform facial recognition and search by text? A: Yes, Immich features local machine learning capabilities. It uses models like InsightFace for accurate facial recognition and CLIP for semantic search, allowing you to search for images using natural language text descriptions.
Q4: How do I access Immich securely over the internet? A: To access your photos securely from outside your home network, it is recommended to set up a reverse proxy like Caddy. Caddy handles routing traffic through HTTPS and automatically manages Let’s Encrypt SSL certificates.
Q5: How can I bulk import an existing photo library into Immich?
A: You can easily import massive photo archives using the official immich-cli tool. By generating an API key from the dashboard, you can run the CLI over your local directories to upload photos seamlessly, preserving structure and albums if desired.



Discussion
Loading comments...