Documents the FolderSync/PhotoSync → UNAS Pro → Immich external library pattern as the workaround for immich-app/immich#4282 (deleted photos re-uploading on next mobile sync). Includes all steps: UNAS share creation, Proxmox NFS mount, LXC bind mount, docker-compose volume, Immich library import path update, and per-app config for Android (FolderSync) and iOS (PhotoSync). - Adds docs/immich/mobile-sync.md with full step-by-step guide - Adds mobile_photos to share map in docs/unas/nfs-mounts.md - Adds IMMICH_MOBILE_SYNC_DIR volume (/mnt/mobile:ro) to template.yaml - Documents IMMICH_MOBILE_SYNC_DIR in README.md and .env.sample
132 lines
5.7 KiB
Markdown
132 lines
5.7 KiB
Markdown
# Immich
|
|
|
|
Self-hosted photo and video management solution with high performance and visual aesthetics.
|
|
|
|
## Prerequisites
|
|
|
|
- **NVIDIA GPU:** This template is configured for NVIDIA GPU acceleration (RTX A400).
|
|
- **Proxmox LXC:** Must be a **Privileged** LXC with NVIDIA drivers and Container Toolkit installed.
|
|
- **Storage:** NFS-backed storage for photo uploads is recommended.
|
|
|
|
## Deployment
|
|
|
|
1. **Update `.env`**:
|
|
Ensure the Immich variables are set in your root `.env` file. `IMMICH_UPLOAD_LOCATION` should point to your dedicated Immich share on the UNAS:
|
|
```dotenv
|
|
IMMICH_UPLOAD_LOCATION=/mnt/unas/immich_data
|
|
IMMICH_DATA_DIR=/opt/immich/appdata
|
|
IMMICH_EXTERNAL_LIBRARY=/mnt/unas/immich_data/upload/upload
|
|
IMMICH_MOBILE_SYNC_DIR=/mnt/unas/mobile_photos
|
|
IMMICH_VERSION=release
|
|
IMMICH_DB_PASSWORD=<your-secure-password>
|
|
IMMICH_DB_USERNAME=immich
|
|
IMMICH_DB_DATABASE_NAME=immich
|
|
IMMICH_DB_HOSTNAME=database
|
|
IMMICH_REDIS_HOSTNAME=redis
|
|
```
|
|
|
|
Optional OIDC settings can also be supplied. Set `OIDC_ENABLED=true`
|
|
explicitly when enabling OAuth; leaving it unset keeps Immich local-login
|
|
only. Zitadel with PKCE does not require a client secret — leave
|
|
`OIDC_CLIENT_SECRET` empty.
|
|
```dotenv
|
|
OIDC_ENABLED=true
|
|
OIDC_ISSUER=https://zitadel.example.com/
|
|
OIDC_CLIENT_ID=<immich-client-id>
|
|
OIDC_CLIENT_SECRET=
|
|
OIDC_NAME=<button-label>
|
|
OIDC_AUTO_REDIRECT=false
|
|
OIDC_MOBILE_OVERRIDE_ENABLED=false
|
|
OIDC_DISABLE_LOCAL_AUTH=false
|
|
```
|
|
|
|
Once OIDC is confirmed working, set `OIDC_DISABLE_LOCAL_AUTH=true` and
|
|
restart the server to enforce SSO-only login:
|
|
```bash
|
|
sed -i 's/DISABLE_PASSWORD_LOGIN=false/DISABLE_PASSWORD_LOGIN=true/' /opt/immich/docker-compose.yml
|
|
docker compose -f /opt/immich/docker-compose.yml up -d --no-deps --force-recreate immich-server
|
|
```
|
|
|
|
2. **Create Directories**:
|
|
Create the local appdata directories and the shared media directory:
|
|
```bash
|
|
# Local config/database storage (matches IMMICH_DATA_DIR)
|
|
sudo mkdir -p /opt/immich/appdata/{postgres,model-cache}
|
|
sudo chown -R 1000:1000 /opt/immich/appdata
|
|
|
|
# Separate UNAS share for photos
|
|
sudo mkdir -p /mnt/unas/immich_data
|
|
sudo chown -R 1000:1000 /mnt/unas/immich_data
|
|
```
|
|
|
|
3. **Mounting the Share**:
|
|
Ensure you have created a separate NFS share on your UNAS (e.g., `immich_data`) and mounted it on your Proxmox host at `/mnt/unas/immich_data` before starting the containers.
|
|
|
|
4. **Render Compose File**:
|
|
```bash
|
|
./substitute_env.sh docker-compose-files/immich/template.yaml docker-compose.immich.yml .env
|
|
```
|
|
|
|
5. **Start Stack**:
|
|
```bash
|
|
docker compose -f docker-compose.immich.yml up -d
|
|
```
|
|
|
|
## External Library Setup
|
|
|
|
After the stack is running, create an external library via the API to import photos from `IMMICH_EXTERNAL_LIBRARY` (mounted read-only at `/mnt/external` inside the container):
|
|
|
|
```bash
|
|
TOKEN=$(curl -s -X POST http://<immich-ip>:2283/api/auth/login \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"email":"<email>","password":"<password>"}' \
|
|
| python3 -c 'import sys,json; print(json.load(sys.stdin)["accessToken"])')
|
|
|
|
USER_ID=$(curl -s http://<immich-ip>:2283/api/users/me \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
| python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
|
|
|
LIB_ID=$(curl -s -X POST http://<immich-ip>:2283/api/libraries \
|
|
-H 'Content-Type: application/json' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-d "{\"name\":\"NAS Photos\",\"ownerId\":\"$USER_ID\",\"importPaths\":[\"/mnt/external/<user-uuid>\"]}" \
|
|
| python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
|
|
|
curl -s -X POST http://<immich-ip>:2283/api/libraries/$LIB_ID/scan \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
The `<user-uuid>` is the Immich user's UUID — find it at **Settings → Account** in the Immich UI, or via:
|
|
```bash
|
|
curl -s http://<immich-ip>:2283/api/users/me -H "Authorization: Bearer $TOKEN" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])'
|
|
```
|
|
|
|
> [!IMPORTANT]
|
|
> Immich rejects any external library import path that falls under `/data/upload/` (the upload volume). The separate `/mnt/external` mount exists specifically to work around this restriction — the same photos are accessible at both paths inside the container, but only `/mnt/external` is accepted for external libraries.
|
|
|
|
## Privileged Mode Rationale
|
|
|
|
Immich is deployed in a **Privileged LXC** to ensure:
|
|
- **GPU Access:** The `immich-machine-learning` container requires direct access to NVIDIA device nodes for CUDA compute.
|
|
- **VRAM Management:** CUDA memory allocation and GSP firmware communication are more stable in privileged mode.
|
|
- **Storage Mapping:** Simplified 1:1 UID/GID mapping for photo uploads on the UNAS NFS share.
|
|
|
|
## GPU Verification
|
|
|
|
Both `immich-server` and `immich-machine-learning` run with `runtime: nvidia`:
|
|
|
|
- **immich-server** — uses NVENC for hardware-accelerated H.264 video transcoding (`NVIDIA_DRIVER_CAPABILITIES=video,compute,utility`)
|
|
- **immich-machine-learning** — uses the `:release-cuda` image with `CUDAExecutionProvider` for CLIP embeddings, face detection, and OCR
|
|
|
|
Verify GPU access after startup:
|
|
|
|
```bash
|
|
# Confirm NVIDIA device visible in server (needed for NVENC)
|
|
docker exec immich_server ls /dev/nvidia0
|
|
|
|
# Confirm CUDA ONNX provider active in ML container
|
|
docker exec immich_machine_learning python3 -c 'import onnxruntime; print(onnxruntime.get_available_providers())'
|
|
# Expected: ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
|
|
```
|
|
|
|
After verifying, go to **Administration → Video Transcoding** in the Immich UI and confirm Hardware Acceleration is set to **NVENC**. If it shows QSV (from a prior config), change it to NVENC and re-run the Video Conversion job.
|