Jesse A. Green / notebook

Immich — Administrator & Developer Guide

Generated May 2026 from immich-app/immich at 6aadb7b

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Immich is a self-hosted photo and video management platform — a privacy-first alternative to Google Photos. This guide covers every aspect of running, operating, and extending Immich: architecture internals, configuration reference, deployment patterns, security hardening, and developer integration. Version covered: 2.7.5 (AGPL-3.0).

Note

This is the Admin & Developer Guide. For end-user workflows — uploading, sharing, albums, mobile backup — see the Immich User Guide.


1. Architecture Overview

1.1 High-Level Topology

Immich is a monorepo product composed of six runtime components:

┌────────────────────────────────────────────────────────┐
│  Clients: Web (SvelteKit), Mobile (Flutter), CLI       │
└────────────────┬───────────────────────────────────────┘
                 │ HTTP/WebSocket (port 2283)
┌────────────────▼───────────────────────────────────────┐
│  immich-server  (NestJS / Node.js 24+)                 │
│  ├─ API Worker        (HTTP + WebSocket)               │
│  ├─ Microservices Worker  (BullMQ consumers)           │
│  └─ Maintenance Worker    (backup + schema ops)        │
└──────┬──────────────┬─────────────────────────────────┘
       │              │
┌──────▼──────┐  ┌────▼────────────────────────────────┐
│  PostgreSQL  │  │  Redis / Valkey  (BullMQ broker)    │
│  (port 5432) │  │  (port 6379)                        │
└─────────────┘  └─────────────────────────────────────┘
       │
┌──────▼──────────────────────────────────────────────┐
│  immich-machine-learning  (Python / FastAPI)         │
│  Gunicorn + Uvicorn, port 3003                      │
│  ONNX Runtime — CLIP · FaceNet · OCR                │
└─────────────────────────────────────────────────────┘

All persistent state lives in PostgreSQL and the media filesystem. Redis is ephemeral — losing it causes no data loss, only in-flight job loss.

1.2 immich-server — NestJS Runtime

The server binary runs as three distinct worker types (controlled by environment variables):

Worker Enum Value Responsibilities
Api ImmichWorker.Api HTTP REST, WebSocket, SvelteKit SSR, file upload
Microservices ImmichWorker.Microservices BullMQ job consumers: thumbnails, ML, metadata, transcoding
Maintenance ImmichWorker.Maintenance Database backup, schema integrity, maintenance auth

A single container runs all three workers by default. Separate them at scale using IMMICH_WORKERS_INCLUDE / IMMICH_WORKERS_EXCLUDE.

Key server characteristics:

  • Framework: NestJS with full DI, Guards, Interceptors, and BullMQ integration
  • Static asset serving: sirv for the compiled SvelteKit bundle
  • Request timeout: 24 hours (accommodates very large video uploads)
  • Security headers: Helmet middleware (configurable via IMMICH_HELMET_FILE)
  • Validation: ZodValidationPipe + ZodSerializerInterceptor
  • OpenAPI spec: auto-generated from decorators

1.3 NestJS Module Architecture

ApiModule (HTTP, port 2283)
├── 64 controllers: activity, album, api-key, asset, auth,
│   download, face, job, library, map, memory, notification,
│   partner, person, plugin, queue, search, server,
│   session, shared-link, stack, sync, system-config,
│   system-metadata, tag, timeline, trash, user, version,
│   view, workflow …
├── 48 services (all extending BaseService)
├── 58 repository files (Kysely-backed)
├── Middleware: AuthGuard, ErrorInterceptor,
│   FileUploadInterceptor, LoggingInterceptor,
│   GlobalExceptionFilter
└── ZodValidationPipe, ZodSerializerInterceptor, BullMQ queues

MicroservicesModule (background workers)
├── Same services + repositories (no controllers)
└── BullMQ queue processors

MaintenanceModule (privileged ops)
├── Config, Logging, Storage, Process, Database, User,
│   SystemMetadata, App repos
├── MaintenanceWorkerController
├── DatabaseBackupService
└── MaintenanceAuthGuard

ImmichAdminModule (CLI, no HTTP)
├── All services + repositories
└── nest-commander commands

1.4 immich-machine-learning — Python Service

The ML service runs as a standalone Python/FastAPI application:

  • Runtime: Gunicorn (process manager) + Uvicorn (ASGI workers)
  • Inference engine: ONNX Runtime
  • Default port: 3003 (overridden by IMMICH_PORT)
  • Model cache: in-memory with configurable TTL (default 300 seconds)
  • ANN: Approximate nearest-neighbor search for fast CLIP embedding lookup

Models used by default:

Task Default Model Notes
Smart search (CLIP) ViT-B-32__openai 512-dimension embeddings
Facial recognition buffalo_l Min score 0.7, max distance 0.5
OCR PP-OCRv5_mobile Text in images
Duplicate detection (CLIP embeddings reused) Max distance 0.01

Hardware acceleration backends (ONNX Runtime execution providers):

Backend Compose override Use case
CUDA hwaccel.ml.yml → cuda NVIDIA GPU
ROCm hwaccel.ml.yml → rocm AMD GPU
OpenVINO hwaccel.ml.yml → openvino / openvino-wsl Intel hardware
ARM NN hwaccel.ml.yml → armnn Arm SoCs
RKNN hwaccel.ml.yml → rknn Rockchip NPU
CPU (default) hwaccel.ml.yml → cpu Software inference

1.5 PostgreSQL — Database Layer

Immich requires a custom PostgreSQL image (ghcr.io/immich-app/postgres:14-vectorchord) that ships with vector extensions pre-installed. Using a stock PostgreSQL image will fail at startup.

Requirement Value
Minimum version >=14.0.0
VectorChord >=0.3 <2
pgvecto.rs >=0.2 <0.4 (alternative)
pgvector >=0.5 <1 (alternative)
Additional extensions cube, earthdistance (geo queries)
Recommended shm_size 128mb
Data checksums Enabled via POSTGRES_INITDB_ARGS=--data-checksums

Important

Always use the Immich-provided PostgreSQL image. The vector extensions (VectorChord, pgvecto.rs, pgvector) are required for smart search and facial recognition. A vanilla PostgreSQL container will not work.

1.6 Redis / Valkey — Queue Broker

  • Image: valkey:9
  • Role: BullMQ job queue broker only (no application data stored)
  • Health check: redis-cli ping
  • Losing Redis data does not lose photos or user data — it only loses queued jobs that have not yet been processed. Jobs will be re-queued on next trigger.

