06 - Immich Photo Management
Version: Immich v3.1.0
Date: 2026-04-02
Platform: Proxmox VE 9.1 / LXC 100 (Docker)
Hardware: Intel i5-8400, 32GB RAM, NVMe + HDD
📋 Table of Contents¶
- System overview
- Prerequisites
- Swap configuration
- iGPU passthrough
- Storage directories
- Docker Compose installation
- Starting Immich
- First login
- ML settings
- Partner Sharing
- Mobile App setup
- TV App + API Key
- Troubleshooting
🖥️ System Overview¶
Infrastructure:¶
| ID | Type | Name | IP | Services |
|---|---|---|---|---|
| 100 | LXC | docker-host | 192.168.0.110 | Docker stack (Nginx, Jellyfin, arr, Immich*) |
| 101 | VM | homeassistant | 192.168.0.202 | Home Assistant OS |
| 102 | LXC | adguard-home | 192.168.0.111 | AdGuard Home DNS |
Immich Services:¶
LXC 100 - Docker (192.168.0.110)
├─ Immich Server (port 2283) - WebUI + API
├─ PostgreSQL (port 5432) - Database
├─ Redis (port 6379) - Cache
└─ Storage: /mnt/storage/immich/
Nobara desktop (192.168.0.100)
└─ Immich ML (port 3003) - Face recognition, CLIP search, on the RTX 2060 SUPER
The ML container was moved off LXC 100 to the Nobara desktop for its GPU, so the walkthrough below - which installs it alongside the server on the iGPU - is the original v2.6.3 install and no longer matches the running stack. Current ML details, including the shared-VRAM failure mode, are in Nobara.
🔧 Prerequisites¶
System requirements:¶
CPU: Intel CPU with iGPU (Quick Sync) - i5-8400 ✅
RAM: 32GB
Immich usage: ~4-6GB
Storage:
NVMe/SSD: PostgreSQL database (~2-5GB)
HDD: Photo library (unlimited, MergerFS pool)
Network: Gigabit LAN (for photo upload speed)
Checks (Proxmox host):¶
# Does iGPU device exist?
ls -la /dev/dri/renderD128
# -rw-rw---- 1 root render 226, 128 Dec 26 renderD128 ✅
# Does LXC 100 exist and is running?
pct status 100
# status: running ✅
# Is MergerFS storage working?
ls -la /mnt/storage/
# drwxr-xr-x media, backup, etc. ✅
💾 Swap Configuration¶
Why is swap needed?¶
On a 32GB RAM system swap is not strictly required, but still useful as a safety net during heavy ML indexing.
4GB swap = protection against unexpected OOM spikes ✅
Setting up swap:¶
Proxmox host SSH:
Find or add:
Save: Ctrl+O, Enter, Ctrl+X
Setting swappiness (emergency only):
# Proxmox host
echo "vm.swappiness=10" >> /etc/sysctl.conf
sysctl -p
# Verify
cat /proc/sys/vm/swappiness
# 10 ✅
What does swappiness=10 mean? - RAM <90% → Swap NOT used - RAM >95% → Swap activates - Fewer SSD writes, longer lifespan! ✅
Start LXC:
Expected result:
total used free shared buff/cache available
Mem: 8.0Gi 1.7Gi 4.2Gi 172Mi 2.3Gi 6.3Gi
Swap: 4.0Gi 0B 4.0Gi ✅ 4GB swap!
🎮 iGPU Passthrough¶
Note: The Intel UHD 630 is shared between Immich (hardware-accelerated built-in ML - face detection, image search) and Jellyfin (hardware transcoding). Immich runs its own self-contained ML models, no external AI service involved. Both services can use the iGPU simultaneously without conflict.
Why is iGPU (Intel Quick Sync) needed?¶
CPU only (no iGPU):
1000 photos face detection: ~60-90 minutes 🐌
iGPU (Quick Sync):
1000 photos face detection: ~15-30 minutes ⚡
3-4x faster ML processing! ✅
Setting up iGPU passthrough:¶
Proxmox host SSH:
Scroll to the END of the file and add these lines:
lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
Save: Ctrl+O, Enter, Ctrl+X
Start LXC and verify:
Expected result:
crw-rw---- 1 nobody nogroup 226, 0 Dec 26 card0
crw-rw---- 1 nobody nogroup 226, 128 Dec 26 renderD128 ✅
Is renderD128 there? ✅ iGPU working!
📂 Storage Directories¶
Permission mapping (LXC unprivileged):¶
Create storage from Proxmox host:
Proxmox host SSH:
# Create directories
mkdir -p /mnt/storage/immich/library
mkdir -p /mnt/storage/immich/pgdata
# Permissions
chown -R 100000:100000 /mnt/storage/immich/library
chown -R 100999:100999 /mnt/storage/immich/pgdata
chmod -R 755 /mnt/storage/immich/library
chmod -R 750 /mnt/storage/immich/pgdata
# Verify
ls -la /mnt/storage/immich/
Expected result:
Verify inside LXC:
As seen inside the LXC:
Mapping works! ✅
🐳 Docker Compose Installation¶
Download Docker Compose files:¶
In LXC 100:
# Create directory
mkdir -p /srv/docker-compose/immich
cd /srv/docker-compose/immich
# Download official files
curl -L -o docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
curl -L -o .env https://github.com/immich-app/immich/releases/latest/download/example.env
curl -L -o hwaccel.transcoding.yml https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
# Verify
ls -lha
Expected result:
-rw-r--r-- 1 root root 979 Dec 26 .env ✅
-rw-r--r-- 1 root root 2.8K Dec 26 docker-compose.yml ✅
-rw-r--r-- 1 root root 1.7K Dec 26 hwaccel.transcoding.yml ✅
.env configuration:¶
Modify these lines:
# Upload location
UPLOAD_LOCATION=/mnt/storage/immich/library
# Database password (CHANGE THIS!)
DB_PASSWORD=YOUR_DB_PASSWORD
# Timezone
TZ=Europe/Bratislava
Save: Ctrl+O, Enter, Ctrl+X
Modifying docker-compose.yml:¶
FULL FILE CONTENTS (replace with this!):
Delete the ENTIRE file and paste this:
#
# 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: quicksync
devices:
- /dev/dri:/dev/dri
deploy:
resources:
limits:
memory: 1G
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
restart: unless-stopped
healthcheck:
disable: false
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
devices:
- /dev/dri:/dev/dri
deploy:
resources:
limits:
memory: 2G
volumes:
- model-cache:/cache
env_file:
- .env
restart: unless-stopped
healthcheck:
disable: false
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
deploy:
resources:
limits:
memory: 256M
healthcheck:
test: redis-cli ping || exit 1
restart: unless-stopped
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
deploy:
resources:
limits:
memory: 512M
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
DB_STORAGE_TYPE: 'HDD'
volumes:
- /mnt/storage/immich/pgdata:/var/lib/postgresql/data
shm_size: 128mb
restart: unless-stopped
volumes:
model-cache:
Save: Ctrl+O, Enter, Ctrl+X
Configuration summary:¶
✅ immich-server:
- iGPU passthrough (/dev/dri)
- Quick Sync video transcoding
- RAM limit: 1G
- Volume: /mnt/storage/immich/library
✅ immich-machine-learning:
- iGPU passthrough (ML acceleration)
- RAM limit: 2G
✅ redis:
- RAM limit: 256M
✅ database:
- RAM limit: 512M
- HDD optimization
- Volume: /mnt/storage/immich/pgdata
🎬 Starting Immich¶
Pull Docker images:¶
Expected output:
[+] Pulling 4/4
✔ immich-server Pulled
✔ immich-machine-learning Pulled
✔ redis Pulled
✔ database Pulled
Start Immich:¶
Expected output:
[+] Running 5/5
✔ Network immich_default Created
✔ Volume immich_model-cache Created
✔ Container immich_redis Started
✔ Container immich_postgres Started
✔ Container immich_machine_learning Started
✔ Container immich_server Started
Check logs:¶
Signs of successful startup:
immich_postgres | database system is ready to accept connections ✅
immich_server | Immich Microservices is running [v2.6.3] ✅
immich_machine_learning | Application startup complete. ✅
immich_redis | Ready to accept connections tcp ✅
Container status:¶
Expected result (after ~2-3 minutes):
NAME STATUS
immich_server Up (healthy) ✅
immich_machine_learning Up (healthy) ✅
immich_postgres Up (healthy) ✅
immich_redis Up (healthy) ✅
All "Up (healthy)"? ✅ WORKING!
Check RAM usage:¶
Expected result:
🌐 First Login¶
Accessing the WebUI:¶
Browser (Windows/Phone/any device):
Expected screen:
┌────────────────────────────────┐
│ IMMICH │
│ │
│ Welcome to Immich │
│ │
│ Getting Started │
│ │
│ [ Sign Up ] │
│ │
└────────────────────────────────┘
Privacy Settings Wizard:¶
Server Privacy:
☑ Map - ENABLED (shows where photos were taken on a map)
☑ Version Check - ENABLED (checks for updates)
User Privacy:
☐ Google Cast - DISABLED (if no Chromecast)
☐ Storage Template - DISABLED (DO NOT enable! Changes file structure!)
Done button
Create admin account:¶
Sign Up form:
Sign Up button
Redirects to Dashboard! ✅
🤖 ML Settings¶
ML Settings configuration:¶
Dashboard → top left ⚙️ (Administration) → Machine Learning:
Smart Search (CLIP):
Facial Recognition:
Scroll down, click "Save" button! ✅
Upload first photos (TEST!):¶
From mobile app (RECOMMENDED!):
1. Google Play / App Store → "Immich"
2. Install
3. Server URL: http://192.168.0.110:2283
4. Login: admin@homelab.local + password
5. Backup settings:
✅ Foreground backup
✅ Background backup
6. Select 10-20 photos (TEST!)
7. Upload!
From WebUI (alternative, one by one):
Start ML Processing:¶
Administration → Jobs:
Smart Search:
Face Detection:
Wait 2-5 minutes (for 10-20 photos)...
ML test - Search:¶
Search bar (top banner):
Try:
Does it find the photos? ✅ AI WORKING! 🤖
People tab (Faces):
Are faces visible? ✅ Face Recognition working!
👥 Partner Sharing¶
Creating a second user:¶
Administration → Users → Create User:
Create button ✅
Activating Partner Sharing:¶
Logged in as admin account (admin@homelab.local):
Account Settings → Partner Sharing:
Save ✅
Log in as partner account:
Account Settings → Partner Sharing:
Save ✅
Partner Sharing result:¶
Admin account:
✅ Sees: own + partner's photos
✅ Upload: admin photos
✅ Face recognition: on admin's photos
Partner account:
✅ Sees: partner's + admin's photos
✅ Upload: partner photos
✅ Face recognition: on partner's photos
IMPORTANT:
❌ Face recognition NOT shared!
✅ Photos shared
✅ CLIP search shared
Face Recognition limitation:¶
Partner Sharing does NOT share:
ONLY these are shared:
This is a DESIGN DECISION! (Privacy!)
Solution for shared face recognition:¶
OPTION 1: Both upload photos (RECOMMENDED!) ✅
Admin photos → Admin face recognition
Partner photos → Partner face recognition
→ They see each other's photos
→ Faces are separate (privacy!)
OPTION 2: Use 1 shared account ⚠️
📱 Mobile App Setup¶
Installing the Immich Mobile App:¶
Android:
iOS:
App configuration:¶
Open app:
1. Server URL: http://192.168.0.110:2283
2. Login: admin@homelab.local (or partner@homelab.local)
3. Password: [password]
4. Login
Backup Settings:
Settings → Backup:
☑ Foreground backup - ON
☑ Background backup - ON
Select albums/folders to backup
Start Backup! 🚀
Photos upload automatically! ✅
Tailscale remote access (optional):¶
If Tailscale VPN is set up:
Mobile app Server URL: http://192.168.0.110:2283
→ Works through Tailscale VPN! ✅
→ Securely accessible from anywhere!
📺 TV App + API Key¶
Generating an API Key:¶
WebUI (logged in as admin@homelab.local):
API Key creation:
Permissions:
Home use (SIMPLE!):
OR Read-Only (SECURE!):
Create button ✅
API Key appears:
┌────────────────────────────────────────┐
│ API Key Created Successfully! │
│ │
│ xK9mP2vR4nQ8sL7tW6yB3cF5gH1jD0aE... │
│ │
│ ⚠️ COPY NOW! Won't be shown again! │
│ │
│ [Copy to Clipboard] [Close] │
└────────────────────────────────────────┘
⚠️ COPY IT! Save it to a text file! 📋
TV App login:¶
Android TV / Google TV:
1. Install: "Immich for Android TV" (Play Store)
2. Open app
3. Server URL: http://192.168.0.110:2283
4. Login method: API Key
5. API Key: [paste the key]
6. Connect!
QR Code alternative (easier!):
1. Generate QR Code: https://www.qr-code-generator.com/
2. Paste API Key → Generate QR
3. TV app: Scan QR Code
4. Show QR code with phone
5. Automatic login! ✅
Revoke API Key (if needed):¶
Account Settings → API Keys:
Active API Keys:
┌─────────────────┬──────────────┬────────────┐
│ Name │ Created │ Action │
├─────────────────┼──────────────┼────────────┤
│ TV App │ 2025-12-26 │ [Revoke] │
└─────────────────┴──────────────┴────────────┘
Revoke → API Key immediately invalidated! ✅
❗ Troubleshooting¶
Problem 1: WebUI not accessible¶
Symptom: http://192.168.0.110:2283 timeout
Check:
# Container status
docker compose ps
# immich_server Up? ✅
# Port check
ss -tulpn | grep 2283
# tcp LISTEN 0.0.0.0:2283 ✅
# Logs
docker compose logs immich_server
Solution:
Problem 2: PostgreSQL permission denied¶
Symptom:
immich_postgres | chown: changing ownership: Operation not permitted
immich_postgres exited with code 1
Solution:
# Exit LXC
exit
# Proxmox host
chown -R 100999:100999 /mnt/storage/immich/pgdata
chmod -R 750 /mnt/storage/immich/pgdata
# Restart
pct enter 100
cd /srv/docker-compose/immich
docker compose down
docker compose up -d
Problem 3: ML processing slow¶
Symptom: 1000 photos → 2+ hours
Check:
# Is iGPU passthrough working?
docker exec immich_machine_learning ls -la /dev/dri/
# renderD128 present? ✅
# iGPU usage
docker exec immich_server vainfo
# VAAPI support? ✅
Solution:
# If no iGPU passthrough
# Proxmox host: check /etc/pve/lxc/100.conf
# LXC restart
pct stop 100
pct start 100
Problem 4: Partner search not working¶
Symptom: Partner account → Search empty, People empty
CAUSE: Face recognition is NOT shared in Partner Sharing!
Solution:
Have the partner account upload photos:
→ ML job runs on partner's photos
→ Partner sees faces on their own photos ✅
OR
Use 1 shared account for both
Problem 5: OOM Killer (Out of Memory)¶
Symptom: Containers crash, "OOMKilled" status
Check:
Solution:
# Increase swap (Proxmox host)
pct set 100 -swap 6144 # 6GB swap
pct reboot 100
# Check resource limits
docker inspect immich_machine_learning | grep Memory
# "Memory": 2147483648 ← 2GB limit? ✅
📊 Performance Metrics¶
Expected performance (i5-8400 + iGPU):¶
Initial ML processing:
100 photos: ~5-10 min (iGPU)
1,000 photos: ~30-60 min
10,000 photos: ~5-8 hours (run overnight!)
Search:
Face search: <1 sec ✅
CLIP search: 1-2 sec ✅
Timeline: instant ✅
Upload:
Local WiFi: 50-100 MB/s
Mobile backup: 5-20 MB/s
RAM usage:
Idle: ~2.5GB
ML processing: ~4-5GB
Peak: ~6-7GB (+ swap)
🔧 Useful Commands¶
Immich management:¶
# Start/Stop
cd /srv/docker-compose/immich
docker compose stop
docker compose start
docker compose restart
# Logs
docker compose logs -f
docker compose logs immich_server --tail=100
# Stats
docker stats
free -h
Database backup:¶
# PostgreSQL backup
docker exec immich_postgres pg_dumpall -U postgres > /mnt/storage/backup/immich-db-$(date +%Y%m%d).sql
# Restore
cat backup.sql | docker exec -i immich_postgres psql -U postgres
Update Immich:¶
The stack is deployed by Komodo (GitOps), so the version pin does not live in a
file on LXC 100. IMMICH_VERSION is set in the Komodo Stack Environment, and
Komodo writes it into .env before every deploy. Editing .env on the host by
hand gets overwritten on the next deploy.
Patch releases need nothing: the pin is a floating major tag (v3) and the stack
has auto_update = true, so Komodo pulls new v3.x images on its own.
A major version bump = change one line in the Komodo Stack Environment, then Deploy:
# Check what is actually running
curl -s -H "x-api-key: $KEY" http://192.168.0.110:2283/api/server/about | jq .version
# Migrations ran cleanly?
docker logs immich_server 2>&1 | grep -i migration
The external ML container on Nobara (compose/nobara/immich-ml/) carries its own
hardcoded tag and must be bumped in the same major version as the server.
v2 → v3 (2026-08-12)¶
The docker-compose.yml needed no changes at all. Checked against the
v3 migration guide:
| Breaking change | Applied here? |
|---|---|
| pgvecto.rs support dropped | No - vchord 0.4.3 was already the vector extension |
MACHINE_LEARNING_PRELOAD__* renames, IMMICH_MACHINE_LEARNING_PING_TIMEOUT removed |
No - none of them were set |
| OAuth insecure requests now blocked by default | No - no OAuth configured |
Removed API endpoints (/assets/random, /sync/full-sync, /server/theme, ...) |
No - the only consumer is the Homepage widget, which reads server/statistics, unchanged |
| ML now needs an x86-64-v2 CPU | No - Nobara's CPU is far newer |
The /data trap: upstream's compose template mounts the library at /data
instead of /usr/src/app/upload since v1.137.0. That was an explicitly optional
template change, not a breaking one, and the old path keeps working. People
who "helpfully" switched the mount during the upgrade ended up with missing
thumbnails and transcoded videos, because the media location recorded in the
database no longer matched. Leave the mount alone.
🎯 Best Practices¶
1. Backup strategy:¶
Daily:
- PostgreSQL database dump
- LXC backup (Proxmox vzdump)
Weekly:
- Photo library verify (SnapRAID scrub)
Monthly:
- Offsite backup (photos)
2. ML Job schedule:¶
Run overnight (less resource competition):
- Smart Search: 02:00
- Face Detection: 03:00
- Duplicate Detection: 04:00
Administration → Jobs → Schedule (if available)
3. Storage management:¶
Weekly check:
- Disk usage: df -h /mnt/storage/immich/
- Database size: du -sh /mnt/storage/immich/pgdata/
- Photo count: Immich dashboard
If 80%+ usage:
- Duplicate detection + delete
- Old photo archiving
- Storage expansion
4. Security:¶
API Keys:
✅ Separate key for each device
✅ Descriptive names (TV App, Phone, CLI)
✅ Revoke if compromised
Accounts:
✅ Strong passwords
✅ 2FA (when available)
Network:
✅ Local network only (NO port forward!)
✅ Tailscale VPN for remote access
❌ DO NOT expose publicly! (security risk)
5. Monitoring:¶
Weekly check:
- Container health: docker compose ps
- RAM usage: free -h
- Swap usage: swapon --show
- Logs: docker compose logs --tail=50
Monthly:
- Immich version check
- Database performance
- Storage health (SMART)
📈 System Status Summary¶
Final configuration:¶
Proxmox Host (192.168.0.109):
├─ Swap: swappiness=10 ✅
│
├─ LXC 100 - Docker (192.168.0.110):
│ ├─ Swap: 4GB ✅
│ ├─ iGPU: Intel QuickSync ✅
│ │
│ └─ Immich (http://192.168.0.110:2283):
│ ├─ Server (1G RAM limit) ✅
│ ├─ ML (2G RAM limit + iGPU) ✅
│ ├─ PostgreSQL (512M limit, HDD optimized) ✅
│ ├─ Redis (256M limit) ✅
│ └─ Storage: /mnt/storage/immich/ ✅
│
├─ LXC 102 - AdGuard (192.168.0.111) ✅
└─ VM 101 - Home Assistant (192.168.0.202) ✅
Features:¶
✅ Photo/Video management
✅ AI-powered search (CLIP)
✅ Face recognition
✅ Partner Sharing
✅ Mobile app auto-backup
✅ TV app viewing (API Key)
✅ Timeline, Albums, Memories
✅ Geo-location map
✅ RAW support
✅ Hardware acceleration (iGPU)
🎉 Installation Complete!¶
What we achieved:
✅ Immich installed - WebUI working (http://192.168.0.110:2283)
✅ ML features - Face detection, CLIP search, object recognition
✅ iGPU acceleration - 3-4x faster ML processing
✅ Resource limits - RAM overflow protection
✅ Swap configuration - OOM Killer protection
✅ Partner Sharing - Family use
✅ Mobile app - Auto-backup from phone
✅ TV app - API Key viewing
✅ Storage - MergerFS pool + SnapRAID protection
COMPLETE SELF-HOSTED GOOGLE PHOTOS ALTERNATIVE! 🏆✨
Created: 2025-12-26
Version: 1.1
System: Proxmox VE 9.1 / Immich v3.1.0 / Docker