Skip to main content

Environment variables

Every config key has an environment variable, and an environment variable beats the config file and whatever the web UI saved to the database. The UI greys out a setting a variable sets and names the variable; immich-memories config show lists them all (where a setting comes from). On Docker, the ones you set live in .env beside docker-compose.yml.

.env and example.env​

The repo ships example.env next to the compose file. Copy it to .env and fill it in. Docker Compose reads .env by itself, and the compose file hands each of its variables to the container:

IMMICH_URL=http://192.168.1.10:2283
IMMICH_API_KEY=
IMMICH_MEMORIES_TRIPS__HOMEBASE_LATITUDE=
IMMICH_MEMORIES_TRIPS__HOMEBASE_LONGITUDE=
TZ=Etc/UTC
# IMMICH_MEMORIES_AUTH_USERNAME=
# IMMICH_MEMORIES_AUTH_PASSWORD=
An edit needs a recreate

The container reads its environment when it starts. After changing .env or the compose file, run docker compose up -d, which recreates it. docker compose restart does not.

A line in .env reaches the container only when the compose file's environment: block names it: compose uses .env to fill in the compose file, not as the container's environment. Every variable in example.env is already there. For any other one, add the line to environment: as well, like the commented ones already there.

The variables you are most likely to set​

"Placement" is where the key lives in config.yaml: top level (1) or under advanced:. "Compose" says whether the shipped compose file already passes it.

Immich and home​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_URLimmich.urlnone1yes, .envImmich, as the app reaches it. Required
IMMICH_API_KEYimmich.api_keynone1yes, .envRequired. Permissions: The API key
IMMICH_MEMORIES_TRIPS__HOMEBASE_LATITUDEtrips.homebase_latitude0 (unset)1yes, .envYour home. Without it no day is away from home and no story is a trip
IMMICH_MEMORIES_TRIPS__HOMEBASE_LONGITUDEtrips.homebase_longitude0 (unset)1yes, .envSame, the longitude
TZnoneEtc/UTCnoneyes, .envThe zone automation.daily_at and the logs use
IMMICH_MEMORIES_IMMICH__API_VERSIONimmich.api_versionauto1nov2 or v3 only to diagnose a proxy that breaks detection

The web UI​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_MEMORIES_AUTH_USERNAME, IMMICH_MEMORIES_AUTH_PASSWORDauth.username, auth.passwordemptyadvancedyes, .envSet both to turn on basic auth. Either alone is ignored
IMMICH_MEMORIES_STORAGE_SECRETnonegeneratednonecommentedSession secret. Generated once onto the config volume, so sessions survive a recreate without it
IMMICH_MEMORIES_SECRET_KEYnoneunsetnoneyesEncrypts the secrets Settings saves (API keys, passwords). Unset, Settings says "Secrets cannot be saved here until IMMICH_MEMORIES_SECRET_KEY is set". Not the same thing as STORAGE_SECRET: set it up
IMMICH_MEMORIES_AUTOMATION__ENABLEDautomation.enabledfalseadvancedcommentedThe daily memory, inside the UI process
IMMICH_MEMORIES_AUTOMATION__DAILY_ATautomation.daily_at09:00advancedcommentedWhen, in the TZ zone
IMMICH_MEMORIES_AUTOMATION__UPLOAD_TO_IMMICHautomation.upload_to_immichfalseadvancednoThe daily film goes back to Immich too (AUTOMATION__ALBUM_NAME picks the album)
IMMICH_MEMORIES_SERVER__TRIGGER_TOKENserver.trigger_tokenemptyadvancednoShared secret for POST /api/trigger, for a cron job or a Kubernetes CronJob to start a run (Automate it)
IMMICH_MEMORIES_SERVER__ENABLE_DEMO_MODEserver.enable_demo_modefalseadvancednoOffers the Demo mode eye button that blurs every picture, for a screen share

Uploads and outside calls​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_MEMORIES_UPLOAD__ENABLEDupload.enabledfalse1noUpload every film generate makes back to Immich; the web render panel has its own box (what Immich sees)
IMMICH_MEMORIES_UPLOAD__ALBUM_NAMEupload.album_namenone1noThe album it goes into
IMMICH_MEMORIES_NETWORK__GEOCODINGnetwork.geocodingfalse1noPlace names from Nominatim: the right district, in the film's language (outside calls)
IMMICH_MEMORIES_NETWORK__GEOCODING_URLnetwork.geocoding_urlempty1noYour own Nominatim instead of the public one
IMMICH_MEMORIES_NETWORK__MAP_TILESnetwork.map_tilesfalse1noSatellite tiles for the trip fly-over and the map behind location cards
IMMICH_MEMORIES_DEFAULTS__SHARINGdefaults.sharingfamily1noWho a film is for when you don't say: just-us, family or shareable (sharing levels)

The secret key​

IMMICH_MEMORIES_SECRET_KEY is what lets the web UI and immich-memories config save an API key or a password to the database, encrypted. Generate one:

openssl rand -base64 32

On Docker, put the value in .env (the shipped compose file passes it through; example.env has the commented line):