1.7 BullMQ Job Queues

All background processing is driven by BullMQ queues backed by Redis. Default concurrency values (tunable via SystemConfig):

Queue Default Concurrency Purpose
backgroundTask 5 Housekeeping tasks
smartSearch 2 CLIP embedding generation
metadataExtraction 5 EXIF, GPS, XMP extraction
faceDetection 2 Facial recognition pipeline
search 5 Search index updates
sidecar 5 XMP sidecar file handling
library 5 External library scanning
migration 5 Asset file migrations
thumbnailGeneration 3 Thumbnail + preview generation
videoConversion 1 FFmpeg transcoding
notification 5 Email / push notifications
ocr 1 OCR text extraction
workflow 5 User-defined workflows
editor 2 Edit operations

Tip

videoConversion runs at concurrency 1 by default — video transcoding is CPU/GPU-bound. Increase this only if you have hardware acceleration configured and sufficient resources.

1.8 Web Frontend — SvelteKit

The web frontend is a SvelteKit application served via SSR from the immich-server process using sirv for static assets. The SvelteKit app uses Svelte 5 runes and file-based routing. It communicates with the backend entirely through the REST API (auto-generated TypeScript SDK from the OpenAPI spec).

1.9 Mobile App — Flutter

The Flutter mobile app (iOS + Android) handles background photo backup, offline viewing, and full-featured library management. The mobile app communicates with the same REST API as the web frontend. OAuth callback URL: app.immich:///oauth-callback.

1.10 CLI — @immich/cli

A separate CLI package built with Commander.js that wraps the auto-generated TypeScript SDK. The CLI is distinct from the immich-admin server-side admin CLI described in Section 5.


2. System Requirements

2.1 Software Requirements

Component Minimum Notes
Docker Latest stable Docker Compose v2 required
Docker Compose v2.x docker compose (not docker-compose)
Node.js (server) 24+ Bundled inside container
Python (ML) 3.x Bundled inside container
PostgreSQL 14.0.0+ Must use Immich custom image
Redis/Valkey 7+ Valkey 9 recommended

2.2 Hardware Requirements

Minimum (small home deployment):

Resource Minimum Recommended
CPU 2 cores 4+ cores
RAM 4 GB 8 GB
Storage Sufficient for media library SSD preferred for database
Network 100 Mbps 1 Gbps for fast bulk import

Notes:

  • ML inference (CLIP, face detection) is compute-intensive. Dedicated ML hardware acceleration dramatically reduces processing time for large libraries.
  • The PostgreSQL vector index (cube, VectorChord) requires RAM proportional to library size.
  • Video transcoding (videoConversion queue) is the most resource-intensive background task. Hardware acceleration (NVENC, QuickSync, VAAPI) is strongly recommended for libraries with significant video content.

2.3 Storage Layout

Default storage paths inside the container:

Path Purpose
/usr/src/app/upload/ Default media location
/usr/src/app/upload/library/ Original asset files
/usr/src/app/upload/thumbs/ Thumbnails and previews
/usr/src/app/upload/profile/ User profile images
/usr/src/app/upload/backups/ Database backups
~/.cache/immich_ml/ ML model cache (ML container)

Override the media root with IMMICH_MEDIA_LOCATION.

Warning

Always bind-mount the media directory to a persistent volume. Container restarts with no volume will destroy all uploaded assets.


3. Configuration Reference

Immich has two layers of configuration:

  1. Environment variables — set at container start, control infrastructure connections and runtime behavior
  2. SystemConfig — runtime configuration stored in PostgreSQL, editable via the web UI Admin panel or REST API

When IMMICH_CONFIG_FILE is set, runtime configuration is loaded from a YAML/JSON file on disk instead of the database. You cannot update SystemConfig via the UI or API while a config file is in use.

3.1 Server Environment Variables

Variable Default Description
IMMICH_PORT 2283 HTTP server listen port
IMMICH_ENV — Environment tag (e.g., testing)
IMMICH_LOG_LEVEL log Log level for the server process
IMMICH_MACHINE_LEARNING_ENABLED true Disable ML entirely by setting to false
IMMICH_MACHINE_LEARNING_URL http://immich-machine-learning:3003 ML service endpoint
IMMICH_MEDIA_LOCATION /usr/src/app/upload Override media storage root
IMMICH_PROCESS_INVALID_IMAGES — Process images that fail normal validation
IMMICH_ALLOW_EXTERNAL_PLUGINS — Enable external plugin loading
IMMICH_ALLOW_SETUP — Allow initial admin setup
IMMICH_WORKERS_INCLUDE — Comma-separated list of workers to enable
IMMICH_WORKERS_EXCLUDE — Comma-separated list of workers to disable
IMMICH_TRUSTED_PROXIES — CIDR list of trusted reverse proxy IPs
IMMICH_API_METRICS_PORT — Prometheus metrics port for API worker
IMMICH_MICROSERVICES_METRICS_PORT — Prometheus metrics port for microservices worker
IMMICH_TELEMETRY_INCLUDE — Set to all to enable all metrics
IMMICH_TELEMETRY_EXCLUDE — Telemetry exclusions
IMMICH_HELMET_FILE — Path to Helmet configuration file
IMMICH_CONFIG_FILE — Path to YAML/JSON config file (disables DB config)
NO_COLOR — Disable ANSI color in log output

3.2 Database Environment Variables

Variable Default Description
DB_URL — Full connection URL (overrides individual fields)
DB_HOSTNAME database PostgreSQL hostname
DB_USERNAME postgres Database user
DB_PASSWORD postgres Database password
DB_DATABASE_NAME immich Database name
DB_SSL_MODE — SSL mode (require, verify-full, etc.)
DB_SKIP_MIGRATIONS — Skip automatic schema migrations on startup

Important

Change DB_PASSWORD from the default postgres before exposing Immich to any network. The default credentials are well-known.

3.3 Redis Environment Variables

Variable Default Description
REDIS_URL — Full Redis/Valkey URL (overrides individual fields)
REDIS_HOSTNAME redis Redis hostname
REDIS_USERNAME — Redis username (for ACL-enabled Redis)
REDIS_PASSWORD — Redis password

3.4 Machine Learning Environment Variables

