234 lines
8.3 KiB
Markdown
234 lines
8.3 KiB
Markdown
# Immich API: Job Management & GPU Troubleshooting
|
|
|
|
> Session-specific commands for triggering ML jobs and diagnosing GPU acceleration on the Immich machine-learning container (v2.7.5+).
|
|
|
|
## Authentication
|
|
|
|
### Login (get bearer token)
|
|
|
|
```bash
|
|
LOGIN=$(curl -s -X POST http://localhost:2283/api/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"email":"admin@example.com","password":"your-password"}')
|
|
TOK=*** -e "console.log(JSON.parse(process.argv[1]).accessToken)" -- "$LOGIN")
|
|
```
|
|
|
|
The token is a bearer token, used as `Authorization: Bearer *** all subsequent API calls.
|
|
|
|
## Job Management
|
|
|
|
### List all job queues
|
|
|
|
```bash
|
|
curl -s http://localhost:2283/api/jobs -H "Authorization: Bearer ***
|
|
```
|
|
|
|
Returns each job name with `queueStatus` (isPaused, isActive) and `jobCounts` (active, completed, failed, delayed, waiting, paused).
|
|
|
|
### Trigger a specific job
|
|
|
|
```bash
|
|
curl -s -X PUT "http://localhost:2283/api/jobs/smartSearch" \
|
|
-H "Authorization: Bearer *** \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"command":"start"}'
|
|
```
|
|
|
|
To force re-process already-completed assets, use `{"command":"start","force":true}`.
|
|
|
|
### Available job names
|
|
|
|
- `smartSearch` — CLIP embeddings for natural language search
|
|
- `faceDetection` — detect faces in photos
|
|
- `facialRecognition` — group detected faces into people
|
|
- `metadataExtraction` — read EXIF dates, GPS, camera info
|
|
- `thumbnailGeneration` — create preview thumbnails
|
|
- `videoConversion` — transcode videos for streaming
|
|
- `ocr` — read text in photos
|
|
- `duplicateDetection` — find duplicate assets
|
|
- `sidecar` — process sidecar files (XMP, etc.)
|
|
- `library` — scan library for new/changed files
|
|
- `backupDatabase` — create DB backup
|
|
- `notifications` — process notification queue
|
|
- `storageTemplateMigration` — move files to organized folder structure
|
|
|
|
## GPU Verification
|
|
|
|
### Check ONNX Runtime CUDA availability inside container
|
|
|
|
```bash
|
|
docker exec immich_machine_learning bash -c \
|
|
'source /opt/venv/bin/activate && \
|
|
python -c "import onnxruntime; print(onnxruntime.get_device()); print(onnxruntime.get_available_providers())"'
|
|
```
|
|
|
|
Expected output for GPU-enabled:
|
|
```
|
|
GPU
|
|
['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']
|
|
```
|
|
|
|
### Check GPU device files in container
|
|
|
|
```bash
|
|
docker exec immich_machine_learning ls -la /dev | grep nvidia
|
|
```
|
|
|
|
Should show: `nvidia0`, `nvidiactl`, `nvidia-uvm`, `nvidia-uvm-tools`
|
|
|
|
### Verify compose GPU config is attached
|
|
|
|
```bash
|
|
docker inspect immich_machine_learning --format '{{json .HostConfig.DeviceRequests}}'
|
|
```
|
|
|
|
Should show: `[{"Driver":"nvidia","Count":-1,...}]` for `count: all`
|
|
|
|
Check runtime: `docker inspect immich_machine_learning --format '{{json .HostConfig.Runtime}}'`
|
|
|
|
### Immich server container has Node.js, not Python
|
|
|
|
For inline JSON parsing inside `docker exec immich_server` commands, use Node:
|
|
|
|
```bash
|
|
# Instead of python3 -c (not available), use:
|
|
docker exec immich_server node -e "console.log(JSON.parse(process.argv[1]).accessToken)" -- "$JSON_DATA"
|
|
```
|
|
|
|
## Docker Networking Recovery
|
|
|
|
If the Immich server cannot reach its database or redis (EHOSTUNREACH errors):
|
|
```
|
|
Error: connect EHOSTUNREACH 172.18.0.3:5432
|
|
Error: connect EHOSTUNREACH 172.18.0.4:6379
|
|
```
|
|
|
|
**Fix:** Restart the stack in dependency order:
|
|
```bash
|
|
cd /opt/immich && docker compose restart database redis
|
|
# Wait 5s for DB/redis to be ready
|
|
docker compose restart immich-server immich-machine-learning
|
|
```
|
|
|
|
## Docker Compose GPU Configuration (v2 format)
|
|
|
|
**Required** for the `v2-cuda` ML image to actually access the GPU:
|
|
|
|
```yaml
|
|
immich-machine-learning:
|
|
container_name: immich_machine_learning
|
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda
|
|
volumes:
|
|
- model-cache:/cache
|
|
env_file:
|
|
- .env
|
|
restart: always
|
|
healthcheck:
|
|
disable: false
|
|
deploy:
|
|
resources:
|
|
reservations:
|
|
devices:
|
|
- driver: nvidia
|
|
count: all
|
|
capabilities: [gpu]
|
|
```
|
|
|
|
The `v2-cuda` image already has `DEVICE=cuda` and `NVIDIA_VISIBLE_DEVICES=all` in its Dockerfile ENV — no need to add those in compose. The missing piece is the `deploy.resources.reservations.devices` block.
|
|
|
|
**⚠️ Container has no `curl`:** The ML container has no `curl` or `ping` installed. For network tests, use Python's `urllib.request` from inside the activated venv:
|
|
```bash
|
|
docker exec immich_machine_learning bash -c 'source /opt/venv/bin/activate && python -c "
|
|
import urllib.request
|
|
r = urllib.request.urlopen('\''https://huggingface.co'\'', timeout=10)
|
|
print(r.status)
|
|
"'
|
|
```
|
|
|
|
## GPU Functional Verification
|
|
|
|
### Layered diagnostic procedure
|
|
|
|
When the ML container shows models failing to load, use this progression to isolate the issue:
|
|
|
|
**Layer 1 — GPU hardware is exposed to container:**
|
|
```bash
|
|
docker exec immich_machine_learning ls -la /dev | grep nvidia
|
|
# Must show: nvidia0, nvidiactl, nvidia-uvm
|
|
```
|
|
|
|
**Layer 2 — ONNX Runtime detects CUDA providers:**
|
|
```bash
|
|
docker exec immich_machine_learning bash -c 'source /opt/venv/bin/activate && python -c "
|
|
import onnxruntime
|
|
print(\"Device:\", onnxruntime.get_device())
|
|
print(\"Providers:\", onnxruntime.get_available_providers())
|
|
"'
|
|
# Expected: Device=GPU, Providers=[TensorrtExecutionProvider, CUDAExecutionProvider, CPUExecutionProvider]
|
|
```
|
|
|
|
**Layer 3 — Actual CUDA inference works:**
|
|
```bash
|
|
docker exec immich_machine_learning bash -c 'source /opt/venv/bin/activate && python -c "
|
|
import onnxruntime as ort, numpy as np, onnx
|
|
from onnx import helper, TensorProto
|
|
|
|
# Build a minimal model (Relu) and run it on CUDA
|
|
X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [1, 3])
|
|
Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [1, 3])
|
|
node = helper.make_node(\"Relu\", [\"X\"], [\"Y\"])
|
|
graph = helper.make_graph([node], \"test\", [X], [Y])
|
|
model = helper.make_model(graph)
|
|
|
|
sess = ort.InferenceSession(model.SerializeToString(),
|
|
providers=[\"CUDAExecutionProvider\", \"CPUExecutionProvider\"])
|
|
result = sess.run(None, {\"X\": np.array([[-2.0, 0.0, 2.0]], dtype=np.float32)})
|
|
print(\"CUDA inference OK:\", result[0])
|
|
"'
|
|
# Expected: CUDA inference OK: [[0. 0. 2.]]
|
|
```
|
|
|
|
**Layer 4 — Load a real CLIP model on GPU (after download):**
|
|
```bash
|
|
# Download the CLIP model first (run this step if not yet cached)
|
|
docker exec immich_machine_learning bash -c 'source /opt/venv/bin/activate && python -c "
|
|
from huggingface_hub import snapshot_download, os
|
|
cache_dir = \"/cache/clip/ViT-B-32__openai\"
|
|
os.makedirs(cache_dir, exist_ok=True)
|
|
snapshot_download(\"immich-app/ViT-B-32__openai\", cache_dir=cache_dir, local_dir=cache_dir,
|
|
ignore_patterns=[\"*.armnn\", \"*.rknn\"], max_workers=2)
|
|
"'
|
|
|
|
# Then load and run inference on GPU
|
|
docker exec immich_machine_learning bash -c 'source /opt/venv/bin/activate && python -c "
|
|
import onnxruntime as ort, numpy as np, time, os
|
|
model_path = \"/cache/clip/ViT-B-32__openai/visual/model.onnx\"
|
|
print(f\"Model: {os.path.getsize(model_path)/1024/1024:.0f} MB\")
|
|
|
|
so = ort.SessionOptions()
|
|
so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
|
sess = ort.InferenceSession(model_path, sess_options=so,
|
|
providers=[\"CUDAExecutionProvider\", \"CPUExecutionProvider\"],
|
|
provider_options=[{\"device_id\": \"0\", \"gpu_mem_limit\": 3*1024*1024*1024}, {}])
|
|
print(f\"Loaded in {time.time():.0f}s\")
|
|
|
|
# Run one inference
|
|
data = np.random.randn(1, 3, 224, 224).astype(np.float32)
|
|
start = time.time()
|
|
outputs = sess.run(None, {sess.get_inputs()[0].name: data})
|
|
print(f\"Inference: {outputs[0].shape} in {(time.time()-start)*1000:.0f}ms\")
|
|
"'
|
|
# Expected: ~0.8s load, ~237ms inference, output shape (1, 512)
|
|
```
|
|
|
|
### Common failures at each layer
|
|
|
|
| Layer | Failure Symptom | Likely Cause |
|
|
|-------|----------------|--------------|
|
|
| 1 | No nvidia device files | GPU not configured in compose — add `deploy.resources.reservations.devices` |
|
|
| 1 | nvidia0 present but nvidia-smi not found | Container image has drivers but no nvidia-smi binary (normal for Immich ML image) |
|
|
| 2 | Only CPUExecutionProvider | GPU not exposed to container (wrong runtime / no device requests) |
|
|
| 3 | ONNX session creation fails | CUDA version mismatch (container CUDA 12.2 vs host driver) or OOM |
|
|
| 4 | Model download OK but load fails | Disk space, corrupt download, or model file mismatch. Check /cache mounts |
|
|
| 4 | Download loop (clear→retry→fail) | Multiple models writing to same cache_dir, overwriting each other's files. Try MACHINE_LEARNING_MODEL_ARENA=true
|