# Immich Postgres Diagnostics (No API Key) When you don't have an Immich API key handy, you can check server health, job progress, ML state, and config directly via the Postgres database inside the Immich Postgres container. ## Finding DB Credentials The DB creds are set on the Postgres container at creation time. Get them from Docker: ```bash docker inspect immich_postgres --format '{{json .Config.Env}}' | python3 -c " import json,sys env = json.load(sys.stdin) for e in env: if 'POSTGRES' in e.upper() or 'DB_' in e: print(e) " ``` Expected output: ``` POSTGRES_PASSWORD=ab496a...17a2 POSTGRES_USER=postgres POSTGRES_DB=immich ``` Run queries with: ```bash docker exec immich_postgres psql -U postgres -d immich -c "SQL HERE" ``` If password is required, pass via env: ```bash docker exec -e PGPASSWORD=PASS immich_postgres psql -U postgres -d immich -c "SQL HERE" ``` ## Job Queue Progress (per-asset job status) Immich v2.7+ tracks per-asset job completion in `asset_job_status`. This is NOT a traditional queue — it shows which assets have had each job type complete: ```sql SELECT COUNT(*) as total_assets, COUNT(*) FILTER (WHERE ajs."facesRecognizedAt" IS NOT NULL) as faces_done, COUNT(*) FILTER (WHERE ajs."metadataExtractedAt" IS NOT NULL) as metadata_done, COUNT(*) FILTER (WHERE ajs."duplicatesDetectedAt" IS NOT NULL) as dedup_done, COUNT(*) FILTER (WHERE ajs."ocrAt" IS NOT NULL) as ocr_done FROM asset a LEFT JOIN asset_job_status ajs ON a.id = ajs."assetId"; ``` **Table schema** (`\d asset_job_status`): ``` assetId | uuid | not null facesRecognizedAt | timestamp with time zone| metadataExtractedAt | timestamp with time zone| duplicatesDetectedAt | timestamp with time zone| ocrAt | timestamp with time zone| ``` ## System Metadata The `system_metadata` table holds Immich's key state as JSONB: ```sql SELECT * FROM system_metadata; ``` Key entries: | key | value (snippet) | purpose | |-----|-----------------|---------| | `system-config` | `{"ffmpeg": {"transcode":"disabled"}, "storageTemplate": {"enabled": true}}` | Server config | | `facial-recognition-state` | `{"lastRun": "2026-05-31T00:00:05.175Z"}` | Last ML face run | | `reverse-geocoding-state` | `{"lastUpdate": "...", "lastImportFileName": "cities500.txt"}` | Geo data freshness | | `version-check-state` | `{"checkedAt": "...", "releaseVersion": "v2.7.5"}` | Version tracking | | `memories-state` | `{"lastOnThisDayDate": "..."}` | Memories feature state | | `system-flags` | `{"mountChecks": {"thumbs": true, "upload": true, ...}}` | Mount health | | `admin-onboarding` | `{"isOnboarded": true}` | Admin setup status | To extract just the ML config: ```sql SELECT json_data -> 'machineLearning' as ml_config FROM system_metadata WHERE key='system-config'; ``` ## Asset & People Counts ```sql SELECT COUNT(*) as total_assets FROM asset; SELECT COUNT(*) as total_albums FROM album; SELECT COUNT(*) as total_people FROM person; ``` ## ML Container GPU Check The ML container uses ONNX Runtime (not PyTorch directly). Check GPU availability: ```bash docker exec immich_machine_learning python3 -c " import onnxruntime as ort print(f'ONNX Runtime: {ort.__version__}') print(f'Providers: {ort.get_available_providers()}') print(f'CUDA: {\"CUDAExecutionProvider\" in ort.get_available_providers()}') " ``` Check the container's configured device mode: ```bash docker inspect immich_machine_learning --format '{{json .Config.Env}}' | python3 -c " import json,sys env = json.load(sys.stdin) for e in env: if 'DEVICE' in e or 'GPU' in e or 'CUDA' in e: print(e) " ``` Expected output for GPU-enabled: ``` DEVICE=cuda NVIDIA_VISIBLE_DEVICES=all CUDA_VERSION=12.2.2 ``` ONNX Runtime 1.24+ typically offers `TensorrtExecutionProvider`, `CUDAExecutionProvider`, `CPUExecutionProvider`. If `CUDAExecutionProvider` is in the list, GPU ML is active. ## Quick All-in-One Health Check ```bash docker exec immich_postgres psql -U postgres -d immich -c " SELECT 'assets' as check, COUNT(*)::text FROM asset UNION ALL SELECT 'people', COUNT(*)::text FROM person UNION ALL SELECT 'albums', COUNT(*)::text FROM album UNION ALL SELECT 'faces_done', COUNT(*) FILTER (WHERE ajs.\"facesRecognizedAt\" IS NOT NULL)::text FROM asset a LEFT JOIN asset_job_status ajs ON a.id = ajs.\"assetId\" UNION ALL SELECT 'metadata_done', COUNT(*) FILTER (WHERE ajs.\"metadataExtractedAt\" IS NOT NULL)::text FROM asset a LEFT JOIN asset_job_status ajs ON a.id = ajs.\"assetId\" UNION ALL SELECT 'ocr_done', COUNT(*) FILTER (WHERE ajs.\"ocrAt\" IS NOT NULL)::text FROM asset a LEFT JOIN asset_job_status ajs ON a.id = ajs.\"assetId\" UNION ALL SELECT 'face_last_run', value->>'lastRun' FROM system_metadata WHERE key='facial-recognition-state' UNION ALL SELECT 'version', value->>'releaseVersion' FROM system_metadata WHERE key='version-check-state' UNION ALL SELECT 'transcoding', CASE WHEN value->'ffmpeg'->>'transcode' = 'disabled' THEN 'disabled' ELSE 'enabled' END FROM system_metadata WHERE key='system-config'; " ``` ## When to use Postgres diagnostics vs API | Situation | Best approach | |-----------|---------------| | You have an API key | `/api/jobs` endpoint (richer data: active, waiting, failed, paused counts) | | No API key / lost access | Postgres queries above | | Checking ML container health | `docker exec` + ONNX Runtime check | | GPU detection | `docker inspect` ML container env vars |