Variable Default Description
IMMICH_HOST [::] ML service bind host
IMMICH_PORT 3003 ML service listen port
IMMICH_LOG_LEVEL info ML log level
MACHINE_LEARNING_CACHE_FOLDER ~/.cache/immich_ml Model download and cache directory
MACHINE_LEARNING_MODEL_TTL 300 Seconds before unloading idle models
MACHINE_LEARNING_MODEL_TTL_POLL_S 10 Seconds between TTL checks
MACHINE_LEARNING_WORKERS 1 Number of Gunicorn worker processes
MACHINE_LEARNING_WORKER_TIMEOUT 300 Gunicorn worker timeout (seconds)
MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S 2 Keep-alive timeout (seconds)
MACHINE_LEARNING_REQUEST_THREADS CPU count ONNX request threads per worker
MACHINE_LEARNING_MODEL_INTER_OP_THREADS 0 ONNX inter-op parallelism (0 = auto)
MACHINE_LEARNING_MODEL_INTRA_OP_THREADS 0 ONNX intra-op parallelism (0 = auto)
MACHINE_LEARNING_ANN true Enable ANN index for fast search
MACHINE_LEARNING_ANN_FP16_TURBO false FP16 turbo mode for ANN
MACHINE_LEARNING_ANN_TUNING_LEVEL 2 ANN accuracy vs. speed (higher = slower, more accurate)
MACHINE_LEARNING_RKNN true Enable RKNN (Rockchip NPU)
MACHINE_LEARNING_RKNN_THREADS 1 RKNN thread count
MACHINE_LEARNING_PRELOAD__CLIP — Model name to preload at startup
MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION — Face model name to preload at startup
MACHINE_LEARNING_DEVICE_ID 0 Primary GPU device ID
MACHINE_LEARNING_DEVICE_IDS — Comma-separated list for multi-GPU
MACHINE_LEARNING_OPENVINO_PRECISION — FP32 or FP16 for OpenVINO
MACHINE_LEARNING_ROCM_PRECISION — FP32 or FP16 for ROCm

3.5 SystemConfig Reference (Runtime Configuration)

SystemConfig is stored in PostgreSQL and can be edited via the Administration → Settings panel in the web UI. All values below are defaults.

3.5.1 Backup

Key Default Description
backup.database.enabled true Enable scheduled database backups
backup.database.cronExpression EVERY_DAY_AT_2AM Backup schedule
backup.database.keepLastAmount 14 Number of backups to retain

3.5.2 FFmpeg / Video Transcoding

Key Default Description
ffmpeg.crf 23 Constant Rate Factor (quality; lower = better quality, larger file)
ffmpeg.threads 0 Encoding threads (0 = auto)
ffmpeg.preset ultrafast FFmpeg encoding preset
ffmpeg.targetVideoCodec H264 Output video codec
ffmpeg.targetAudioCodec AAC Output audio codec
ffmpeg.targetResolution 720 Target resolution height (pixels)
ffmpeg.maxBitrate 0 Max bitrate (0 = unlimited)
ffmpeg.transcode Required When to transcode: Required, Optimal, All, Disabled
ffmpeg.accel Disabled Hardware acceleration backend
ffmpeg.tonemap Hable HDR tonemapping algorithm

3.5.3 Job Concurrency

Concurrency for each BullMQ queue is tunable at runtime. See Section 1.7 for defaults.

3.5.4 Logging

Key Default Description
logging.enabled true Enable application logging
logging.level Log Minimum log level

3.5.5 Machine Learning

Key Default Description
machineLearning.enabled true Enable ML features globally
machineLearning.urls — ML service URL(s)
machineLearning.clip.modelName ViT-B-32__openai CLIP model for smart search
machineLearning.duplicateDetection.maxDistance 0.01 Embedding distance threshold for duplicates
machineLearning.facialRecognition.modelName buffalo_l Face detection/recognition model
machineLearning.facialRecognition.minScore 0.7 Minimum confidence score
machineLearning.facialRecognition.maxDistance 0.5 Maximum embedding distance for clustering
machineLearning.facialRecognition.minFaces 3 Minimum faces to form a named person cluster
machineLearning.ocr.modelName PP-OCRv5_mobile OCR model

3.5.6 Maps & Geocoding

Key Default Description
map.enabled true Enable map view
map.lightStyle tiles.immich.cloud Tile source for light mode
map.darkStyle tiles.immich.cloud Tile source for dark mode
reverseGeocoding.enabled true Enable GPS-to-place-name lookup

3.5.7 OAuth / SSO

Key Default Description
oauth.autoLaunch false Redirect to OAuth provider on login page load
oauth.autoRegister true Auto-create accounts for new OAuth users
oauth.buttonText — Label for the OAuth sign-in button
oauth.clientId — OAuth client ID
oauth.clientSecret — OAuth client secret
oauth.scope openid email profile OAuth scopes to request
oauth.signingAlgorithm RS256 JWT signing algorithm
oauth.tokenEndpointAuthMethod client_secret_post Token endpoint auth method
oauth.timeout 30000 OAuth request timeout (ms)

3.5.8 Password Login

Key Default Description
passwordLogin.enabled true Allow username/password authentication

Caution

If you disable both passwordLogin and oauth, users (including admins) will be unable to log in. Use immich-admin enable-password-login to recover.

3.5.9 Storage Template

Key Default Description
storageTemplate.enabled false Organize files by template on disk
storageTemplate.template {{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}} Path template using date/metadata tokens

3.5.10 Image Quality

Key Default Description
image.thumbnail.format webp Thumbnail format
image.thumbnail.size 250 Thumbnail size (pixels, longest edge)
image.thumbnail.quality 80 Thumbnail quality (1–100)
image.preview.format jpeg Preview format
image.preview.size 1440 Preview size (pixels, longest edge)
image.preview.quality 80 Preview quality (1–100)
image.colorspace P3 Output colorspace
image.fullsize.enabled false Serve original-resolution images

3.5.11 Trash

Key Default Description
trash.enabled true Enable soft-delete trash bin
trash.days 30 Days before permanent deletion

3.5.12 Library

Key Default Description
library.scan.enabled true Enable scheduled external library scans
library.scan.cronExpression EVERY_DAY_AT_MIDNIGHT Scan schedule
library.watch.enabled false Enable filesystem watching (inotify)

3.5.13 Notifications (SMTP)

SMTP notifications are disabled by default. Configure via Administration → Notifications.

3.5.14 Server Behavior

Key Default Description
server.externalDomain — Public-facing domain for generated links
server.loginPageMessage — Custom message shown on login page
server.publicUsers true Whether user list is public within the instance

3.5.15 Nightly Tasks