IMMICH_MEMORIES_SECRET_KEY=paste-the-openssl-output-here

then docker compose up -d. Keep the value with your other secrets: lose or change it and every secret saved from the UI has to be saved again. You don't need it at all if your keys come from .env or config.yaml. The details: secrets in the database.

The store​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_MEMORIES_DATABASE_URLdatabase.urlsqlite:///~/.immich-memories/store.db1commentedThe store's database: sqlite:////data/store.db or postgresql://user:pass@host/db. Read before the store opens, so never from the store
IMMICH_MEMORIES_DATABASE_SCHEMAdatabase.schemaimmich_memories1noThe PostgreSQL schema for the store
IMMICH_MEMORIES_IMPORT_FROMdatabase.import_from~/.immich-memories1noWhere the one-time import of pre-store files (people.yaml, cache.db history, annotations.sqlite, ...) looks, and the default of store import --from (upgrading)
IMMICH_MEMORIES_ALLOW_NETWORK_SQLITEnoneunsetnoneno1 opens a SQLite file on NFS, SMB or CIFS with a warning instead of refusing. WAL needs shared memory those filesystems cannot give two hosts, so set it only when one host ever opens the file

Preparation and output​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_MEMORIES_TIERtierauto1autoResolves NAS, GPU or Full from inference capability and the configured LLM; preparation follows it
IMMICH_MEMORIES_EDITORIAL__PREPARATION__DETECTOR_CACHE_DIReditorial.preparation.detector_cache_dirthe Hugging Face cacheadvancedthe config volumeWhere the document classifier lives. Keep it on a volume
IMMICH_MEMORIES_EDITORIAL__PREPARATION__CAPTION_BASE_URLeditorial.preparation.caption_base_urlhttp://localhost:8092/v1advancedcommentedThe SmolVLM caption server, for GPU and Full
IMMICH_MEMORIES_EDITORIAL__PREPARATION__CAPTION_PROVIDEReditorial.preparation.caption_providersmolvlmadvancednollm explicitly sends images to the configured LLM on any tier; less efficient and potentially much more expensive, especially hosted
IMMICH_MEMORIES_OUTPUT__DIRECTORYoutput.directory~/Videos/Memories; /app/output in the image1set by the imageWhere films land. In a container, set this, not output.directory
IMMICH_MEMORIES_PRESETpresetnone1nofast: 1080p H.264, fast encoder preset, static titles. Explicit settings win
IMMICH_MEMORIES_HARDWARE__BACKENDhardware.backendautoadvancednonone, nvidia, apple, vaapi or qsv to name one encoder instead of probing

Optional add-ons​

VariableConfig keyDefaultPlacementComposeWhat it does
IMMICH_MEMORIES_LLM__BASE_URLllm.base_urlhttp://localhost:8080/v1advancedcommentedThe reader's endpoint. The default is the app's own port: set it
IMMICH_MEMORIES_LLM__MODELllm.modelemptyadvancedcommentedThe reader. Empty means the rules editor works alone. Must match GET /v1/models; a text model is enough
IMMICH_MEMORIES_LLM__API_KEYllm.api_keyemptyadvancednoThe reader's token, for a server that answers 401
IMMICH_MEMORIES_INFERENCE__FACTS_BASE_URLinference.facts_base_urlemptyadvancedcommentedSend the heads and detectors to the inference service

The pattern​

Every other key follows IMMICH_MEMORIES_<SECTION>__<FIELD>, with a double underscore between levels. <SECTION> is always the flat runtime name (LLM, AUTH, EDITORIAL), never ADVANCED__LLM, even for sections that live under advanced: in the file. A nested field takes another double underscore. Field names are in the config reference.

IMMICH_MEMORIES_OUTPUT__RESOLUTION=4k
IMMICH_MEMORIES_EDITORIAL__PEOPLE__SEAT_MIN_PICTURES=30

List- and dict-valued fields take JSON: auth.trusted_proxies, auth.allowed_emails, auth.allowed_domains, notifications.urls, analysis.exclude_filename_patterns, llm.drop_params, llm.extra_params, llm.thinking_params and editorial.head_versions.

IMMICH_MEMORIES_ANALYSIS__EXCLUDE_FILENAME_PATTERNS='["RingVideo_*", "Screenshot*"]'

Immich connection​

IMMICH_MEMORIES_IMMICH__URL="https://photos.example.com"
IMMICH_MEMORIES_IMMICH__API_KEY="your-api-key-here"
IMMICH_MEMORIES_IMMICH__API_VERSION="auto"

auto detects v2 or v3 at runtime; set v2/v3 only to diagnose a proxy that breaks detection. immich-memories config test is read-only and prints the API version it found: Immich API compatibility.

An extra account puts its name in the variable (lowercase in config, any case here):

IMMICH_MEMORIES_IMMICH__ACCOUNTS__PARTNER__URL="https://photos.example.com"
IMMICH_MEMORIES_IMMICH__ACCOUNTS__PARTNER__API_KEY="partner-api-key"

The file can name the account and its URL while the key comes from here; the two merge. See extra accounts.

