Files
2026-07-12 10:17:17 -04:00

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