Key Default Description
nightlyTasks.startTime 00:00 UTC time for nightly task window
nightlyTasks.databaseCleanup true Purge stale DB records
nightlyTasks.generateMemories true Generate "On This Day" memories
nightlyTasks.syncQuotaUsage true Recalculate storage quotas
nightlyTasks.missingThumbnails true Queue assets with missing thumbnails
nightlyTasks.clusterNewFaces true Cluster newly detected faces

3.5.16 User Management

Key Default Description
user.deleteDelay 7 Days to retain user data after account deletion
newVersionCheck.enabled true Check GitHub for new Immich releases
metadata.faces.import false Import face metadata from EXIF

4. Deployment & Scaling

4.1 Standard Docker Compose Deployment

The canonical production deployment uses docker/docker-compose.prod.yml:

# Key services and ports
services:
  immich-server:
    ports: ["2283:2283"]      # HTTP API + Web UI

  immich-machine-learning:
    ports: ["3003:3003"]      # ML API (internal only)

  redis:
    # Internal only — no exposed port

  database:
    ports: ["5432:5432"]     # PostgreSQL (restrict in production)

  immich-prometheus:
    ports: ["9090:9090"]     # Prometheus (restrict in production)

  immich-grafana:
    ports: ["3000:3000"]     # Grafana (restrict in production)

Minimal production .env:

# Database
DB_PASSWORD=change_me_strong_password
DB_USERNAME=immich
DB_DATABASE_NAME=immich

# Paths
UPLOAD_LOCATION=/path/to/your/media
DB_DATA_LOCATION=/path/to/postgres/data

4.2 Rootless Deployment

docker/docker-compose.rootless.yml adds security hardening:

services:
  immich-server:
    user: '1000:1000'
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - NET_RAW

Use rootless mode whenever the host allows it. Ensure the volume mount paths are owned by UID/GID 1000.

4.3 Splitting Workers for Scale

By default, a single immich-server container runs all three worker types. For high-load deployments, separate them:

# API-only container
IMMICH_WORKERS_INCLUDE=api docker compose up

# Microservices-only container (background jobs)
IMMICH_WORKERS_INCLUDE=microservices docker compose up

Note

When running split workers, all instances share the same PostgreSQL and Redis. The BullMQ queue ensures jobs are processed exactly once — multiple microservices workers safely consume from the same queues.

4.4 Hardware Acceleration — Video Transcoding

Override the base compose file with a hardware acceleration profile:

# NVIDIA GPU (NVENC)
docker compose -f docker-compose.yml -f hwaccel.transcoding.yml --profile nvenc up

# Intel Quick Sync
docker compose -f docker-compose.yml -f hwaccel.transcoding.yml --profile quicksync up

# AMD/Intel VAAPI (Linux)
docker compose -f docker-compose.yml -f hwaccel.transcoding.yml --profile vaapi up

# VAAPI on WSL2
docker compose -f docker-compose.yml -f hwaccel.transcoding.yml --profile vaapi-wsl up

# Rockchip RKMPP
docker compose -f docker-compose.yml -f hwaccel.transcoding.yml --profile rkmpp up

Then set ffmpeg.accel in SystemConfig to match the backend.

4.5 Hardware Acceleration — Machine Learning

# NVIDIA CUDA
docker compose -f docker-compose.yml -f hwaccel.ml.yml --profile cuda up

# AMD ROCm
docker compose -f docker-compose.yml -f hwaccel.ml.yml --profile rocm up

# Intel OpenVINO
docker compose -f docker-compose.yml -f hwaccel.ml.yml --profile openvino up

# Rockchip RKNN
docker compose -f docker-compose.yml -f hwaccel.ml.yml --profile rknn up

# ARM NN
docker compose -f docker-compose.yml -f hwaccel.ml.yml --profile armnn up

4.6 CLIP Model Selection

Immich ships with 40+ supported CLIP models. Models differ in embedding dimensions (512 to 1536), search quality, and inference speed.

Key model families:

Model Family Dimensions Notes
ViT-B-32__openai (default) 512 Fast, good quality
ViT-L-14__openai 768 Higher quality, slower
SigLIP variants 768–1152 Google's improved CLIP
SigLIP2 variants 768–1152 Latest Google CLIP
NLLB-CLIP variants 512–768 Multilingual support

Warning

Changing the CLIP model requires re-embedding your entire library. This is a compute-intensive operation. After changing the model in SystemConfig, trigger Jobs → Smart Search → Run All from the admin panel.

4.7 Reverse Proxy Configuration

Set IMMICH_TRUSTED_PROXIES to your reverse proxy's IP/CIDR to enable proper client IP forwarding:

IMMICH_TRUSTED_PROXIES=172.16.0.0/12,192.168.0.0/16

Set server.externalDomain in SystemConfig to the public URL so generated share links are correct.

nginx example:

location / {
    proxy_pass http://immich-server:2283;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    client_max_body_size 50000M;
    proxy_read_timeout 86400s;
    proxy_send_timeout 86400s;
}

The proxy_read_timeout must match Immich's 24-hour request timeout to support large video uploads.

4.8 File-Based Configuration

To use a configuration file instead of database-stored SystemConfig:

IMMICH_CONFIG_FILE=/etc/immich/config.json
{
  "ffmpeg": {
    "crf": 23,
    "preset": "ultrafast",
    "targetVideoCodec": "H264"
  },
  "machineLearning": {
    "enabled": true,
    "clip": { "modelName": "ViT-B-32__openai" }
  }
}

Caution

While IMMICH_CONFIG_FILE is set, the SystemConfig API and admin UI settings panel are disabled. Any attempt to update settings via the API returns: "Cannot update configuration while IMMICH_CONFIG_FILE is in use".


5. Operations

5.1 immich-admin CLI

The immich-admin CLI runs inside the server container and provides privileged administrative operations that bypass the HTTP API. Always use this CLI to recover from authentication lockout scenarios.

docker exec -it immich_server immich-admin <command>
Command Description
list-users List all user accounts with IDs and status
grant-admin <email> Grant admin privileges to a user
revoke-admin <email> Revoke admin privileges from a user
reset-admin-password Interactively reset the admin password
enable-password-login Re-enable password authentication
disable-password-login Disable password authentication (OAuth only)
enable-oauth-login Enable OAuth SSO
disable-oauth-login Disable OAuth SSO
enable-maintenance-mode Put server in maintenance mode
disable-maintenance-mode Exit maintenance mode
change-media-location Update DB file paths after storage migration
schema-check Validate database schema integrity
version Display installed Immich version