The ones that do not follow the pattern​

Retired keys are dropped. The scene-detection and segment-length knobs that went with the old clip scorer are dropped by name from a config file with one warning; the environment-variable form gets no warning at all. Delete both.

The captioner has its own credential. OPENAI_API_KEY does not reach it: give it IMMICH_MEMORIES_EDITORIAL__PREPARATION__CAPTION_API_KEY, or leave it blank for a server that needs none.

Shorthands​

VariableOverrides
IMMICH_URLimmich.url
IMMICH_API_KEYimmich.api_key
OPENAI_API_KEYllm.api_key, only when the config file states no key
ANTHROPIC_API_KEYllm.api_key under the same rule, read instead of OPENAI_API_KEY when llm.provider is anthropic or zai
MUSICGEN_ENABLED, MUSICGEN_BASE_URL, MUSICGEN_API_KEYmusicgen.enabled, .base_url, .api_key
ACE_STEP_ENABLED, ACE_STEP_API_URL, ACE_STEP_API_KEYace_step.enabled, .api_url, .api_key
ACE_STEP_MODEace_step.mode (api or lib; other values ignored)
IMMICH_MEMORIES_AUTH_USERNAME + IMMICH_MEMORIES_AUTH_PASSWORDauth.username and auth.password, and sets auth.enabled=true, auth.provider=basic. Both must be set

An empty shorthand counts as unset.

An LLM key written in the file beats its shorthand

OPENAI_API_KEY is the name every OpenAI-SDK client reads, a local mlx or vLLM server included, so on a machine that exports it for that server it says nothing about the endpoint the key will be sent to. So a key in llm.api_key wins, and the variable fills the field only where the file leaves it empty or holds a ${VAR} nobody set. ANTHROPIC_API_KEY works the same way. To replace a key that is in the file, use IMMICH_MEMORIES_LLM__API_KEY.

--config PATH makes PATH the single config file, and the same variables apply to it.

Not config keys​

VariableEffect
IMMICH_MEMORIES_STORAGE_SECRETSecret for the web UI session store. Priority: this variable, then ~/.immich-memories/.storage_secret, then generated on first start
IMMICH_MEMORIES_SKIP_STORED_SETTINGS1 starts without the settings saved in the database (env, config.yaml and defaults only). Without it, a store that is configured but unreadable stops the app (where a setting comes from)
IMMICH_MEMORIES_SECRET_KEYEncrypts the secrets saved to the database from the UI or CLI (API keys, passwords). Any string of 32+ characters, e.g. openssl rand -base64 32. Unset: secrets cannot be saved there, only in env or config.yaml. Read from the environment only, never from the store (secrets in the database)
IMMICH_MEMORIES_LOG_FORMATtext (default) or json
IMMICH_MEMORIES_LOG_LEVELINFO (default), DEBUG, WARNING or ERROR. The CLI's -v and --log-level win for one run
IMMICH_MEMORIES_LOG_FILEAlso write logs to this file
IMMICH_FORCE_CPU1, true or yes puts the title renderer on the CPU even with a GPU
IMMICH_MEMORIES_FONTS_DIRWhere titles fonts --install puts the Noto fonts for other alphabets and where titles look for them (default ~/.immich-memories/fonts/noto; the image sets its own)
IMMICH_MEMORIES_OWNERWho "you" is in the people registry, for people scan (same as --owner)
ACESTEP_CHECKPOINTS_DIRACE-Step lib mode: where checkpoints go (default ~/.cache/ace-step/checkpoints)
ACESTEP_MLX_VAE_CHUNKACE-Step lib mode on Apple Silicon: VAE decode chunk in latent frames (minimum 192). Lower it if MLX runs out of memory
IMMICH_MEMORIES_ACESTEP_MLX_DIT_FP32ACE-Step lib mode on Apple Silicon: 1 keeps the decoder in fp32 (about twice the memory)
FORWARDED_ALLOW_IPSuvicorn: proxies whose X-Forwarded-* headers are trusted. Wins over auth.trusted_proxies. See Authentication
Scheduled jobs do not inherit your shell

A launchd or cron job starts from a login-less environment, so nothing you export interactively reaches it. auto install copies PATH, ACESTEP_CHECKPOINTS_DIR, ACESTEP_MLX_VAE_CHUNK, IMMICH_MEMORIES_ACESTEP_MLX_DIT_FP32 and PYTORCH_MPS_HIGH_WATERMARK_RATIO into the plist or unit, and nothing else, since IMMICH_MEMORIES_* also holds credentials. Change one of them and run auto install again.

Precedence​

CLI flags beat the shorthand variables, which beat the IMMICH_MEMORIES_<SECTION>__<FIELD> form, then the config file and the database: the full order, with a diagram, is on Where a setting comes from. OPENAI_API_KEY and ANTHROPIC_API_KEY are the exception: they sit below the config file.

Compute tier​

IMMICH_MEMORIES_TIER=auto|nas|gpu|full sets the same tier as tier: in the config file, and beats it. The default is auto: The three tiers.