Common recovery workflows:

# Locked out — reset admin password
docker exec -it immich_server immich-admin reset-admin-password

# Disabled password login accidentally
docker exec -it immich_server immich-admin enable-password-login

# Promote a user to admin
docker exec -it immich_server immich-admin grant-admin [email removed]

# Verify DB schema after upgrade
docker exec -it immich_server immich-admin schema-check

5.2 Database Backups

Immich performs automated PostgreSQL backups on the schedule defined in backup.database.cronExpression (default: daily at 2 AM). Backups are stored in the backups/ subdirectory of the media location.

Retention: backup.database.keepLastAmount (default: 14 backups).

Manual backup via Docker:

docker exec immich_postgres pg_dumpall -U postgres > immich_backup_$(date +%Y%m%d).sql

Restore from backup:

docker exec -i immich_postgres psql -U postgres < immich_backup_20260513.sql

Warning

After restoring a database from backup on a new machine, run change-media-location if the media paths differ. Inconsistent paths produce the error: "Detected an inconsistent media location".

5.3 Database Migrations

Migrations run automatically on server startup unless DB_SKIP_MIGRATIONS=true. If the schema check fails:

  • Error "Detected schema drift" — see docs.immich.app/errors#schema-drift
  • Error "Invalid upgrade path" — see docs.immich.app/errors/#typeorm-upgrade

Never downgrade Immich without restoring a pre-upgrade database backup. Migrations are not reversible.

5.4 Job Management

The admin panel (Administration → Jobs) provides controls to:

  • Run, pause, or clear individual queues
  • View queue depths and processing status
  • Trigger full re-processing (e.g., re-embed all assets for a new CLIP model)

5.5 External Library Management

External libraries allow Immich to index photos from arbitrary filesystem paths without moving files. Configuration:

  1. Mount the external path into the container
  2. Create a Library in Administration → Libraries
  3. Configure path(s) for the library
  4. Run a scan (manual or scheduled via library.scan.cronExpression)

Filesystem watching (library.watch.enabled) uses inotify and reacts to changes in real time, but is disabled by default due to resource implications on large libraries.

5.6 Storage Template Migration

Enable storageTemplate.enabled to reorganize files on disk using a date/metadata template. Default template: {{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}.

Caution

Enabling storage templates moves files on disk. Ensure sufficient free space and a working backup before enabling. The migration runs as a background job in the migration queue.

5.7 Telemetry and Metrics

Immich exposes Prometheus metrics when telemetry is configured:

IMMICH_TELEMETRY_INCLUDE=all
IMMICH_API_METRICS_PORT=8081
IMMICH_MICROSERVICES_METRICS_PORT=8082

The production Docker Compose ships with immich-prometheus and immich-grafana pre-configured. Access Grafana at port 3000 to view dashboards for queue depths, job throughput, API latency, and ML processing times.

5.8 Audit Log

Immich maintains an audit log of user actions. The audit log retains events for a maximum of 100 days (AUDIT_LOG_MAX_DURATION).

5.9 Maintenance Mode

Maintenance mode prevents user operations while allowing admin tasks:

docker exec -it immich_server immich-admin enable-maintenance-mode
# ... perform maintenance ...
docker exec -it immich_server immich-admin disable-maintenance-mode

6. Performance

6.1 Database Tuning

The vector extension (VectorChord / pgvecto.rs) drives CLIP search performance. Key tuning parameters for PostgreSQL:

-- Verify extensions are installed
SELECT * FROM pg_extension WHERE extname IN ('vector', 'vectorchord', 'vectors');

-- Check vector index status
SELECT schemaname, tablename, indexname FROM pg_indexes
WHERE indexname LIKE '%embedding%';

PostgreSQL container settings:

  • shm_size: 128mb — required for vector operations
  • POSTGRES_INITDB_ARGS=--data-checksums — data integrity

For large libraries (100K+ assets), consider increasing shared_buffers and effective_cache_size in postgresql.conf.

6.2 Machine Learning Performance

Model preloading eliminates cold-start latency on first request after restart:

MACHINE_LEARNING_PRELOAD__CLIP=ViT-B-32__openai
MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION=buffalo_l

Multi-worker ML (if memory headroom exists):

MACHINE_LEARNING_WORKERS=2

Each Gunicorn worker loads models independently into memory. With 2 workers and both models loaded, expect substantially higher RAM usage.

Thread tuning for CPU-only inference:

# Let ONNX choose (usually best)
MACHINE_LEARNING_MODEL_INTER_OP_THREADS=0
MACHINE_LEARNING_MODEL_INTRA_OP_THREADS=0

# Or pin explicitly for a known CPU
MACHINE_LEARNING_MODEL_INTRA_OP_THREADS=4

ANN (Approximate Nearest Neighbor) is enabled by default and provides near-instant search after the index is built. Disable only to debug search accuracy:

MACHINE_LEARNING_ANN=false

6.3 Queue Concurrency Tuning

Tune job concurrency in SystemConfig based on your hardware:

Scenario Recommendation
Low-power NAS Reduce all concurrency to 1–2
High-core server, CPU transcoding Increase videoConversion to 2–4
GPU transcoding (NVENC) videoConversion = 4–8 (GPU handles parallelism)
Dedicated ML GPU smartSearch + faceDetection = 4–8
Slow storage Reduce thumbnailGeneration to 1–2

6.4 FFmpeg Optimization

For quality vs. size tradeoffs in systemConfig.ffmpeg:

# Better quality, larger files
crf: 18, preset: slow

# Balanced (default)
crf: 23, preset: ultrafast

# Smallest files
crf: 28, preset: ultrafast

HDR content: set tonemap to Hable (default), Reinhard, or Mobius depending on your display targets.

6.5 Network and Upload Performance

  • Large video uploads are protected by a 24-hour request timeout
  • For bulk initial imports, use the CLI upload tool rather than the web UI
  • For external libraries on NAS, ensure the mount uses appropriate network filesystem tuning (NFS with noatime, appropriate rsize/wsize)

7. Security

7.1 Authentication Architecture

Immich supports two authentication mechanisms:

Method Config Key Default
Password login passwordLogin.enabled true
OAuth/OIDC SSO oauth.clientId + related disabled

Password hashing: bcrypt with SALT_ROUNDS = 10.

Session management: Sessions are tracked in the database. Admins can revoke sessions via Administration → Users → Sessions.

API Keys: Users generate API keys from their profile. Keys are stored as bcrypt hashes.

7.2 OAuth / OIDC Configuration

Immich implements OIDC (OpenID Connect). Configure in Administration → Settings → Authentication:

clientId:      <your OIDC client ID>
clientSecret:  <your OIDC client secret>
scope:         openid email profile
signingAlgorithm: RS256
tokenEndpointAuthMethod: client_secret_post

Auto-registration (oauth.autoRegister: true): New users logging in via OAuth are automatically provisioned. Disable if you want to pre-create all accounts.

Auto-launch (oauth.autoLaunch: false): When true, the login page immediately redirects to the OAuth provider.

Mobile OAuth callback URL: app.immich:///oauth-callback

Important

Known issue (#26839): OAuth account linking fails if email addresses differ in case between the identity provider and Immich. Ensure your IdP sends lowercase email addresses, or normalize on the IdP side.

7.3 HTTP Security Headers

Immich applies Helmet middleware by default, which sets standard security headers including Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, and Strict-Transport-Security (when TLS is terminated upstream).

Customize Helmet configuration by providing a JSON file and setting IMMICH_HELMET_FILE.

7.4 Trusted Proxies

When running behind a reverse proxy, set IMMICH_TRUSTED_PROXIES to the proxy's IP range so client IPs are correctly recorded in audit logs:

IMMICH_TRUSTED_PROXIES=10.0.0.1/32,172.17.0.0/16

Without this, all traffic appears to originate from the proxy IP.

7.5 Network Isolation

Production deployments should:

  • Expose only port 2283 (via reverse proxy with TLS)
  • Keep PostgreSQL (5432), Redis (6379), ML (3003), Prometheus (9090), and Grafana (3000) on internal Docker networks only
  • Use rootless containers (docker-compose.rootless.yml) where possible

7.6 Storage Security

  • Media files are served with signed download tokens
  • Shared links use separate authentication (no user session required)
  • Trash retention (trash.days: 30) protects against accidental deletion

7.7 Admin Account Security

  • Only one admin account is permitted at initial setup
  • Additional admins are promoted via immich-admin grant-admin
  • The admin password can be reset via immich-admin reset-admin-password (requires container access)
  • Error: "The server already has an admin" prevents multiple admin setups

7.8 Quota Management

Storage quotas are configurable per user. Quota usage is reconciled nightly (nightlyTasks.syncQuotaUsage: true). When a user exceeds their quota, uploads fail with: "Quota has been exceeded!".


8. Developer Internals

8.1 Repository Structure

immich-app/immich/
├── server/                NestJS backend (TypeScript)
│   └── src/
│       ├── controllers/   64 HTTP controllers
│       ├── services/      48 services (extending BaseService)
│       ├── repositories/  58 Kysely-backed repository files
│       ├── commands/      immich-admin CLI commands
│       ├── middleware/    Guards, interceptors, filters
│       ├── schema/        Database schema (Kysely)
│       ├── config.ts      Queue defaults, SystemConfig structure
│       └── constants.ts   App-wide constants
├── web/                   SvelteKit frontend (TypeScript, Svelte 5)
├── mobile/                Flutter mobile app (Dart)
├── machine-learning/      Python ML service
│   └── app/
│       ├── models/        ONNX model wrappers
│       ├── sessions/      ONNX session management
│       └── ann/           ANN index
├── cli/                   @immich/cli (Commander.js)
├── open-api/              OpenAPI spec + TypeScript SDK
├── docker/                Compose files (dev, prod, rootless, hwaccel)
├── e2e/                   End-to-end tests (Vitest + Playwright)
├── i18n/                  Translations (79 locale files)
├── docs/                  Docusaurus documentation site
├── plugins/               Plugin system
└── deployment/            Deployment modules (Cloudflare)

8.2 Service Layer Architecture

All 48 services extend BaseService, which provides access to injected repository instances, Logger, Config, and the event emitter.

Services organized by domain:

Domain Services
Assets AssetService, AssetMediaService, MediaService, StorageService, StorageTemplateService
Search & ML SearchService, SmartInfoService, OcrService
People & Faces PersonService
Users & Auth UserService, UserAdminService, AuthService, AuthAdminService, SessionService, ApiKeyService
Albums AlbumService
Libraries LibraryService
Jobs JobService, QueueService
System SystemConfigService, SystemMetadataService, ServerService, DatabaseService, DatabaseBackupService
Notifications NotificationService, NotificationAdminService
Social PartnerService, SharedLinkService, ActivityService, MemoryService
Organization TagService, StackService, DuplicateService, TimelineService
Infrastructure TelemetryService, MapService, VersionService, PluginService
Workflows WorkflowService
Maintenance MaintenanceService

8.3 Repository Layer

The 58 repository files implement the data access layer using Kysely — a type-safe SQL query builder:

  • CamelCasePlugin — automatically converts snake_case DB columns to camelCase TypeScript properties
  • Transactions for multi-step operations
  • Repository interfaces enable dependency injection and mocking in tests
  • No ORM — raw SQL via Kysely type-safe builder
  • Migrations managed via Kysely migrations in server/src/schema/

8.4 API Design

The REST API is defined via NestJS decorators, validated with Zod schemas (ZodValidationPipe), serialized with Zod interceptors (ZodSerializerInterceptor), and documented via auto-generated OpenAPI/Swagger spec.

API categories (from ApiTag enum):

Activities, Albums, ApiKeys, Assets, Authentication, AuthenticationAdmin, DatabaseBackups, Deprecated, Download, Duplicates, Faces, Jobs, Libraries, Maintenance, Map, Memories, Notifications, NotificationsAdmin, Partners, People, Plugins, Queues, Search, Server, Sessions, SharedLinks, Stacks, Sync, SystemConfig, SystemMetadata, Tags, Timeline, Trash, UsersAdmin, Users, Views, Workflows

Authentication on API calls:

x-api-key: YOUR_API_KEY

Or via session cookie for browser-based clients.

8.5 WebSocket Events

Real-time events (upload progress, job status, sync) are pushed via WebSocket. The server uses NestJS's WebSocket adapter on the same port 2283.

8.6 Plugin System

Immich has a plugin architecture (plugins/ directory, PluginService, PluginHostFunctions). External plugins are disabled by default:

IMMICH_ALLOW_EXTERNAL_PLUGINS=true

8.7 OpenAPI SDK Generation

The TypeScript SDK (open-api/) is auto-generated from the server's OpenAPI spec. Both the web frontend and CLI consume this SDK. When developing new API endpoints:

  1. Add controller + decorators in server/src/controllers/
  2. Run the SDK generation script
  3. Update web/CLI clients

8.8 Machine Learning Service Internals

The Python ML service is a FastAPI application:

Session lifecycle:

  • Models are loaded on first request
  • Sessions are cached in memory
  • MACHINE_LEARNING_MODEL_TTL controls eviction of idle models
  • MACHINE_LEARNING_MODEL_TTL_POLL_S controls how often the TTL check runs

ANN index:

  • Built from CLIP embeddings stored in PostgreSQL
  • Provides sub-millisecond search on large libraries
  • Exported and reloaded between restarts

8.9 End-to-End Testing

The e2e/ directory contains:

  • Vitest — API-level integration tests against a running Immich instance
  • Playwright — browser automation tests for web UI flows

Run E2E tests against a local dev stack:

# Start dev environment first
docker compose -f docker/docker-compose.dev.yml up

# Run API tests
cd e2e && npm run test:api

# Run browser tests
cd e2e && npm run test:browser

8.10 Internationalization

79 locale files live in i18n/. The web frontend uses these via SvelteKit's i18n support.

8.11 Key Constants

Constant Value Purpose
POSTGRES_VERSION_RANGE >=14.0.0 Enforced at startup
VECTORCHORD_VERSION_RANGE >=0.3 <2 Vector extension version
SALT_ROUNDS 10 bcrypt cost factor
FACE_THUMBNAIL_SIZE 250 Face crop dimensions (px)
AUDIT_LOG_MAX_DURATION 100 days Audit log retention
MOBILE_REDIRECT app.immich:///oauth-callback OAuth mobile callback
LOGIN_URL /auth/login?autoLaunch=0 Login page with autoLaunch bypassed

9. Integration Points

9.1 REST API Access

Base URL: https://your-immich-domain/api

Authentication:

curl -H "x-api-key: YOUR_API_KEY" https://your-immich/api/server/ping

Core endpoint categories:

Category Base Path Description
Assets /api/assets Upload, list, update, delete assets
Albums /api/albums Album management
People /api/people Named people, face management
Search /api/search Smart search, metadata search
Libraries /api/libraries External library management
Jobs /api/jobs Queue status and control
System Config /api/system-config Runtime config (admin only)
Server /api/server Version, stats, health
Users (Admin) /api/admin/users User management
Auth /api/auth Login, logout, sessions
Shared Links /api/shared-links Public share management
Memories /api/memories "On This Day" memories
Tags /api/tags Tag management
Stacks /api/stacks Asset stack management
Duplicates /api/duplicates Duplicate detection results
Workflows /api/workflows Workflow automation
Sync /api/sync Incremental sync for mobile
Timeline /api/timeline Timeline view data
Notifications /api/notifications User notifications
Partners /api/partners Partner sharing

9.2 @immich/cli — Bulk Operations

The @immich/cli package provides command-line access for bulk operations:

npm install -g @immich/cli

# Configure
immich login https://your-immich/api YOUR_API_KEY

# Bulk upload
immich upload --recursive /path/to/photos/

# Upload to album
immich upload --album "Vacation 2026" /path/to/vacation/

The CLI uses the auto-generated TypeScript SDK and communicates via the same REST API as the web UI.

9.3 Prometheus Metrics Integration

Enable metrics:

IMMICH_TELEMETRY_INCLUDE=all
IMMICH_API_METRICS_PORT=8081
IMMICH_MICROSERVICES_METRICS_PORT=8082

Add to Prometheus scrape_configs:

scrape_configs:
  - job_name: 'immich-api'
    static_configs:
      - targets: ['immich-server:8081']
  - job_name: 'immich-microservices'
    static_configs:
      - targets: ['immich-server:8082']

The production Docker Compose includes pre-configured Prometheus and Grafana services.

9.4 SMTP / Email Notifications

Configure SMTP in Administration → Notifications. Immich can send new device login alerts, shared album invitations, and storage quota warnings via the NotificationService / NotificationAdminService pipeline.

9.5 Webhook Integration

Immich supports outbound webhooks for event notifications, configured in Administration → Webhooks. Webhooks are signed with HMAC for authenticity verification.

9.6 OAuth Provider Integration

Immich works as an OIDC relying party. Configure your identity provider with:

  • Redirect URI: https://your-immich/auth/login
  • Mobile redirect URI: app.immich:///oauth-callback
  • Required claims: email, sub (and optionally name, picture)

10. Troubleshooting

Error: "Password login has been disabled"

Cause: passwordLogin.enabled was set to false and OAuth is not functioning.

Solution:

docker exec -it immich_server immich-admin enable-password-login

Prevention: Test OAuth login before disabling password login. Keep a break-glass admin procedure documented.


Error: "Incorrect email or password"

Cause: Wrong credentials, or account was created via OAuth and has no password.

Solution: Use the OAuth flow if the account was OAuth-created. For admin password reset:

docker exec -it immich_server immich-admin reset-admin-password

Prevention: Ensure users know which auth method their account uses.


Error: "Quota has been exceeded!"

Cause: User's storage quota has been reached.

Solution: Increase or remove the user's quota in Administration → Users → edit user. Or wait for nightly quota sync to reconcile actual usage.

Prevention: Set quotas with sufficient headroom and monitor usage in the admin dashboard.


Error: "OAuth is not enabled"

Cause: Client is attempting OAuth flow but oauth.clientId is not configured.

Solution: Configure OAuth settings in Administration → Settings → Authentication.

Prevention: Validate OAuth configuration before communicating OAuth availability to users.


Error: "The server already has an admin"

Cause: Attempting to run initial setup when an admin account already exists.

Solution: Log in with the existing admin account. If credentials are lost:

docker exec -it immich_server immich-admin reset-admin-password

Prevention: Document admin credentials securely at setup time.


Error: "Cannot update configuration while IMMICH_CONFIG_FILE is in use"

Cause: IMMICH_CONFIG_FILE env var is set, disabling database-backed SystemConfig.

Solution: Edit the config file directly, then restart the server. Or remove IMMICH_CONFIG_FILE to switch back to database config.

Prevention: Choose one config method and document it. Do not mix file-based and database-based config.


Error: "Smart search is not enabled"

Cause: machineLearning.enabled is false, or the ML container is unreachable.

Solution:

# Verify ML container is running
docker ps | grep machine-learning

# Check ML health
curl http://immich-machine-learning:3003/ping

# Check ML container logs
docker logs immich_machine_learning

Verify IMMICH_MACHINE_LEARNING_URL matches the ML container's address.

Prevention: Monitor ML container health. The prod compose includes health checks.


Error: "Detected an inconsistent media location"

Cause: Database records reference file paths that no longer exist on disk (e.g., after moving the media volume).

Solution:

docker exec -it immich_server immich-admin change-media-location

See docs.immich.app/errors#inconsistent-media-location for detailed steps.

Prevention: Always update IMMICH_MEDIA_LOCATION and run change-media-location when migrating storage.


Error: "Detected schema drift"

Cause: The database schema does not match what the current server version expects. Often occurs after a failed migration or manual DB modification.

Solution: See docs.immich.app/errors#schema-drift. Run:

docker exec -it immich_server immich-admin schema-check

Prevention: Never manually modify the Immich database schema. Always upgrade via official Docker images.


Error: "Invalid upgrade path"

Cause: Attempting to upgrade Immich by skipping major versions.

Solution: See docs.immich.app/errors/#typeorm-upgrade. Upgrade through intermediate versions sequentially.

Prevention: Follow the official upgrade guide. Always back up the database before upgrading.


Issue: ML container stuck "unhealthy" after update (GitHub #27228)

Cause: Model cache on disk is incompatible with the new ML version (format change).

Solution:

docker stop immich_machine_learning
# Clear the model cache volume
docker volume rm immich_model-cache
docker start immich_machine_learning
# Wait for models to re-download
docker logs -f immich_machine_learning

Prevention: When updating Immich, check the changelog for ML model cache format changes.


Issue: Faces disappeared (GitHub #27637)

Cause: Re-running face detection with changed thresholds or after a model update can cause re-clustering that loses named assignments.

Solution: Re-assign faces in People view. Consider exporting face assignments before major ML updates.

Prevention: Back up face assignment data before changing facialRecognition settings or ML model versions.


Issue: New photos not appearing on timeline (GitHub #27787)

Cause: Metadata extraction or thumbnail generation jobs are stuck or backed up.

Solution:

  1. Check job queue status in Administration → Jobs
  2. Look for stuck metadataExtraction or thumbnailGeneration jobs
  3. Clear and re-run the affected queues

Prevention: Monitor queue depths via Prometheus/Grafana.


Issue: Android app won't sync, stuck on hashing (GitHub #27846)

Cause: Network timeout or reverse proxy configuration rejecting large uploads.

Solution:

  1. Check reverse proxy client_max_body_size — must be large enough for video files
  2. Verify proxy_read_timeout is set to at least 86400s to match Immich's 24-hour request timeout
  3. Restart the mobile app sync process

Prevention: Configure reverse proxy timeouts appropriately (see Section 4.7).


Issue: OAuth login fails with email case mismatch (GitHub #26839)

Cause: Identity provider sends [email removed] but Immich stored [email removed] at registration.

Solution: Manually update the email in the database to match the IdP exactly, or configure the IdP to always send lowercase email.

Prevention: Normalize email addresses in your IdP to lowercase before they reach Immich.


Issue: Restore on another machine not working (GitHub #27684)

Cause: Media files exist on the old machine but not the new one, or file paths differ.

Solution:

  1. Copy media files to the new machine at the same absolute path
  2. If paths differ, run change-media-location after restoring the database
  3. Verify with schema-check

Prevention: Document exact media paths. Keep media and database backups synchronized.


Issue: Assets cannot be added to album (GitHub #27545)

Cause: Attempting to add partner-owned photos to an album in certain web UI flows.

Solution: Use the asset owner's account to add assets. Album membership for partner assets has known limitations.

Prevention: Design album workflows around asset ownership boundaries.


Issue: Video thumbnails not generating

Cause: FFmpeg unavailable in the container, or video format is unsupported with current transcode settings.

Solution:

# Verify FFmpeg is available
docker exec immich_server ffmpeg -version

# Check microservices logs for errors
docker logs immich_server 2>&1 | grep -i "ffmpeg\|transcode\|thumbnail"

# Re-queue thumbnail generation:
# Administration → Jobs → Thumbnail Generation → Run All

Prevention: Ensure the immich-server image is not modified (FFmpeg is bundled).


Issue: High memory usage in ML container

Cause: Multiple models loaded simultaneously, or MACHINE_LEARNING_WORKERS > 1 with large models.

Solution:

  1. Reduce MACHINE_LEARNING_MODEL_TTL to evict models sooner
  2. Set MACHINE_LEARNING_WORKERS=1
  3. Disable unused ML features in SystemConfig

Prevention: Size ML container memory to hold: (worker count) × (sum of enabled model sizes).


Issue: Database backup failing

Cause: Insufficient disk space in the backup destination, or pg_dumpall timeout.

Solution:

# Check backup directory space
docker exec immich_server df -h /usr/src/app/upload/backups

# Manual backup with compression
docker exec immich_postgres pg_dumpall -U postgres | gzip > backup.sql.gz

Prevention: Monitor disk space. The default 14-backup retention helps but large databases need explicit space planning.


Issue: Port 2283 unreachable after deployment

Cause: Firewall rules, or container failed to start due to missing env vars or Redis/DB connection failure.

Solution:

# Check container status
docker compose ps

# Check server startup logs
docker logs immich_server --tail 100

Prevention: Always verify all required env vars are set before starting. The server logs a clear error for missing database or Redis connections.


Issue: Storage template migration hanging

Cause: Very large library or I/O-bound storage causing slow file moves.

Solution: The migration runs as a background job. Monitor progress in Administration → Jobs → Migration. Do not restart the server mid-migration.

Prevention: Run storage template migration during low-traffic periods. Ensure the migration queue has sufficient concurrency.


Cause: server.externalDomain was not updated to match the new domain.

Solution: Update server.externalDomain in Administration → Settings → Server. Existing share links will use the new domain for future requests.

Prevention: Configure externalDomain from day one. Document domain dependency for any server moves.


Issue: Duplicate detection not finding obvious duplicates

Cause: machineLearning.duplicateDetection.maxDistance threshold may be too low, or smart search embeddings are not yet indexed for all assets.

Solution:

  1. Increase maxDistance slightly (e.g., from 0.01 to 0.02)
  2. Ensure smart search jobs have completed for all assets
  3. Check for stuck smartSearch queue jobs in Administration → Jobs

Prevention: Run a full smart search job after bulk imports before relying on duplicate detection.