# Technical Documentation: immich-app/immich

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/immich-app/immich)

### Overview (README.md)

<p align="center"> 
  <br/>
  <a href="https://opensource.org/license/agpl-v3"><img src="https://img.shields.io/badge/License-AGPL_v3-blue.svg?color=3F51B5&style=for-the-badge&label=License&logoColor=000000&labelColor=ececec" alt="License: AGPLv3"></a>
  <a href="https://discord.immich.app">
    <img src="https://img.shields.io/discord/979116623879368755.svg?label=Discord&logo=Discord&style=for-the-badge&logoColor=000000&labelColor=ececec" alt="Discord"/>
  </a>
  <br/>
  <br/>
</p>

<p align="center">
<img src="design/immich-logo-stacked-light.svg" width="300" title="Login With Custom URL">
</p>
<h3 align="center">High performance self-hosted photo and video management solution</h3>
<br/>
<a href="https://immich.app">
<img src="design/immich-screenshots.png" title="Main Screenshot">
</a>
<br/>

<p align="center">
  <a href="readme_i18n/README_ca_ES.md">Català</a>
  <a href="readme_i18n/README_es_ES.md">Español</a>
  <a href="readme_i18n/README_fr_FR.md">Français</a>
  <a href="readme_i18n/README_it_IT.md">Italiano</a>
  <a href="readme_i18n/README_ja_JP.md">日本語</a>
  <a href="readme_i18n/README_ko_KR.md">한국어</a>
  <a href="readme_i18n/README_de_DE.md">Deutsch</a>
  <a href="readme_i18n/README_nl_NL.md">Nederlands</a>
  <a href="readme_i18n/README_tr_TR.md">Türkçe</a>
  <a href="readme_i18n/README_zh_CN.md">简体中文</a>
  <a href="readme_i18n/README_zh_TW.md">正體中文</a>
  <a href="readme_i18n/README_uk_UA.md">Українська</a>
  <a href="readme_i18n/README_ru_RU.md">Русский</a>
  <a href="readme_i18n/README_bg_BG.md">Български</a>
  <a href="readme_i18n/README_pt_BR.md">Português Brasileiro</a>
  <a href="readme_i18n/README_sv_SE.md">Svenska</a>
  <a href="readme_i18n/README_ar_JO.md">العربية</a>
  <a href="readme_i18n/README_vi_VN.md">Tiếng Việt</a>
  <a href="readme_i18n/README_th_TH.md">ภาษาไทย</a>
  <a href="readme_i18n/README_ml_IN.md">മലയാളം</a>
</p>


> [!WARNING]
> ⚠️ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos!
> 
 

> [!NOTE]
> You can find the main documentation, including installation guides, at https://immich.app/.

## Links

- [Documentation](https://docs.immich.app/)
- [About](https://docs.immich.app/overview/introduction)
- [Installation](https://docs.immich.app/install/requirements)
- [Roadmap](https://immich.app/roadmap)
- [Demo](#demo)
- [Features](#features)
- [Translations](https://docs.immich.app/developer/translations)
- [Contributing](https://docs.immich.app/overview/support-the-project)

## Demo

Access the demo [here](https://demo.immich.app). For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoint URL`.

### Login credentials

| Email           | Password |
| --------------- | -------- |
| demo@immich.app | demo     |

## Features

| Features                                     | Mobile | Web |
| :------------------------------------------- | ------ | --- |
| Upload and view videos and photos            | Yes    | Yes |
| Auto backup when the app is opened           | Yes    | N/A |
| Prevent duplication of assets                | Yes    | Yes |
| Selective album(s) for backup                | Yes    | N/A |
| Download photos and videos to local device   | Yes    | Yes |
| Multi-user support                           | Yes    | Yes |
| Album and Shared albums                      | Yes    | Yes |
| Scrubbable/draggable scrollbar               | Yes    | Yes |
| Support raw formats                          | Yes    | Yes |
| Metadata view (EXIF, map)                    | Yes    | Yes |
| Search by metadata, objects, faces, and CLIP | Yes    | Yes |
| Administrative functions (user management)   | No     | Yes |
| Background backup                            | Yes    | N/A |
| Virtual scroll                               | Yes    | Yes |
| OAuth support                                | Yes    | Yes |
| API Keys                                     | N/A    | Yes |
| LivePhoto/MotionPhoto backup and playback    | Yes    | Yes |
| Support 360 degree image display             | No     | Yes |
| User-defined storage structure               | Yes    | Yes |
| Public Sharing                               | Yes    | Yes |
| Archive and Favorites                        | Yes    | Yes |
| Global Map                                   | Yes    | Yes |
| Partner Sharing                              | Yes    | Yes |
| Facial recognition and clustering            | Yes    | Yes |
| Memories (x years ago)                       | Yes    | Yes |
| Offline support                              | Yes    | No  |
| Read-only gallery                            | Yes    | Yes |
| Stacked Photos                               | Yes    | Yes |
| Tags                                         | No     | Yes |
| Folder View                                  | Yes    | Yes |

## Translations

Read more about translations [here](https://docs.immich.app/developer/translations).

<a href="https://hosted.weblate.org/engage/immich/">
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
</a>

## Repository activity



## Contributors

<a href="https://github.com/immich-app/immich/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=immich-app/immich" width="100%"/>
</a>

---

### README (`docs/README.md`)

# Website

This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ pnpm install
```

### Local Development

```
$ pnpm run start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ pnpm run build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

Using SSH:

```
$ USE_SSH=true pnpm run deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> pnpm run deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

---

### Docs FAQ (`docs/docs/FAQ.mdx`)

# FAQ

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

## Commercial Guidelines

### Are you open to commercial partnerships and collaborations?

We are working to commercialize Immich and we'd love for you to help us by making Immich better. FUTO is dedicated to developing sustainable models for developing open source software for our customers. We want our customers to be delighted by the products our engineers deliver, and we want our engineers to be paid when they succeed.

If you wish to use Immich in a commercial product not owned by FUTO, we have the following requirements:

- Plugin Integrations: Integrations for other platforms are typically approved, provided proper notification is given.

- Reseller Partnerships: Must adhere to the guidelines outlined below regarding trademark usage, and proper representation.

- Strategic Collaborations: We welcome discussions about mutually beneficial partnerships that enhance the value proposition for both organizations.

### What are your guidelines for resellers and trademark usage?

For organizations seeking to resell Immich, we have established the following guidelines to protect our brand integrity and ensure proper representation.

- We request that resellers do not display our trademarks on their websites or marketing materials. If such usage is discovered, we will contact you to request removal.

- Do not misrepresent your reseller site or services as being officially affiliated with or endorsed by Immich or our development team.

- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase product keys directly from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work.

When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app

---

## User

### How can I reset the admin password?

The admin password can be reset by running the [reset-admin-password](/administration/server-commands.md) command on the immich-server.

### How can I see a list of all users in Immich?

You can see the list of all users by running [list-users](/administration/server-commands.md) Command on the Immich-server.

### How can I change my profile picture?

View a single photo, press the three dots in the top-right to show context menu, and select "Set as profile picture". In the pop-up, use your mouse scroll wheel to zoom in the picture until it completely fills the circle. Click and drag the picture to align it to your liking. Press "Save" to save your changes.

---

## Mobile App

### What is the difference between the cloud icons on the mobile app?

| Icon                               | Description                                                                                                                                      |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|            | Asset is only available on the server and was uploaded from some other device (like the web client) or was deleted from this device after upload |
|  | Asset is only available locally and has not yet been backed up                                                                                   |
|  | Asset was uploaded from this device and is now backed up to the server; the original file is still on the device                                 |

### I cannot log into the application after an update. What can I do?

Verify that the mobile app and server are both running the same version (major and minor).

:::note
App store updates sometimes take longer because the stores (Google Play Store and Apple App Store)
need to approve the update first, and it can take some time.
:::

If you still cannot log in to the app, try the following:

- Check the mobile logs
- Make sure login credentials are correct by logging in on the web app

### Why does foreground backup stop when I navigate away from the app? Shouldn't it transfer the job to background backup?

Foreground backup and background backup are two separate mechanisms. They don't communicate or interact with each other.

Foreground backup is controlled by the user's action, while background backup is controlled by your device's operating system. When the app is put in the background, the invocation of background tasks is delegated to the device's operating system scheduler. It decides when the background task can run and how long it can run.

The behaviors differ based on your device manufacturer and operating system, but most are related to battery-saving policies.

### Why is background backup on iOS not working?

On iOS (iPhone and iPad), the operating system determines if a particular app can invoke background tasks based on multiple factors, most of which the Immich app has no control over. To increase the likelihood that the background backup task is run, follow the steps below:

- Enable Background App Refresh for Immich in the iOS settings at `Settings > General > Background App Refresh`.
- Disable **Low Power Mode** when not needed, as this can prevent apps from running in the background.
- Disable Background App Refresh for apps that don't need background tasks to run. This will reduce the competition for background task invocation for Immich.
- Use the Immich app more often.

### Why are features in the mobile app not working with a self-signed certificate, Basic Auth, custom headers, or mutual TLS?

These network features are experimental. They often do not work with video playback, asset upload or download, and other features.
Many of these limitations are tracked in [#15230](https://github.com/immich-app/immich/issues/15230).
Instead of these experimental features, we recommend using the URL switching feature, a VPN, or a [free trusted SSL certificate](https://letsencrypt.org/) for your domain.

We are not actively developing these features and will not be able to provide support, but welcome contributions to improve them.
Please discuss any large PRs with our dev team to ensure your time is not wasted.

### Why isn't the mobile app updated yet?

The app stores can take a few days to approve new builds of the app. If you're impatient, android APKs can be downloaded from the GitHub releases.

---

## Assets

### Does Immich change the file?

No, Immich does not modify the original files.
All edited metadata is saved in companion `.xmp` sidecar files and the database.
However, Immich will delete original files that have been trashed when the trash is emptied in the Immich UI.

### Why do my file names appear as a random string in the file manager?

When Storage Template is off (default) Immich saves the file names in a random string (also known as random UUIDs) to prevent duplicate file names.
To retrieve the original file names, you must enable the Storage Template and then run the STORAGE TEMPLATE MIGRATION job.
It is recommended to read about [Storage Template](/administration/storage-template) before activation.

### Can I add my existing photo library?

Yes, with an [External Library](/features/libraries.md).

### What happens to existing files after I choose a new [Storage Template](/administration/storage-template.mdx)?

Template changes will only apply to _new_ assets. To retroactively apply the template to previously uploaded assets, run the Storage Migration Job, available on the [Jobs](/administration/jobs-workers/#jobs) page.

### Why are only photos and not videos being uploaded to Immich?

This often happens when using a reverse proxy in front of Immich.
Make sure to [set your reverse proxy](/administration/reverse-proxy/) to allow large requests.
Also, check the disk space of your reverse proxy.
In some cases, proxies cache requests to disk before passing them on, and if disk space runs out, the request fails.

If you are using Cloudflare Tunnel, please know that they set a maximum filesize of 100 MB that cannot be changed.
At times, files larger than this may work, potentially up to 1 GB. However, the official limit is 100 MB.
If you are having issues, we recommend switching to a different network deployment.

### Why are some photos stored in the file system with the wrong date?

There are a few different scenarios that can lead to this situation. The solution is to rerun the storage migration job.
The job is only automatically run once per asset after upload. If metadata extraction originally failed, the jobs were cleared/canceled, etc.,
the job may not have run automatically the first time.

### How can I hide a photo or video from the timeline?

You can _archive_ them. This will hide the asset from the main timeline and folder view, but it will still show up in searches. All archived assets can be found in the _Archive_ view

### How can I backup data from Immich?

See [Backup and Restore](/administration/backup-and-restore.md).

### Does Immich support reading existing face tag metadata?

Yes, it creates new faces and persons from the imported asset metadata. For details see the [feature request #4348](https://github.com/immich-app/immich/discussions/4348) and [PR #6455](https://github.com/immich-app/immich/pull/6455).

### Does Immich support the filtering of NSFW images?

No, it currently does not. There is an [open feature request on Github](https://github.com/immich-app/immich/discussions/2451).

### Why are there so many thumbnail generation jobs?

There are three thumbnail jobs for each asset:

- Blurred (thumbhash)
- Preview (Webp)
- Thumbnail (Jpeg)

Also, there are additional jobs for person (face) thumbnails.

### Why do files from WhatsApp not appear with the correct date?

Files sent on WhatsApp are saved without metadata on the file. Therefore, Immich has no way of knowing the original date of the file when files are uploaded from WhatsApp,
not the order of arrival on the device. [See #9116](https://github.com/immich-app/immich/discussions/9116).

### What happens if an asset exists in more than one account?

There are no requirements for assets to be unique across users. If multiple users upload the same image, it is processed as if it were a distinct asset, and jobs run and thumbnails are generated accordingly.

### Why do HDR videos appear pale in Immich player but look normal after download?

Immich uses a player with known HDR color display issues. We are experimenting with a different player that provides better color profiles for HDR content for future improvements.

### Why does Immich transcode my videos to a lower quality?

Immich always keeps your original files. Alongside that, it generates a transcoded version for compatibility and performance reasons.

### How can I delete transcoded videos without deleting the original?

The transcoded version of an asset can be deleted by setting a transcode policy that makes it unnecessary and then running a transcoding job for that asset. This can be done on a per-asset basis by starting a transcoding job for a single asset with the _Refresh encoded videos_ button in the asset viewer options or for all assets by running transcoding jobs for all assets from the administration page.

To update the transcode policy, navigate to Administration > Video Transcoding Settings > Transcoding Policy and select a policy from the drop-down. This policy will determine whether an existing transcode will be deleted or overwritten in the transcoding job. If a video should be transcoded according to this policy, an existing transcode is overwritten. If not, then it is deleted.

:::note
For example, say you have existing transcodes with the policy "Videos higher than normal resolution or not in the desired format" and switch to a narrower policy: "Videos not in the desired format." If an asset was only transcoded due to its resolution, running a transcoding job for it will delete the existing transcode. This is because resolution is no longer part of the transcode policy and the transcode is unnecessary. Likewise, if you set the policy to "Don't transcode any videos" and run transcoding jobs for all assets, this will delete all existing transcodes as they are unnecessary.
:::

### Is it possible to compress images during backup?

No. Our design principle is that the original assets should always be untouched.

### How can I mount a CIFS/Samba volume within Docker?

If you aren't able to or prefer not to mount Samba on the host (such as Windows environment), you can mount the volume within Docker.
Below is an example in the `docker-compose.yml`.

Change your username, password, local IP, and share name, and see below where the line `- originals:/usr/src/app/originals`,
correlates to the section where the volume `originals` was created. You can call this whatever you like, and map it to the docker container as you like.
For example you could change `originals:` to `Photos:`, and change `- originals:/usr/src/app/originals` to `Photos:/usr/src/app/photos`.

```diff
...
services:
  immich-server:
...
    volumes:
      # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file
      - ${UPLOAD_LOCATION}:/data
      - /etc/localtime:/etc/localtime:ro
+     - originals:/usr/src/app/originals
...
volumes:
  model-cache:
+ originals:
+   driver_opts:
+     type: cifs
+     o: 'iocharset=utf8,username=USERNAMEHERE,password=PASSWORDHERE,rw' # change to `ro` if read only desired
+     device: '//localipaddress/sharename'
```

---

## Albums

### Can I keep my existing album structure while importing assets into Immich?

Yes, by using the [Immich CLI](/features/command-line-interface) along with the `--album` flag.

### Is there a way to reorder photos within an album?

No, not yet. For updates on this planned feature, follow the [GitHub discussion](https://github.com/immich-app/immich/discussions/1689).

---

## External Library

### Can I add an external library while keeping the existing album structure?

We haven't implemented an official mechanism for creating albums from external libraries, but there are some [workarounds from the community](https://github.com/immich-app/immich/discussions/4279) to help you achieve that.

### What happens to duplicates in external libraries?

Duplicate checking only exists for upload libraries, using the file hash. Furthermore, duplicate checking is not global, but _per library_. Therefore, a situation where the same file appears twice in the timeline is possible, especially for external libraries.

### Why are my edits to files not being saved in read-only external libraries?

Images in read-write external libraries (the default) can be edited as normal.
In read-only libraries (`:ro` in the `docker-compose.yml`), Immich is unable to create the `.xmp` sidecar files to store edited file metadata.
For this reason, the metadata (timestamp, location, description, star rating, etc.) cannot be edited for files in read-only external libraries.

### How are deletions of files handled in external libraries?

Immich will attempt to delete original files that have been trashed when the trash is emptied.
In read-write external libraries (the default), Immich will delete the original file.
In read-only libraries (`:ro` in the `docker-compose.yml`), files can still be trashed in the UI.
However, when the trash is emptied, the files will re-appear in the main timeline since Immich is unable to delete the original file.

---

## Machine Learning

### How does smart search work?

Immich uses CLIP models. An ML model converts each image to an "embedding", which is essentially a string of numbers that semantically encodes what is in the image. The same is done for the text that you enter when you do a search, and that text embedding is then compared with those of the images to find similar ones. As such, there are no "tags", "labels", or "descriptions" generated that you can look at. For more information about CLIP and its capabilities, read about it [here](https://openai.com/research/clip).

### How does facial recognition work?

See [How Facial Recognition Works](/features/facial-recognition#how-facial-recognition-works) for details.

### How can I disable machine learning?

:::info
Disabling machine learning will result in a poor experience for searching and the 'Explore' page, as these are reliant on it to work as intended.
:::

Machine learning can be disabled under Administration > Settings > Machine Learning Settings, either entirely or by model type. For instance, you can choose to disable smart search with CLIP, but keep facial recognition enabled. This means that the machine learning service will only process the enabled jobs.

However, disabling all jobs will not disable the machine learning service itself. To prevent it from starting up at all in this case, you can comment out the `immich-machine-learning` section of the docker-compose.yml.

### I'm getting errors about models being corrupt or failing to download. What do I do?

You can delete the model cache volume, where models are downloaded. This will give the service a clean environment to download the model again. If models are failing to download entirely, you can manually download them from [Hugging Face][huggingface] and place them in the cache folder.

### Can I use a custom CLIP model?

No, this is not supported. Only models listed in the [Hugging Face][huggingface] page are compatible. Feel free to make a feature request if there's a model not listed here that you think should be added.

### I want to be able to search in other languages besides English. How can I do that?

You can change to a multilingual CLIP model. See [here](/features/searching#clip-models) for instructions.

### Does Immich support Facial Recognition for videos?

Immich's machine learning feature operates on the generated thumbnail. If a face is visible in the video's thumbnail it will be picked up by facial recognition.
Scanning the entire video for faces may be implemented in the future.

### Does Immich have animal recognition?

No.
:::tip
You can use [Smart Search](/features/searching.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results.
:::

### I'm getting a lot of "faces" that aren't faces, what can I do?

You can increase the MIN DETECTION SCORE to 0.8 to help prevent bad thumbnails. Setting the score too high (above 0.9) might filter out too many real faces depending on the library used. If you just want to hide specific faces, you can adjust the 'MIN FACES DETECTED' setting in the administration panel  
to increase the bar for what the algorithm considers a "core face" for that person, reducing the chance of bad thumbnails being chosen.

### The immich_model-cache volume takes up a lot of space, what could be the problem?

If you installed several models and chose not to use some of them, it might be worth deleting the old models that are in immich_model-cache. To do this you can mount the model cache and remove the undesired models.

<details>
<summary>Steps</summary>

```bash
docker run -it --rm -v immich_model-cache:/mnt-models alpine sh
cd /mnt-models
ls clip/ facial-recognition/
# rm -r clip/ABC facial-recognition/DEF # delete unused models
```

</details>

---

## Performance

### Why is Immich slow on low-memory systems like the Raspberry Pi?

Immich optionally uses transcoding and machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/FAQ#can-i-lower-cpu-and-ram-usage) this or host Immich's machine-learning container on a [more powerful system](/guides/remote-machine-learning), or [disable](/FAQ#how-can-i-disable-machine-learning) machine learning entirely.

### Can I lower CPU and RAM usage?

The initial backup is the most intensive due to the number of jobs running. The most CPU-intensive ones are transcoding and machine learning jobs (Smart Search, Face Detection), and to a lesser extent thumbnail generation. Here are some ways to lower their CPU usage:

- Lower the job concurrency for these jobs to 1.
- Under Settings > Transcoding Settings > Threads, set the number of threads to a low number like 1 or 2.
- Under Settings > Machine Learning Settings > Facial Recognition > Model Name, you can change the facial recognition model to `buffalo_s` instead of `buffalo_l`. The former is a smaller and faster model, albeit not as good.
  - For facial recognition on new images to work properly, You must re-run the Face Detection job for all images after this.
- At the container level, you can [set resource constraints](/FAQ#can-i-limit-cpu-and-ram-usage) to lower usage further.
  - It's recommended to only apply these constraints _after_ taking some of the measures here for best performance.
- If these changes are not enough, see [above](/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning.

### Can I limit CPU and RAM usage?

By default, a container has no resource constraints and can use as much of a given resource as the host's kernel scheduler allows. To limit this, you can add the following to the `docker-compose.yml` block of any containers that you want to have limited resources.

<details>
<summary>docker-compose.yml</summary>

```yaml
deploy:
  resources:
    limits:
      # Number of CPU threads
      cpus: '1.00'
      # Gigabytes of memory
      memory: '1G'
```

</details>
For more details, you can look at the [original docker docs](https://docs.docker.com/config/containers/resource_constraints/) or use this [guide](https://www.baeldung.com/ops/docker-memory-limit).

Note that memory constraints work by terminating the container, so this can introduce instability if set too low.

### How can I boost machine learning speed?

:::note
This advice improves throughput, not latency. This is to say that it will make Smart Search jobs process more quickly, but it won't make searching faster.
:::

You can increase throughput by increasing the job concurrency for machine learning jobs (Smart Search, Face Detection). With higher concurrency, the host will work on more assets in parallel. You can do this by navigating to Administration > Settings > Job Settings and increasing concurrency as needed.

:::danger
On a normal machine, 2 or 3 concurrent jobs can probably max the CPU. Storage speed and latency can quickly become the limiting factor beyond this, particularly when using HDDs.

The concurrency can be increased more comfortably with a GPU, but should still not be above 16 in most cases.

Do not exaggerate with the job concurrency because you're probably thoroughly overloading the server.
:::

### My server shows Server Status Offline | Version Unknown. What can I do?

You need to [enable WebSockets](/administration/reverse-proxy/) on your reverse proxy.

---

## Docker

### How can I see Immich logs?

Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/guides/docker-help.md).

### How can I reduce the log verbosity of Redis?

To decrease Redis logs, you can add the following line to the `redis:` section of the `docker-compose.yml`:

`    command: redis-server --loglevel warning`

### How can I run Immich as a non-root user?

You can change the user in the container by setting the `user` argument in `docker-compose.yml` for each service.

[Example docker-compose.rootless.yml file](https://github.com/immich-app/immich/blob/main/docker/docker-compose.rootless.yml)

<ComposeBuilder query="rootless.enabled=true" />

You may need to add mount points or docker volumes for the following internal container paths:

- `redis:/data`

The non-root user/group needs read/write access to the volume mounts, including `UPLOAD_LOCATION` and `/cache` for machine-learning.

:::note Docker Compose Volumes
The Docker Compose top level volume element does not support non-root access, all of the above volumes must be local volume mounts.
:::

For a further hardened system, you can add the following block to every container.

<details>
<summary>docker-compose.yml</summary>

```yaml
security_opt:
  # Prevent escalation of privileges after the container is started
  - no-new-privileges:true
cap_drop:
  # Prevent access to raw network traffic
  - NET_RAW
```

</details>

### How can I purge data from Immich?

Data for Immich comes in two forms:

1. **Metadata** stored in a Postgres database, stored in the `DB_DATA_LOCATION` folder (previously `pg_data` Docker volume).
2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/administration/backup-and-restore#asset-types-and-storage-locations).

:::warning
This will destroy your database and reset your instance, meaning that you start from scratch.
:::

```bash title="Remove Immich (containers and volumes)"
docker compose down -v
```

After removing the containers and volumes, there are a few directories that need to be deleted to reset Immich to a new installation. Once they are deleted, Immich can be started back up and will be a fresh installation.

- `DB_DATA_LOCATION` contains the database, media info, and settings.
- `UPLOAD_LOCATION` contains all the media uploaded to Immich.

:::note Portainer
If you use portainer, bring down the stack in portainer. Go into the volumes section  
and remove all the volumes related to immich then restart the stack.
:::

### Why does the machine learning service report workers crashing?

:::note
If the error says the worker is exiting, then this is normal. This is a feature intended to reduce RAM consumption when the service isn't being used.
:::

There are a few reasons why this can happen.

If the error mentions SIGKILL or error code 137, it most likely means the service is running out of memory.
Consider either increasing the server's RAM or moving the service to a server with more RAM.

If it mentions SIGILL (note the lack of a K) or error code 132, it most likely means your server's CPU is incompatible with Immich.

## Database

### Why am I getting database ownership errors?

If you get database errors such as `FATAL:  data directory "/var/lib/postgresql/data" has wrong ownership` upon database startup, this is likely due to an issue with your filesystem.
NTFS and ex/FAT/32 filesystems are not supported. See [here](/install/requirements#special-requirements-for-windows-users) for more details.

### How can I verify the integrity of my database?

Database checksums are enabled by default for new installations since v1.104.0. You can check if they are enabled by running the following command.
A result of `on` means that checksums are enabled.

<details>
<summary>Check if checksums are enabled</summary>

```bash
docker exec -it immich_postgres psql --dbname=postgres --username=<DB_USERNAME> --command="show data_checksums"
 data_checksums
----------------
 on
(1 row)
```

</details>

If checksums are enabled, you can check the status of the database with the following command. A normal result is all `0`s.

<details>
<summary>Check for database corruption</summary>

```bash
docker exec -it immich_postgres psql --dbname=postgres --username=<DB_USERNAME> --command="SELECT datname, checksum_failures, checksum_last_failure FROM pg_stat_database WHERE datname IS NOT NULL"
  datname  | checksum_failures | checksum_last_failure
-----------+-------------------+-----------------------
 postgres  |                 0 |
 immich    |                 0 |
 template1 |                 0 |
 template0 |                 0 |
(4 rows)
```

</details>

You can also scan the Postgres database file structure for errors:

<details>
<summary>Scan for file structure errors</summary>
```bash
docker exec -it immich_postgres pg_amcheck --username=<DB_USERNAME> --heapallindexed --parent-check --rootdescend --progress --all --install-missing
```

A normal result will end something like this and return with an exit code of `0`:

```bash
7470/8832 relations (84%), 730829/734735 pages (99%)
8425/8832 relations (95%), 734367/734735 pages (99%)
8832/8832 relations (100%), 734735/734735 pages (100%)
```

</details>

If corruption is detected, you should immediately make a backup before performing any other work in the database.
To do so, you may need to set the `zero_damaged_pages=on` flag for the database server to allow `pg_dumpall` to succeed.
After taking a backup, the recommended next step is to restore the database from a healthy backup before corruption was detected.
The damaged database dump can be used to manually recover any changes made since the last backup, if needed.

The causes of possible corruption are many, but can include unexpected poweroffs or unmounts, use of a network share for Postgres data, or a poor storage medium such an SD card or failing HDD/SSD.

### Should I upgrade the database to a newer major version?

We recommend using the same version of PostgreSQL as in our default docker-compose.yml, however newer versions are also supported. Do note that the major upgrade process is not as simple as just changing the version of the container image. See [the PostgreSQL documentation](https://www.postgresql.org/docs/current/upgrading.html) for more detail.

## Other

### Will Immich save me money?

Probably not. At first glance, it seems like it might: compared to cloud-hosted commercial services, Immich has a one time purchase for the [product key](https://buy.immich.app/), but there are no monthly fees to run Immich on your own server. However, there are many other costs like hardware, storage for backups, and your own time. There are many good reasons for self-hosting, but saving money is rarely one of them.

### I really want a particular feature. Can I sponsor development of it?

No, we have no feature sponsorship scheme. The Immich team [works on Immich full-time](https://immich.app/blog/immich-joins-futo), which is supported by users paying for [a product key](https://buy.immich.app/). We'd rather not let money influence the roadmap. If features could be bought, we'd end up building Immich for the highest bidder instead of for all of our users.

A common alternative suggestion is to offer bounties that can be collected by anyone who submits a pull request, but we don't want those either; the Zig project has [explained the reason well](https://ziglang.org/news/bounties-damage-open-source-projects/).

If you really want to pay someone to build a feature you like, you're welcome to hire somebody directly and pay them for their time, but please note that we provide no guarantee that we'll merge the resulting work so we recommend that you check in with us first.

[huggingface]: https://huggingface.co/immich-app

---

### Docs Api (`docs/docs/api.md`)

# API

Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](https://api.immich.app/).

## Generator

OpenAPI is used to generate the client (Typescript, Dart) SDK. `openapi-generator-cli` can be installed [here](https://openapi-generator.tech/docs/installation/). The generated SDK is based on the `immich-openapi-specs.json` file, which is autogenerated by the server **when running in development mode**. The `immich-openapi-specs.json` file can be modified with `@nestjs/swagger` decorators used or referenced by controller endpoints. See the [NestJS OpenAPI docs](https://docs.nestjs.com/openapi/types-and-parameters) for more info. When you add a new endpoint or modify an existing one, you must run the server in development mode and run the command below to update the client SDK.

```bash
mise open-api
```

You can find the generated client SDK in the `packages/sdk/client` for Typescript SDK and `mobile/generated/openapi` for Dart SDK.

---

### Docs Administration Backup And Restore (`docs/docs/administration/backup-and-restore.md`)

# Backup and Restore

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import { mdiAlertCircle, mdiCheckCircle } from '@mdi/js';
import Icon from '@mdi/react';

A [3-2-1 backup strategy](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution. This page provides an overview on how to backup the database and the location of user-uploaded pictures and videos. A template bash script that can be run as a cron job is provided [here](/guides/template-backup-script.md)

:::danger
The instructions on this page show you how to prepare your Immich instance to be backed up, and which files to take a backup of. You still need to take care of using an actual backup tool to make a backup yourself.
:::

## Database

Immich stores [file paths](https://github.com/immich-app/immich/discussions/3299) and user metadata in the database. It does not scan the library folder, so database backups are essential.

### Automatic Database Backups

Immich automatically creates database backups for disaster-recovery purposes. These backups are stored in `UPLOAD_LOCATION/backups` and can be managed through the web interface.

You can adjust the backup schedule and retention settings in **Administration > Settings > Database Dump Settings** (default: keep last 14 backups, create daily at 2:00 AM).

:::caution
Database backups do **not** contain photos or videos — only metadata. They must be used together with a copy of the files in `UPLOAD_LOCATION` as outlined below.
:::

#### Creating a Backup

You can trigger a database backup manually:

1. Go to **Administration > Job Queues**
2. Click **Create job** in the top right
3. Select **Create Database Dump** and click **Confirm**

The backup will appear in `UPLOAD_LOCATION/backups` and counts toward your retention limit.

### Restoring a Database Backup

Immich provides two ways to restore a database backup: through the web interface or via the command line. The web interface is the recommended method for most users.

#### Restore from Settings {#restore-from-settings}

If you have an existing Immich installation:

<img
src={require('./img/restore-from-settings.webp').default}
title="Restore from settings"
/>

1. Go to **Administration > Maintenance**
2. Expand the **Restore database backup** section
3. You'll see a list of available backups with their version and creation date
4. Click **Restore** next to the backup you want to restore
5. Confirm the restore operation

:::info
Restoring a backup will wipe the current database and replace it with the backup. A restore point is automatically created before the operation begins, allowing rollback if the restore fails.
:::

#### Restore from Onboarding {#restore-from-onboarding}

If you're setting up Immich on a fresh installation and want to restore from an existing backup:

1. Download and populate `.env` and `docker-compose.yml` as per the [installation instructions](/install/docker-compose).
2. Move the previous's instance data directories containing `backups`, `encoded-video`, `library`, `profile`, `thumbs` and `upload` into the new `UPLOAD_LOCATION`
3. **(For external libraries)** If you used external library feature in your previous instance, make sure that the mount settings in your new `docker-compose.yml` reflect the same structure. You may need to move files accordingly.

:::info Example

Assuming your previous `UPLOAD_LOCATION` was `UPLOAD_LOCATION=/my-broken-instance/media` and your new one is `UPLOAD_LOCATION=/a-brand-new-instance/data`, you will need to perform the following file moves:

```
/my-broken-instance/media/backups          ->    /a-brand-new-instance/data/backups
/my-broken-instance/media/encoded-video    ->    /a-brand-new-instance/data/encoded-video
/my-broken-instance/media/library          ->    /a-brand-new-instance/data/library
/my-broken-instance/media/profile          ->    /a-brand-new-instance/data/profile
/my-broken-instance/media/thumbs           ->    /a-brand-new-instance/data/thumbs
/my-broken-instance/media/upload           ->    /a-brand-new-instance/data/upload
```

:::

4. Start the Immich services with `docker compose up -d`

<img
src={require('./img/restore-from-onboarding.webp').default}
title="Restore from onboarding"
/>

5. On the welcome screen, click **Restore from backup**
6. Immich will enter maintenance mode and display integrity checks for your storage folders
7. Review the folder status to ensure your library files are accessible
8. Click **Next** to proceed to backup selection
9. Select a backup from the list or upload a backup file (`.sql.gz`)
10. Click **Restore** to begin the restoration process

:::tip
Before restoring, ensure your `UPLOAD_LOCATION` folders contain the same files that existed when the backup was created. The integrity check will show you which folders are readable/writable and how many files they contain.
:::

### Uploading a Backup File {#uploading-backup}

You can upload a database backup file directly:

1. In the **Restore database backup** section, click **Select from computer**
2. Choose a `.sql.gz` file
3. The uploaded backup will appear in the list with an `uploaded-` prefix
4. Click **Restore** to restore from the uploaded file

### Backup Version Compatibility {#backup-compatibility}

When viewing backups, Immich displays compatibility indicators based on the current version and the information from the filename:

- <Icon path={mdiCheckCircle} size={1} color="green"/> Backup version matches current Immich version
- <Icon path={mdiAlertCircle} size={1} color="#feb001"/> Backup was created with a different Immich version
- <Icon path={mdiAlertCircle} size={1} color="red"/> Could not determine backup version

:::warning
Restoring a backup from a different Immich version may require database migrations. The restore process will attempt to run migrations automatically, but you should ensure you're restoring to a compatible version when possible.
:::

### Restore Process {#restore-process}

During restoration, Immich will:

1. Create a backup of the current database (restore point)
2. Restore the selected backup
3. Run database migrations if needed
4. Perform a health check to verify the restore succeeded

If the restore fails (e.g., corrupted backup or missing admin user), Immich will automatically roll back to the restore point.

### Restore via Command Line {#restore-cli}

For advanced users or automated recovery scenarios, you can restore a database backup using the command line.

<Tabs>
  <TabItem value="Linux system" label="Linux system" default>

```bash title='Backup'
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME> | gzip > "/path/to/backup/dump.sql.gz"
```

```bash title='Restore'
docker compose down -v  # CAUTION! Deletes all Immich data to start from scratch
## Uncomment the next line and replace DB_DATA_LOCATION with your Postgres path to permanently reset the Postgres database
# rm -rf DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch
docker compose pull             # Update to latest version of Immich (if desired)
docker compose create           # Create Docker containers for Immich apps without running them
docker start immich_postgres    # Start Postgres server
sleep 10                        # Wait for Postgres server to start up
# Check the database user if you deviated from the default
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
gunzip --stdout "/path/to/backup/dump.sql.gz" \
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
| docker exec -i immich_postgres psql --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME> --single-transaction --set ON_ERROR_STOP=on  # Restore Backup
docker compose up -d            # Start remainder of Immich apps
```

  </TabItem>
  <TabItem value="Windows system (PowerShell)" label="Windows system (PowerShell)">

```powershell title='Backup'
# Replace <DB_USERNAME> with the database username - usually postgres unless you have changed it.
# Replace <DB_DATABASE_NAME> with the database name - usually immich unless you have changed it.
[System.IO.File]::WriteAllLines("C:\absolute\path\to\backup\dump.sql", (docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME>))
```

```
/* Detailed source-code truncated for AI context efficiency. */
```

  </TabItem>
</Tabs>

:::warning
The backup and restore process changed in v2.5.0, if you have a backup created with an older version of Immich, use the documentation version selector to find manual restore instructions for your backup.
:::

:::note
For the database restore to proceed properly, it requires a completely fresh install (i.e., the Immich server has never run since creating the Docker containers). If the Immich app has run, you may encounter Postgres conflicts (relation already exists, violated foreign key constraints, etc.). In this case, delete the `DB_DATA_LOCATION` folder to reset the database.
:::

:::tip
Some deployment methods make it difficult to start the database without also starting the server. In these cases, set the environment variable `DB_SKIP_MIGRATIONS=true` before starting the services. This prevents the server from running migrations that interfere with the restore process. Remove this variable and restart services after the database is restored.
:::

:::tip
The provided restore process ensures your database is never in a broken state by committing all changes in one transaction. This may be undesirable behaviour in some circumstances, you can disable it by removing `--single-transaction --set ON_ERROR_STOP=on` from the command.
:::

## Filesystem

Immich does not handle filesystem backups for you. You have to arrange these yourself! Immich stores two types of content in the filesystem: (a) original, unmodified assets (photos and videos), and (b) generated content. We recommend backing up the entire contents of `UPLOAD_LOCATION`, but only the original content is critical, which is stored in the following folders:

1. `UPLOAD_LOCATION/library`
2. `UPLOAD_LOCATION/upload`
3. `UPLOAD_LOCATION/profile`

If you choose to back up only those folders, you will need to rerun the transcoding and thumbnail generation jobs for all assets after you restore from a backup.

:::caution
If you moved some of these folders onto a different storage device, such as `profile/`, make sure to adjust the backup path to match your setup
:::

### Asset Types and Storage Locations

Some storage locations are impacted by the Storage Template. See below for more details.

<Tabs>
  <TabItem value="Storage Template Off (Default)." label="Storage Template Off (Default)." default>

:::note
The `UPLOAD_LOCATION/library` folder is not used by default on new machines running version 1.92.0. It is used only if the system administrator activated the storage template engine,
for more info read the [release notes](https://github.com/immich-app/immich/releases/tag/v1.92.0#:~:text=the%20partner%E2%80%99s%20assets.-,Hardening%20storage%20template).
:::

**1. User-Specific Folders:**

- Each user has a unique string representing them.
- You can find your user ID in Account Account Settings -> Account -> User ID.

**2. Asset Types and Storage Locations:**

- **Source Assets:**
  - Original assets uploaded through the browser interface & mobile & CLI.
  - Stored in `UPLOAD_LOCATION/upload/<userID>`.
- **Avatar Images:**
  - User profile images.
  - Stored in `UPLOAD_LOCATION/profile/<userID>`.
- **Thumbs Images:**
  - Preview images (small thumbnails and large previews) for each asset and thumbnails for recognized faces.
  - Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
- **Encoded Assets:**
  - Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
  - Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
- **Database Dump Backups:**
  - Automatic database backups created by Immich for disaster recovery.
  - Stored in `UPLOAD_LOCATION/backups/`.
- **Postgres**
  - The Immich database containing all the information to allow the system to function properly.  
    **Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
  - Stored in `DB_DATA_LOCATION`.

</TabItem>
  <TabItem value="Storage Template On" label="Storage Template On">

:::note
If you choose to activate the storage template engine, it will move all assets to `UPLOAD_LOCATION/library/<userID>`.

When you turn off the storage template engine, it will leave the assets in `UPLOAD_LOCATION/library/<userID>` and will not return them to `UPLOAD_LOCATION/upload`.  
**New assets** will be saved to `UPLOAD_LOCATION/upload`.
:::

**1. User-Specific Folders:**

- Each user has a unique string representing them.
  - The administrator can set a Storage Label for a user, which will be used instead of `<userID>` for the `library/` folder.
  - The Admin has a default storage label of `admin`.
- You can find your user ID and Storage Label in Account Account Settings -> Account -> User ID.

**2. Asset Types and Storage Locations:**

- **Source Assets:**
  - Original assets uploaded through the browser interface, mobile, and CLI.
  - Stored in `UPLOAD_LOCATION/library/<userID>`.
- **Avatar Images:**
  - User profile images.
  - Stored in `UPLOAD_LOCATION/profile/<userID>`.
- **Thumbs Images:**
  - Preview images (blurred, small, large) for each asset and thumbnails for recognized faces.
  - Stored in `UPLOAD_LOCATION/thumbs/<userID>`.
- **Encoded Assets:**
  - Videos that have been re-encoded from the original for wider compatibility. The original is not removed.
  - Stored in `UPLOAD_LOCATION/encoded-video/<userID>`.
- **Files in Upload Queue (Mobile):**
  - Files uploaded through mobile apps.
  - Temporarily located in `UPLOAD_LOCATION/upload/<userID>`.
  - Transferred to `UPLOAD_LOCATION/library/<userID>` upon successful upload.
- **Database Dump Backups:**
  - Automatic database backups created by Immich for disaster recovery.
  - Stored in `UPLOAD_LOCATION/backups/`.
- **Postgres**
  - The Immich database containing all the information to allow the system to function properly.  
    **Note:** This folder will only appear to users who have made the changes mentioned in [v1.102.0](https://github.com/immich-app/immich/discussions/8930) (an optional, non-mandatory change) or who started with this version.
  - Stored in `DB_DATA_LOCATION`.

</TabItem>

</Tabs>

:::danger
Do not touch the files inside these folders under any circumstances except taking a backup. Changing or removing an asset can cause untracked and missing files.
You can think of it as App-Which-Must-Not-Be-Named, the only access to viewing, changing and deleting assets is only through the mobile or browser interface.
:::

## Backup ordering

A backup of Immich should contain both the database and the asset files. When backing these up it's possible for them to get out of sync, potentially resulting in broken assets after you restore.  
The best way of dealing with this is to stop the immich-server container while you take a backup. If nothing is changing then the backup will always be in sync.

If stopping the container is not an option, then the recommended order is to back up the database first, and the filesystem second. This way, the worst case scenario is that there are files on the filesystem that the database doesn't know about. If necessary, these can be (re)uploaded manually after a restore. If the backup is done the other way around, with the filesystem first and the database second, it's possible for the restored database to reference files that aren't in the filesystem backup, thus resulting in broken assets.

---

### Docs Administration Email Notification (`docs/docs/administration/email-notification.mdx`)

# Email Notifications

Immich supports the option to send notifications via Email for the following events:

- Creating a new user
- Notifying a user when they get added to a shared album
- Informing other users about the addition of new assets to a shared album

## SMTP settings

You can access the settings panel from the web at `Administration -> Settings -> Notification settings`.

Under Email, enter the required details to connect with an SMTP server.

You can use [this guide](/guides/smtp-gmail) to use Gmail's SMTP server.

You can use [this guide](/guides/smtp-microsoft365) to use Microsoft's SMTP server.

## User's notifications settings

Users can manage their email notification settings from their account settings page on the web. They can choose to turn email notifications on or off for the following events:

<img src={require('./img/user-notifications-settings.webp').default} width="80%" title="User notification settings" />

## Notification templates

You can override the default notification text with custom templates in HTML format. You can use tags to show dynamic tags in your templates.

<img src={require('./img/user-notifications-templates.webp').default} width="80%" title="User notification templates" />

---

### Docs Administration Jobs Workers (`docs/docs/administration/jobs-workers.md`)

# Jobs and Workers

## Workers

### Architecture

The `immich-server` container contains multiple workers:

- `api`: responds to API requests for data and files for the web and mobile app.
- `microservices`: handles most other work, such as thumbnail generation and video encoding, in the form of _jobs_. Simply put, a job is a request to process data in the background.

## Split workers

If you prefer to throttle or distribute the workers, you can do this using the [environment variables](/install/environment-variables) to specify which container should pick up which tasks.

For example, for a simple setup with one container for the Web/API and one for all other microservices, you can do the following:

Copy the entire `immich-server` block as a new service and make the following changes to the **copy**:

```diff
- immich-server:
-   container_name: immich_server
...
-   ports:
-     - 2283:2283
+ immich-microservices:
+   container_name: immich_microservices
```

Once you have two copies of the immich-server service, make the following changes to each one. This will allow one container to only serve the web UI and API, and the other one to handle all other tasks.

```diff
services:
  immich-server:
    ...
+   environment:
+     IMMICH_WORKERS_INCLUDE: 'api'

  immich-microservices:
    ...
+   environment:
+     IMMICH_WORKERS_EXCLUDE: 'api'
```

## Jobs

When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page.

<img src={require('./img/admin-jobs.webp').default} width="60%" title="Admin jobs" />

Additionally, some jobs (such as memories generation) run on a schedule, which is every night at midnight by default. To change when they run or enable/disable a job navigate to System Settings -> [Nightly Tasks Settings](https://my.immich.app/admin/system-settings?isOpen=nightly-tasks).

<img src={require('./img/admin-nightly-tasks.webp').default} width="80%" title="Admin nightly tasks" />

:::note
Some jobs ([External Libraries](/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings.
:::

## Job processing order

The below diagram shows the job run order for newly uploaded files

```mermaid
graph TD
    A[Asset Upload] --> B[Metadata Extraction]
    B --> C[Storage Template Migration]
    C --> D["Thumbnail Generation (Large, small, blurred and person)"]
    D --> E[Smart Search]
    D --> F[Face Detection]
    D --> G[OCR]
    D --> H[Video Transcoding]
    E --> I[Duplicate Detection]
    F --> J[Facial Recognition]
```

---

### Docs Administration Maintenance Mode (`docs/docs/administration/maintenance-mode.md`)

# Maintenance Mode

Maintenance mode is used to perform administrative tasks such as restoring backups to Immich.

You can enter maintenance mode by either:

- Selecting "Switch to maintenance mode" in `Maintenance` tab in administration.
- Running the enable maintenance mode [administration command](./server-commands.md).

## Logging in during maintenance

Maintenance mode uses a separate login system which is handled automatically behind the scenes in most cases. Enabling maintenance mode in settings will automatically log you into maintenance mode when the server comes back up.

If you find that you've been logged out, you can:

- Open the logs for the Immich server and look for _"🚧 Immich is in maintenance mode, you can log in using the following URL:"_
- Run the enable maintenance mode [administration command](./server-commands.md) again, this will give you a new URL to login with.
- Run the disable maintenance mode [administration command](./server-commands.md) then re-enter through system settings.

---

### Docs Administration Oauth (`docs/docs/administration/oauth.md`)

# OAuth Authentication

This page contains details about using OAuth in Immich.

:::tip
Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobile Redirect URI](#mobile-redirect-uri) for an alternative solution.
:::

## Overview

Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including:

- [Authentik](https://integrations.goauthentik.io/media/immich/)
- [Authelia](https://www.authelia.com/integration/openid-connect/immich/)
- [Okta](https://www.okta.com/openid-connect/)
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)
- [Keycloak](https://www.keycloak.org)

## Prerequisites

Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.

1. Create a new (Client) Application
   1. The **Provider** type should be `OpenID Connect` or `OAuth2`
   2. The **Client type** should be `Confidential`
   3. The **Application** type should be `Web`
   4. The **Grant** type should be `Authorization Code`

2. Configure Redirect URIs/Origins

   The **Sign-in redirect URIs** should include:
   - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/features/mobile-app.mdx)
   - `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client
   - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client

   Redirect URIs should contain all the domains you will be using to access Immich. Some examples include:

   Mobile
   - `app.immich:///oauth-callback` (You **MUST** include this for iOS and Android mobile apps to work properly)

   Localhost
   - `http://localhost:2283/auth/login`
   - `http://localhost:2283/user-settings`

   Local IP
   - `http://192.168.0.200:2283/auth/login`
   - `http://192.168.0.200:2283/user-settings`

   Hostname
   - `https://immich.example.com/auth/login`
   - `https://immich.example.com/user-settings`

3. Configure Backchannel logout URL

   If the authentication server supports it, the **Backchannel logout URL** can be specified, and it is of the form: `http://DOMAIN:PORT/api/oauth/backchannel-logout`.

## Enable OAuth

Once you have a new OAuth client application configured, Immich can be configured using the Administration Settings page, available on the web (Administration -> Settings).

| Setting                                              | Type    | Default              | Description                                                                         |
| ---------------------------------------------------- | ------- | -------------------- | ----------------------------------------------------------------------------------- |
| Enabled                                              | boolean | false                | Enable/disable OAuth                                                                |
| `issuer_url`                                         | URL     | (required)           | Required. Self-discovery URL for client (from previous step)                        |
| `client_id`                                          | string  | (required)           | Required. Client ID (from previous step)                                            |
| `client_secret`                                      | string  | (required)           | Required. Client Secret (previous step)                                             |
| `scope`                                              | string  | openid email profile | Full list of scopes to send with the request (space delimited)                      |
| `id_token_signed_response_alg`                       | string  | RS256                | The algorithm used to sign the id token (examples: RS256, HS256)                    |
| `userinfo_signed_response_alg`                       | string  | none                 | The algorithm used to sign the userinfo response (examples: RS256, HS256)           |
| `prompt`                                             | string  | (empty)              | Prompt parameter for authorization url (examples: select_account, login, consent)   |
| `end_session_endpoint`                               | URL     | (empty)              | Http(s) alternative end session endpoint (logout URI)                               |
| Request timeout                                      | string  | 30,000 (30 seconds)  | Number of milliseconds to wait for http requests to complete before giving up       |
| Storage Label Claim                                  | string  | preferred_username   | Claim mapping for the user's storage label**¹**                                     |
| Role Claim                                           | string  | immich_role          | Claim mapping for the user's role. (should return "user" or "admin")**¹**           |
| Storage Quota Claim                                  | string  | immich_quota         | Claim mapping for the user's storage**¹**                                           |
| Default Storage Quota (GiB)                          | number  | 0                    | Default quota for user without storage quota claim (empty for unlimited quota)      |
| Button Text                                          | string  | Login with OAuth     | Text for the OAuth button on the web                                                |
| Auto Register                                        | boolean | true                 | When true, will automatically register a user the first time they sign in           |
| [Auto Launch](#auto-launch)                          | boolean | false                | When true, will skip the login page and automatically start the OAuth login process |
| [Mobile Redirect URI Override](#mobile-redirect-uri) | URL     | (empty)              | Http(s) alternative mobile redirect URI                                             |

:::note Claim Options [1]

Claims are applied when a user is first registered. On subsequent logins, claims present in the OAuth profile are synchronized to the Immich user profile. Claims that are absent from the profile are left unchanged.

The storage label is an exception: with the default claim (`preferred_username`), it is only set at registration and is not updated on later logins. To keep the storage label in sync with the IdP, configure a different Storage Label Claim (for example `uid`).

The storage quota claim is a number in GiB: `-1` for unlimited, `0` to block uploads, any positive value for a fixed quota.

:::

:::info
The Issuer URL should look something like the following, and return a valid json document.

- `https://accounts.google.com/.well-known/openid-configuration`
- `http://localhost:9000/application/o/immich/.well-known/openid-configuration`

The `.well-known/openid-configuration` part of the url is optional and will be automatically added during discovery.
:::

## Auto Launch

When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`.
Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?autoLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich.

## Mobile Redirect URI

The redirect URI for the mobile app is `app.immich:///oauth-callback`, which is a [Custom Scheme](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app). If this custom scheme is an invalid redirect URI for your OAuth Provider, you can work around this by doing the following:

1. Configure an http(s) endpoint to forwards requests to `app.immich:///oauth-callback`
2. Whitelist the new endpoint as a valid redirect URI with your provider.
3. Specify the new endpoint as the `Mobile Redirect URI Override`, in the OAuth settings.

With these steps in place, you should be able to use OAuth from the [Mobile App](/features/mobile-app.mdx) without a custom scheme redirect URI.

:::info
Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:///oauth-callback`, and can be used for step 1.
:::

## Example Configuration

<details>
<summary>Authelia Example</summary>

### Authelia Example

Here's an example of OAuth configured for Authelia:

This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich.
The configuration concerning the quota is optional.

```
/* Detailed source-code truncated for AI context efficiency. */
```

Configuration of OAuth in Immich System Settings

| Setting                            | Value                                                               |
| ---------------------------------- | ------------------------------------------------------------------- |
| Issuer URL                         | `https://auth.example.com`                                          |
| Client ID                          | immich                                                              |
| Client Secret                      | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... |
| Token Endpoint Auth Method         | client_secret_post                                                  |
| Scope                              | openid email profile immich_scope                                   |
| ID Token Signed Response Algorithm | RS256                                                               |
| Userinfo Signed Response Algorithm | RS256                                                               |
| End Session Endpoint               | https://auth.example.com/logout?rd=https://immich.example.com/      |
| Storage Label Claim                | uid                                                                 |
| Storage Quota Claim                | immich_quota                                                        |
| Default Storage Quota (GiB)        | 0 (empty for unlimited quota)                                       |
| Button Text                        | Sign in with Authelia (optional)                                    |
| Auto Register                      | Enabled (optional)                                                  |
| Auto Launch                        | Enabled (optional)                                                  |
| Mobile Redirect URI Override       | Disable                                                             |
| Mobile Redirect URI                |                                                                     |

</details>

<details>
<summary>Authentik Example</summary>

### Authentik Example

Here's an example of OAuth configured for Authentik:

Configuration of Authorised redirect URIs (Authentik OAuth2/OpenID Provider)

<img src={require('./img/authentik-redirect-uris-example.webp').default} width='70%' title="Authentik authorised redirect URIs" />

Configuration of OAuth in Immich System Settings

| Setting                      | Value                                                               |
| ---------------------------- | ------------------------------------------------------------------- |
| Issuer URL                   | `https://authentik.example.com/application/o/immich/`               |
| Client ID                    | AFCj2rM1f4rps**\*\*\*\***\***\*\*\*\***lCLEum6hH9...                |
| Client Secret                | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... |
| Scope                        | openid email profile                                                |
| Signing Algorithm            | RS256                                                               |
| Storage Label Claim          | preferred_username                                                  |
| Storage Quota Claim          | immich_quota                                                        |
| Default Storage Quota (GiB)  | 0 (empty for unlimited quota)                                       |
| Button Text                  | Sign in with Authentik (optional)                                   |
| Auto Register                | Enabled (optional)                                                  |
| Auto Launch                  | Enabled (optional)                                                  |
| Mobile Redirect URI Override | Disable                                                             |
| Mobile Redirect URI          |                                                                     |

</details>

<details>
<summary>Google Example</summary>

### Google Example

Here's an example of OAuth configured for Google:

Configuration of Authorised redirect URIs (Google Console)

<img src={require('./img/google-redirect-uris-example.webp').default} width='50%' title="Google authorised redirect URIs" />

Configuration of OAuth in Immich System Settings

| Setting                      | Value                                                                        |
| ---------------------------- | ---------------------------------------------------------------------------- |
| Issuer URL                   | `https://accounts.google.com`                                                |
| Client ID                    | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
| Client Secret                | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO                              |
| Scope                        | openid email profile                                                         |
| Signing Algorithm            | RS256                                                                        |
| Storage Label Claim          | preferred_username                                                           |
| Storage Quota Claim          | immich_quota                                                                 |
| Default Storage Quota (GiB)  | 0 (empty for unlimited quota)                                                |
| Button Text                  | Sign in with Google (optional)                                               |
| Auto Register                | Enabled (optional)                                                           |
| Auto Launch                  | Enabled                                                                      |
| Mobile Redirect URI Override | Enabled (required)                                                           |
| Mobile Redirect URI          | `https://example.immich.app/api/oauth/mobile-redirect`                       |

</details>

<details>
<summary>Keycloak Example</summary>

### Keycloak Example

Here's an example of OAuth configured for Keycloak:

Create your immich client on your Keycloak Realm.

<img src={require('./img/keycloak-general-settings.webp').default} width='100%' title="Keycloak Client general Settings" />
<img src={require('./img/keycloak-access-settings.webp').default} width='100%' title="Keycloak Client Access Settings" />
<img src={require('./img/keycloak-capability-config.webp').default} width='100%' title="Keycloak Client Capability Configuration" />

Configuration of OAuth in Immich System Settings

| Setting                      | Value                                                 |
| ---------------------------- | ----------------------------------------------------- |
| Issuer URL                   | `https://<KEYCLOAK_DOMAIN>/realms/<YOUR_REALM>`       |
| Client ID                    | immich                                                |
| Client Secret                | can be obtained from Clients -> immich -> Credentials |
| Scope                        | openid email profile                                  |
| Signing Algorithm            | RS256                                                 |
| Storage Label Claim          | preferred_username                                    |
| Role Claim                   | immich_role                                           |
| Storage Quota Claim          | immich_quota                                          |
| Default Storage Quota (GiB)  | 0 (empty for unlimited quota)                         |
| Button Text                  | Sign in with Keycloak (recommended)                   |
| Auto Register                | Enabled (optional)                                    |
| Auto Launch                  | Enabled (optional)                                    |
| Mobile Redirect URI Override | Disabled                                              |
| Mobile Redirect URI          |                                                       |

Role Claim can be managed via Client Role. Remember to create a mapper with claim name `immich_role`.

</details>

[oidc]: https://openid.net/connect/

---

### Docs Administration Postgres Standalone (`docs/docs/administration/postgres-standalone.md`)

# Pre-existing Postgres

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

While not officially recommended, it is possible to run Immich using a pre-existing Postgres server. To use this setup, you should have a baseline level of familiarity with Postgres and the Linux command line. If you do not have these, we recommend using the default setup with a dedicated Postgres container.

By default, Immich expects superuser permission on the Postgres database and requires certain extensions to be installed. This guide outlines the steps required to prepare a pre-existing Postgres server to be used by Immich.

:::tip
Running with a pre-existing Postgres server can unlock powerful administrative features, including logical replication and streaming write-ahead log backups using programs like pgBackRest or Barman.
:::

## Prerequisites

You must install pgvector as it is a prerequisite for VectorChord.
The easiest way to do this on Debian/Ubuntu is by adding the [PostgreSQL Apt repository][pg-apt] and then
running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`).

You must install VectorChord into your instance of Postgres using their [instructions][vchord-install]. After installation, add `shared_preload_libraries = 'vchord.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vchord.so'`.

:::note Supported versions
Immich is known to work with Postgres versions `>= 14, < 20`.

VectorChord is known to work with pgvector versions `>= 0.7, < 0.9`.

The Immich server will check the VectorChord version on startup to ensure compatibility, and refuse to start if a compatible version is not found.
The current accepted range for VectorChord is `>= 0.3, < 2.0`.
:::

## Specifying the connection URL

<ComposeBuilder query="database.external=true" />

You can connect to your pre-existing Postgres server by setting the `DB_URL` environment variable in the `.env` file.

```
DB_URL='postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport/immichdatabasename'

# require a SSL connection to Postgres
# DB_URL='postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport/immichdatabasename?sslmode=require'

# require a SSL connection, but don't enforce checking the certificate name
# DB_URL='postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport/immichdatabasename?sslmode=require&sslmode=no-verify'
```

## With superuser permission

Typically Immich expects superuser permission in the database, which you can grant by running `ALTER USER <immichdbusername> WITH SUPERUSER;` at the `psql` console. If you prefer not to grant superuser permissions, follow the instructions in the next section.

## Without superuser permission

:::caution
This method is recommended for **advanced users only** and often requires manual intervention when updating Immich.
:::

:::danger
Currently, automated backups require superuser permission due to the usage of `pg_dumpall`.
:::

Immich can run without superuser permissions by following the below instructions at the `psql` prompt to prepare the database.

```sql title="Set up Postgres for Immich"
CREATE DATABASE <immichdatabasename>;
\c <immichdatabasename>
BEGIN;
ALTER DATABASE <immichdatabasename> OWNER TO <immichdbusername>;
CREATE EXTENSION vchord CASCADE;
CREATE EXTENSION earthdistance CASCADE;
COMMIT;
```

### Updating VectorChord

When installing a new version of VectorChord, you will need to manually update the extension and reindex by connecting to the Immich database and running:

```
ALTER EXTENSION vchord UPDATE;
REINDEX INDEX face_index;
REINDEX INDEX clip_index;
```

## Migrating to VectorChord

VectorChord is the successor extension to pgvecto.rs, allowing for higher performance, lower memory usage and higher quality results for smart search and facial recognition.

### Migrating from pgvecto.rs

Support for pgvecto.rs has been dropped as of 3.0, hence all users currently using pgvecto.rs should migrate to VectorChord. There are two primary approaches to do so.

The easiest option is to have both extensions installed during the migration:

<details>
<summary>Migration steps (automatic)</summary>
1. Ensure you still have pgvecto.rs installed
2. Install `pgvector` (`>= 0.7, < 0.9`). The easiest way to do this is on Debian/Ubuntu by adding the [PostgreSQL Apt repository][pg-apt] and then running `apt install postgresql-NN-pgvector`, where `NN` is your Postgres version (e.g., `16`)
3. [Install VectorChord][vchord-install]
4. Add `shared_preload_libraries= 'vchord.so, vectors.so'` to your `postgresql.conf`, making sure to include _both_ `vchord.so` and `vectors.so`. You may include other libraries here as well if needed
5. Restart the Postgres database
6. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;` using psql or your choice of database client
7. Start Immich and wait for the logs `Reindexed face_index` and `Reindexed clip_index` to be output
8. If Immich does not have superuser permissions, run the SQL command `DROP EXTENSION vectors;`
9. Drop the old schema by running `DROP SCHEMA vectors;`
10. Remove the `vectors.so` entry from the `shared_preload_libraries` setting
11. Restart the Postgres database
12. Uninstall pgvecto.rs (e.g. `apt-get purge vectors-pg14` on Debian-based environments, replacing `pg14` as appropriate). `pgvector` must remain installed as it provides the data types used by `vchord`

</details>

If it is not possible to have both VectorChord and pgvecto.rs installed at the same time, you can perform the migration with more manual steps:

<details>
<summary>Migration steps (manual)</summary>
1. While pgvecto.rs is still installed, run the following SQL command using psql or your choice of database client. Take note of the number outputted by this command as you will need it later

```sql
SELECT atttypmod as dimsize
    FROM pg_attribute f
    JOIN pg_class c ON c.oid = f.attrelid
    WHERE c.relkind = 'r'::char
    AND f.attnum > 0
    AND c.relname = 'smart_search'::text
    AND f.attname = 'embedding'::text;
```

2. Remove references to pgvecto.rs using the below SQL commands

```sql
DROP INDEX IF EXISTS clip_index;
DROP INDEX IF EXISTS face_index;
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE real[];
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[];
```

3. [Install VectorChord][vchord-install]
4. Change the columns back to the appropriate vector types, replacing `<number>` with the number from step 1

```sql
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
ALTER TABLE smart_search ALTER COLUMN embedding SET DATA TYPE vector(<number>);
ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512);
```

5. Start Immich and let it create new indices using VectorChord

</details>

### Migrating from pgvector

<details>
<summary>Migration steps</summary>
1. Ensure you have at least `0.7.0` of pgvector installed. If it is below that, please upgrade it and run the SQL command `ALTER EXTENSION vector UPDATE;` using psql or your choice of database client
2. Follow the Prerequisites to install VectorChord
3. If Immich does not have superuser permissions, run the SQL command `CREATE EXTENSION vchord CASCADE;`
4. Remove the `DB_VECTOR_EXTENSION=pgvector` environmental variable as it will make Immich still use pgvector if set
5. Start Immich and let it create new indices using VectorChord

</details>

Note that VectorChord itself uses pgvector types, so you should not uninstall pgvector after following these steps.

[vchord-install]: https://docs.vectorchord.ai/vectorchord/getting-started/installation.html
[pg-apt]: https://www.postgresql.org/download/linux/#generic

---

### Docs Administration Reverse Proxy (`docs/docs/administration/reverse-proxy.md`)

# Reverse Proxy

Users can deploy a custom reverse proxy that forwards requests to Immich. This way, the reverse proxy can handle TLS termination, load balancing, or other advanced features. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Real-IP`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.

:::caution
Immich does not support being served on a sub-path such as `location /immich {`. It has to be served on the root path of a (sub)domain.
:::

:::info
If your reverse proxy uses the [Let's Encrypt](https://letsencrypt.org/) [http-01 challenge](https://letsencrypt.org/docs/challenge-types/#http-01-challenge), you may want to verify that the Immich well-known endpoint (`/.well-known/immich`) gets correctly routed to Immich, otherwise it will likely be routed elsewhere and the mobile app may run into connection issues.
:::

### Nginx example config

Below is an example config for nginx. Make sure to set `public_url` to the front-facing URL of your instance, and `backend_url` to the path of the Immich server.

```nginx
server {
    server_name <public_url>;

    # allow large file uploads
    client_max_body_size 50000M;

    # disable buffering uploads to prevent OOM on reverse proxy server and make uploads twice as fast (no pause)
    proxy_request_buffering off;

    # increase body buffer to avoid limiting upload speed
    client_body_buffer_size 1024k;

    # Set headers
    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;

    # enable websockets: http://nginx.org/en/docs/http/websocket.html
    proxy_http_version 1.1;
    proxy_redirect     off;

    # set timeout
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    send_timeout       600s;

    location / {
        proxy_pass http://<backend_url>:2283;
        proxy_set_header   Upgrade    $http_upgrade;
        proxy_set_header   Connection "upgrade";
    }

    # useful when using Let's Encrypt http-01 challenge
    # location = /.well-known/immich {
    #     proxy_pass http://<backend_url>:2283;
    # }
}
```

### Caddy example config

As an alternative to nginx, you can also use [Caddy](https://caddyserver.com/) as a reverse proxy (with automatic HTTPS configuration). Below is an example config.

```
immich.example.org {
    reverse_proxy http://<snip>:2283
}
```

### Apache example config

Below is an example config for Apache2 site configuration.

```ApacheConf
<VirtualHost *:80>
   ServerName <snip>
   ProxyRequests Off
   # set timeout in seconds
   ProxyPass / http://127.0.0.1:2283/ timeout=600 upgrade=websocket
   ProxyPassReverse / http://127.0.0.1:2283/
   ProxyPreserveHost On
</VirtualHost>
```

### Traefik Proxy example config

The example below is for Traefik version 3.

The most important is to increase the `respondingTimeouts` of the entrypoint used by immich. In this example of entrypoint `websecure` for port `443`. Per default it's set to 60s which leeds to videos stop uploading after 1 minute (Error Code 499). With this config it will fail after 10 minutes which is in most cases enough. Increase it if needed.

`traefik.yaml`

```yaml
[...]
entryPoints:
  websecure:
    address: :443
    # this section needs to be added
    transport:
      respondingTimeouts:
        readTimeout: 600s
        idleTimeout: 600s
```

The second part is in the `docker-compose.yml` file where immich is in. Add the Traefik specific labels like in the example.

`docker-compose.yml`

```yaml
services:
  immich-server:
    [...]
    labels:
      traefik.enable: true
      # increase readingTimeouts for the entrypoint used here
      traefik.http.routers.immich.entrypoints: websecure
      traefik.http.routers.immich.rule: Host(`immich.example.com`)
      traefik.http.services.immich.loadbalancer.server.port: 2283
```

Keep in mind, that Traefik needs to communicate with the network where immich is in, usually done
by adding the Traefik network to the `immich-server`.

---

### Docs Administration Server Commands (`docs/docs/administration/server-commands.md`)

# Server Commands

The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands:

| Command                    | Description                                                   |
| -------------------------- | ------------------------------------------------------------- |
| `help`                     | Display help                                                  |
| `reset-admin-password`     | Reset the password for the admin user                         |
| `disable-password-login`   | Disable password login                                        |
| `enable-password-login`    | Enable password login                                         |
| `disable-maintenance-mode` | Disable maintenance mode                                      |
| `enable-maintenance-mode`  | Enable maintenance mode                                       |
| `enable-oauth-login`       | Enable OAuth login                                            |
| `disable-oauth-login`      | Disable OAuth login                                           |
| `list-users`               | List Immich users                                             |
| `grant-admin`              | Grant admin privileges to a user (by email)                   |
| `revoke-admin`             | Revoke admin privileges from a user (by email)                |
| `version`                  | Print Immich version                                          |
| `change-media-location`    | Change database file paths to align with a new media location |
| `schema-check`             | Verify database migrations and check for schema drift         |

## How to run a command

To run a command, [connect](/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin <command>`.

## Examples

Reset Admin Password

```
immich-admin reset-admin-password
Found Admin:
- ID=e65e6f88-2a30-4dbe-8dd9-1885f4889b53
- OAuth ID=
- Email=admin@example.com
- Name=Immich Admin
? Please choose a new password (optional) immich-is-cool
? Invalidate existing sessions? Yes
The admin password has been updated.
```

Disable Password Login

```
immich-admin disable-password-login
Password login has been disabled.
```

Enable Password Login

```
immich-admin enable-password-login
Password login has been enabled.
```

Disable Maintenance Mode

```
immich-admin disable-maintenance-mode
Maintenance mode has been disabled.
```

Enable Maintenance Mode

```
immich-admin enable-maintenance-mode
Maintenance mode has been enabled.

Log in using the following URL:
https://my.immich.app/maintenance?token=<token>
```

Enable OAuth login

```
immich-admin enable-oauth-login
OAuth login has been enabled.
```

Disable OAuth login

```
immich-admin disable-oauth-login
OAuth login has been disabled.
```

List Users

```
immich-admin list-users
[
  {
    id: 'e65e6f88-2a30-4dbe-8dd9-1885f4889b53',
    email: 'immich@example.com',
    name: 'Immich Admin',
    storageLabel: 'admin',
    externalPath: null,
    profileImagePath: 'upload/profile/e65e6f88-2a30-4dbe-8dd9-1885f4889b53/e65e6f88-2a30-4dbe-8dd9-1885f4889b53.jpg',
    shouldChangePassword: true,
    isAdmin: true,
    createdAt: 2023-07-11T20:12:20.602Z,
    deletedAt: null,
    updatedAt: 2023-09-21T15:42:28.129Z,
    oauthId: '',
  }
]
```

Grant Admin

```
immich-admin grant-admin
? Please enter the user email:  user@example.com
Admin access has been granted to user@example.com
```

Revoke Admin

```
immich-admin revoke-admin
? Please enter the user email:  user@example.com
Admin access has been revoked from user@example.com
```

Print Immich Version

```
immich-admin version
v1.129.0
```

Change media location

```
immich-admin change-media-location
? Enter the previous value of IMMICH_MEDIA_LOCATION: /data
? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data
...
  Previous value: /data
  Current value:  /my-data

  Changing database paths from "/data/*" to "/my-data/*"

? Do you want to proceed? [Y/n] y

Database file paths updated successfully! 🎉
...
```

Schema Check

```
immich-admin schema-check
Migrations are up to date

No schema drift detected
```

---

### Docs Administration Server Stats (`docs/docs/administration/server-stats.md`)

# Server Stats

Server statistics to show the total number of videos, photos, usage and quota per user.

:::info External library
External libraries are not included in the storage quota due to custom mount points.
:::

<img src={require('./img/server-stats.webp').default} title="server statistic" />

---

### Docs Administration Storage Template (`docs/docs/administration/storage-template.mdx`)

import StorageTemplate from '/docs/partials/_storage-template.md';

# Storage Template

<StorageTemplate />

---

### Docs Administration System Integrity (`docs/docs/administration/system-integrity.md`)

# System Integrity

## Integrity report

At a [customizable interval](https://my.immich.app/admin/system-settings?isOpen=integrity-checks), Immich runs integrity checks to ensure that your library is still intact and there are no corrupt files.
There are three kind of issues Immich checks for:

- Untracked files: the path was found in Immich's directories but it is not referenced in Immich's database
- Missing files: the path is found in the Immich internal database, but does not actually exist on disk
- Checksum mismatches: the checksum of the file stored in Immich's database does not match the actual file's checksum anymore

All three run nightly at 3am by default. For the "Checksum files" check, there are additional time and progress limits, as those are the most taxing checks. With these additional limits, Immich can slowly check checksums of your files over the course of a couple of days.

You can see the results of these checks on the [maintenance](https://my.immich.app/admin/maintenance) page. Here, you can also trigger a full scan (a _check_) for specific jobs, or all of them. In addition, you can also _refresh_ checks. This will only look at items that have currently been reported on, and check if those have been fixed.

### Common causes

Most common are untracked files. In many cases those are corrupted thumbnails or encoded videos that have been partially generated at some point and never got cleaned up properly. These are usually fine to delete, as both can always be regenerated at a later point. Other files will need to be investigated on a case-by-case basis by checking they already exist in Immich and thinking about how they might have gotten untracked.

:::info
You might want to run the _missing_ jobs for thumbnails and encoded videos (https://my.immich.app/admin/queues) to make sure all your assets have proper thumbnails and encoded videos. Watch the server logs while running the jobs, in case there are any issues with some assets.
:::

Missing files are files where Immich references them internally, but they don't actually exist on disk in that location. It could be that you deleted a file on disk from the internal library (don't do that, Immich doesn't support it). It could also be that there are issues with your file storage. Please carefully investigate missing files, and never hesitate to reach out on [our Discord](https://discord.immich.app) if you have any questions!

Checksum mismatches are often indicative of file system corruption. It could also be that you previously edited a file from the internal library on the disk, which is also not supported and will cause a checksum mismatch. Again, the recommended action is to look at any reported item individually, check it out, try to remember if you changed it or some metadata on it at some point. If you edited the file, the supported resolution is to delete the mismatched asset from Immich and reupload it as a new asset.

## Folder checks

:::info
The folders considered for these checks include: `upload/`, `library/`, `thumbs/`, `encoded-video/`, `profile/`, `backups/`
:::

When Immich starts, it performs a series of checks in order to validate that it can read and write files to the volume mounts used by the storage system. If it cannot perform all the required operations, it will fail to start. The checks include:

- Creating an initial hidden file (`.immich`) in each folder
- Reading a hidden file (`.immich`) in each folder
- Overwriting a hidden file (`.immich`) in each folder

The checks are designed to catch the following situations:

- Incorrect permissions (cannot read/write files)
- Missing volume mount (`.immich` files should exist, but are missing)

### Common issues

:::note
`.immich` files serve as markers and help keep track of volume mounts being used by Immich. Except for the situations listed below, they should never be manually created or deleted.
:::

#### Missing `.immich` files

```
Verifying system mount folder checks (enabled=true)
...
ENOENT: no such file or directory, open 'upload/encoded-video/.immich'
```

The above error messages show that the server has previously (successfully) written `.immich` files to each folder, but now does not detect them. This could be because any of the following:

- Permission error - unable to read the file, but it exists
- File does not exist - volume mount has changed and should be corrected
- File does not exist - user manually deleted it and should be manually re-created (`touch .immich`)
- File does not exist - user restored from a backup, but did not restore each folder (user should restore all folders or manually create `.immich` in any missing folders)

### Ignoring the checks

:::danger
The checks are designed to catch common problems that we have seen users have in the past, and often indicate there's something wrong that you should solve. If you know what you're doing and you want to disable them you can set the following environment variable:
:::

```
IMMICH_IGNORE_MOUNT_CHECK_ERRORS=true
```

---

### Docs Administration System Settings (`docs/docs/administration/system-settings.md`)

# System Settings

The admin user can manage settings for the Immich instance here.

:::tip
You can always return to the default settings by clicking the `Reset to default` button.
:::

## Authentication Settings

Manage password, OAuth, and other authentication settings

### OAuth Authentication

Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/administration/oauth).

### Password Authentication

The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts.

:::tip
You can always use the [Server CLI](/administration/server-commands) to re-enable password login.
:::

## Image Settings (thumbnails and previews)

- Thumbnails - Used in the main timeline.
- Previews - Used in the asset viewer.

By default Immich creates 3 thumbnails for each asset,
Blurred (thumbhash) , Small - thumbnails (webp) , and Large - previews (jpeg/webp), using these settings you can change the quality for the thumbnails and previews files that are created.

**Thumbnail format**  
Allows you to choose the type of format you want for the Thumbnail images, Webp produces smaller files than jpeg, but is slower to encode.

:::tip
You can read in detail about the advantages and disadvantages of using webp over jpeg on [Adobe's website](https://www.adobe.com/creativecloud/file-types/image/raster/webp-file.html)
:::

**Thumbnail resolution**  
Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.

**Preview format**  
Allows you to choose the type of format you want for the Preview images, Webp produces smaller files than jpeg, but is slower to encode.

**Preview resolution**  
Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.

**Quality**  
Image quality from 1-100. Higher is better for quality but produces larger files, this option affects the Preview and Thumbnail images.

**Prefer wide gamut**  
Use Display P3 for thumbnails. This better preserves the vibrance of images with wide colorspaces, but images may appear differently on old devices with an old browser version. sRGB images are kept as sRGB to avoid color shifts.

**Prefer embedded preview**  
Use embedded previews in RAW photos as the input to image processing when available. This can produce more accurate colors for some images, but the quality of the preview is camera-dependent and the image may have more compression artifacts.

:::tip
The default resolution for Large thumbnails can be lowered from 1440p (default) to 1080p or 720p to save storage space.
:::

## Job Settings

Using these settings, you can determine the amount of work that will run concurrently for each task in microservices. Some tasks can be set to higher values on computers with powerful hardware and storage with good I/O capabilities.

With higher concurrency, the host will work on more assets in parallel,
this advice improves throughput, not latency, for example, it will make Smart Search jobs process more quickly, but it won't make searching faster.

It is important to remember that jobs like Smart Search, Face Detection, Facial Recognition, and Transcode Videos require a **lot** of processing power and therefore do not exaggerate the amount of jobs because you're probably thoroughly overloading the server.

:::danger IMPORTANT
If you increase the concurrency from the defaults we set, especially for thumbnail generation, make sure you do not increase them past the amount of CPU cores you have available.
Doing so can impact API responsiveness with no gain in thumbnail generation speed.
:::

:::info Facial Recognition Concurrency
The Facial Recognition Concurrency value cannot be changed because
[DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok) is traditionally sequential, but there are parallel implementations of it out there. Our implementation isn't parallel.
:::

## External Library

### Library watching (EXPERIMENTAL)

External libraries can automatically import changed files without a full rescan. It will import the file whenever the operating system reports a file change. If your photos are mounted over the network, this does not work.

### Periodic Scanning

You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library.  
You can set the scanning interval using the preset or cron format. For more information please refer to e.g. [Crontab Guru](https://crontab.guru/).

## Logging

The default Immich log level is `Log` (commonly known as `Info`). The Immich administrator can choose a higher or lower log level according to personal preference or as requested by the Immich support team.

## Machine Learning Settings

Through this setting, you can manage all the settings related to machine learning in Immich, from the setting of remote machine learning to the model and its parameters
You can choose to disable a certain type of machine learning, for example smart search or facial recognition.

### URL

The built in (`http://immich-machine-learning:3003`) machine learning server will be configured by default, but you can change this or add additional servers.

Hosting the `immich-machine-learning` container on a machine with a more powerful GPU can be helpful to for processing a large number of photos (such as during batch import) or for faster search.

If more than one URL is provided, each server will be attempted one-at-a-time until one responds successfully, in order from first to last. Servers that don't respond will be temporarily ignored until they come back online.

### Smart Search

The [smart search](/features/searching) settings allow you to change the [CLIP model](https://openai.com/research/clip). Larger models will typically provide [more accurate search results](https://github.com/immich-app/immich/discussions/11862) but consume more processing power and RAM. When [changing the CLIP model](/FAQ#can-i-use-a-custom-clip-model) it is mandatory to re-run the Smart Search job on all images to fully apply the change.

:::info Internet connection
Changing models requires a connection to the Internet to download the model.
After downloading, there is no need for Immich to connect to the network
Unless version checking has been enabled in the settings.
:::

### Duplicate Detection

Use CLIP embeddings to find likely duplicates. The maximum detection distance can be configured in order to improve / reduce the level of accuracy.

- **Maximum detection distance -** Maximum distance between two images to consider them duplicates, ranging from 0.001-0.1. Higher values will detect more duplicates, but may result in false positives.

### Facial Recognition

Under these settings, you can change the facial recognition settings
Editable settings:

- **Facial Recognition Model**
- **Min Detection Score**
- **Max Recognition Distance**
- **Min Recognized Faces**

You can learn more about these options on the [Facial Recognition page](/features/facial-recognition#how-face-detection-works)

:::info
When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces.
You will have to restart **only** the job FACIAL RECOGNITION - ALL.

If you replace the Facial Recognition Model, you will have to run the job FACE DETECTION - ALL.
:::

:::tip identical twins
If you have twins, you might want to lower the Max Recognition Distance value, decreasing this a **bit** can make it distinguish between them.
:::

## Map & GPS Settings

### Map Settings

In these settings, you can change the appearance of the map in night and day modes according to your personal preference and according to the supported options.
The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for example.

### Reverse Geocoding Settings

Immich supports [Reverse Geocoding](/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database.

## Notification Settings

SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/administration/email-notification)

## Notification Templates

Override the default notifications text with notification templates. More information can be found [here](/administration/email-notification)

## Server Settings

### External Domain

Overrides the domain name in shared links and email notifications. The URL should not include a trailing slash.

### Welcome Message

The administrator can set a custom message on the login screen (the message will be displayed to all users).

## Storage Template

Immich supports a custom [Storage Template](/administration/storage-template). Learn more about this feature and its configuration [here](/administration/storage-template).

## Theme Settings

You can write custom CSS that will get loaded in the web application for all users. This enables administrators to change fonts, colors, and other styles.

For example:

```css title='Custom CSS'
p {
  color: green;
}
```

## Trash Settings

In the system administrator's option to set a trash for deleted files, these files will remain in the trash until the deletion date 30 days (default) or as defined by the system administrator.

The trash can be disabled, however this is not recommended as future files that are deleted will be permanently deleted.

:::tip Keyboard shortcut for permanently deletion
You can select assets and press Ctrl + Del from the timeline for quick permanent deletion without the trash option.
:::

## User Settings

### Delete delay

The system administrator can choose to delete users through the administration panel, the system administrator can delete users immediately or alternatively delay the deletion for users (7 days by default) this action permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.

## Version Check

When this option is enabled the `immich-server` will periodically make requests to GitHub to check for new releases.

## Video Transcoding Settings

The system administrator can configure which video files will be converted to different formats. The settings can be changed in depth, to learn more about the terminology used here, refer to FFmpeg documentation for [H.264](https://trac.ffmpeg.org/wiki/Encode/H.264) codec, [HEVC](https://trac.ffmpeg.org/wiki/Encode/H.265) codec and [VP9](https://trac.ffmpeg.org/wiki/Encode/VP9) codec.

Which streams of a video file will be transcoded is determined by the [Transcode Policy](#ffmpeg.transcode). Streams that are transcoded use the following settings (config file name in brackets). Streams that are not transcoded are untouched and preserve their original settings.

### Accepted containers (`ffmpeg.acceptedContainers`) {#ffmpeg.acceptedContainers}

If the video asset's container format is not in this list, it will be remuxed to MP4 even if no streams need to be transcoded.

The default set of accepted container formats is `mov`, `ogg` and `webm`.

### Preset (`ffmpeg.preset`) {#ffmpeg.preset}

The amount of "compute effort" to put into transcoding. These use [the preset names from h264](https://trac.ffmpeg.org/wiki/Encode/H.264#Preset) and will be converted to appropriate values for encoders that configure effort in different ways.

The default value is `ultrafast`.

### Audio codec (`ffmpeg.targetAudioCodec`) {#ffmpeg.targetAudioCodec}

Which audio codec to use when the audio stream is being transcoded. Can be one of `mp3`, `aac`, `opus`.

The default value is `aac`.

### Video Codec (`ffmpeg.targetVideoCodec`) {#ffmpeg.targetVideoCodec}

Which video codec to use when the video stream is being transcoded. Can be one of `h264`, `hevc`, `vp9` or `av1`.

The default value is `h264`.

### Target resolution (`ffmpeg.targetResolution`) {#ffmpeg.targetResolution}

When transcoding a video stream, downscale the largest dimension to this value while preserving aspect ratio. Videos are never upscaled.

The default value is `720`.

### Transcode policy (`ffmpeg.transcode`) {#ffmpeg.transcode}

The transcoding policy configures which streams of a video asset will be transcoded. The transcoding decision is made independently for video streams and audio streams. This means that if a video stream needs to be transcoded, but an audio stream does not, then the video stream will be transcoded while the audio stream will be copied. If the transcoding policy does not require any stream to be transcoded and does not require the video to be remuxed, then no separate video file will be created.

The default policy is `required`.

#### All videos (`all`) {#ffmpeg.transcode-all}

Videos are always transcoded. This ensures consistency during video playback.

#### Don't transcode any videos (`disabled`) {#ffmpeg.transcode-disabled}

Videos are never transcoded. This saves space and resources on the server, but may prevent playback on devices that don't support the source format (especially web browsers) or result in high bandwidth usage when playing high-bitrate files.

#### Only videos not in an accepted format (`required`) {#ffmpeg.transcode-required}

Video streams are transcoded when any of the following conditions are met:

- The video is HDR.
- The video is not in the yuv420p pixel format.
- The video codec is not in `acceptedVideoCodecs`.

Audio is transcoded if the audio codec is not in `acceptedAudioCodecs`.

#### Videos higher than max bitrate or not in an accepted format (`bitrate`) {#ffmpeg.transcode-bitrate}

In addition to the conditions in `required`, video streams are also transcoded if their bitrate is over `maxBitrate`.

#### Videos higher than target resolution or not in an accepted format (`optimal`) {#ffmpeg.transcode-optimal}

In addition to the conditions in `required`, video streams are also transcoded if the horizontal **and** vertical dimensions are higher than [`targetResolution`](#ffmpeg.targetResolution).

---

### Docs Administration User Management (`docs/docs/administration/user-management.mdx`)

import RegisterAdminUser from '/docs/partials/_register-admin.md';
import UserCreate from '/docs/partials/_user-create.md';

# User Management

Immich supports multiple users, each with their own library.

## Register the Admin User

<RegisterAdminUser />

## Create a New User

<UserCreate />

## Send new user email notification

:::note
This option is only available if an SMTP server has been configured in the administrator settings.
:::

Admin can send a welcome email if the Email option is set, you can learn here how to set up the SMTP server in Immich.

<img
  src={require('./img/send-user-email-notification.webp').default}
  width="40%"
  title="Send user email notification"
/>

## Set Storage Quota For User

Admin can specify the storage quota for the user as the instance's admin; once the limit is reached, the user won't be able to upload to the instance anymore.

In order to select a storage quota, click on the edit user icon and enter the storage quota in GiB. You can choose an unlimited quota by leaving it empty (default).

:::tip
The system administrator can see the usage quota percentage of all users in Server Stats page.
:::

:::info
External libraries don't take up space from the storage quota.
:::

<img src={require('./img/user-quota-size.webp').default} width="80%" title="Set Quota Size" />

## Set Storage Label For User

The admin can add a custom label for each user, so instead of `upload/{userId}/your-template` it will be `upload/{custom_user_label}/your-template`.  
To apply a storage template, go to the `Administration > Users`, then click on the context menu button next to the user.
:::note
To apply the Storage Label to previously uploaded assets, run the Storage Migration Job.
:::

<img src={require('./img/user-storage-label.webp').default} width="40%" title="Delete User" />

## Password Reset

<img src={require('./img/user-edit-menu.webp').default} width="80%" title="Customize Delete User" />

To reset a user's password, go to `Administration > Users`, then click on the context menu button next to the user, then click "Reset Password". The user's password will be reset to a random password and they have to change it next time they sign in.

## Delete a User

If you need to remove a user from Immich, go to `Administration > Users`, then click on the context menu button next to the user. The user account will immediately become disabled and their library and all associated data will be removed after 7 days by default.

### Delete Delay

You can customize the time of the deletion of the users from `Administration -> Settings -> User Settings`.
:::info user deletion job
The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.
:::

### Immediately Remove User

You can choose to delete a user immediately by checking the box
`Queue user and assets for immediate deletion` in the deletion process, this will immediately remove the user and all assets.
This cannot be undone and the files cannot be recovered.

<img src={require('./img/immediately-remove-user.webp').default} width="40%" title="Customize Delete User" />

---

### Docs Developer Architecture (`docs/docs/developer/architecture.mdx`)

---
sidebar_position: 1
---

import AppArchitecture from './img/app-architecture.webp';
import MobileArchitecture from './img/immich_mobile_architecture.svg';

# Architecture

Immich uses a traditional client-server design, with a dedicated database for data persistence. The frontend clients communicate with backend services over HTTP using REST APIs. Below is a high level diagram of the architecture.

## High Level Diagram

<img alt="Immich Architecture" src={AppArchitecture} className="p-4 dark:bg-immich-dark-primary my-4" />

The diagram shows clients communicating with the server's API via REST. The server communicates with downstream systems (i.e. Redis, Postgres, Machine Learning, file system) through repository interfaces. Not shown in the diagram, is that the server is split into two separate containers `immich-server` and `immich-microservices`. The microservices container does not handle API requests or schedule cron jobs, but primarily handles incoming job requests from Redis.

## Clients

Immich has three main clients:

1. Mobile app - Android, iOS
2. Web app - Responsive website
3. CLI - Command-line utility for bulk upload

:::info
All three clients use [OpenAPI](/api.md) to auto-generate rest clients for easy integration. For more information about this process, see [OpenAPI](/api.md).
:::

### Mobile App

The mobile app is written in [Dart](https://dart.dev/) using [Flutter](https://flutter.dev/). Below is an architecture overview:

<MobileArchitecture className="p-4 dark:bg-immich-dark-primary my-4" />

The diagrams shows the target architecture, the current state of the code-base is not always following the architecture yet. New code and contributions should follow this architecture.
Currently, it uses [Isar Database](https://isar.dev/) for a local database and [Riverpod](https://riverpod.dev/) for state management (providers).
Entities and Models are the two types of data classes used. While entities are stored in the on-device database, models are ephemeral and only kept in memory.
The Repositories should be the only place where other data classes are used internally (such as OpenAPI DTOs). However, their interfaces must not use foreign data classes!

### Web Client

The web app is a [TypeScript](https://www.typescriptlang.org/) project that uses [SvelteKit](https://kit.svelte.dev) and [Tailwindcss](https://tailwindcss.com/).

### CLI

The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users control their Immich instance from the command line. It uses the API to perform various tasks, especially uploading assets. See the [CLI documentation](/features/command-line-interface.md) for more information.

## Server

The Immich backend is divided into several services, which are run as individual docker containers.

1. `immich-server` - Handle and respond to REST API requests, execute background jobs (thumbnail generation, metadata extraction, transcoding, etc.)
1. `immich-machine-learning` - Execute machine learning models
1. `postgres` - Persistent data storage
1. `redis`- Queue management for background jobs

### Immich Server

The Immich Server is a [TypeScript](https://www.typescriptlang.org/) project written for [Node.js](https://nodejs.org/). It uses the [Nest.js](https://nestjs.com) framework, [Express](https://expressjs.com/) server, and the query builder [Kysely](https://kysely.dev/). The server codebase also loosely follows the [Hexagonal Architecture](<https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)>). Specifically, we aim to separate technology specific implementations (`src/repositories`) from core business logic (`src/services`).

### API Endpoints

An incoming HTTP request is mapped to a controller (`src/controllers`). Controllers are collections of HTTP endpoints. Each controller usually implements the following CRUD operations for its respective resource type:

- `POST` `/<type>` - **Create**
- `GET` `/<type>` - **Read** (all)
- `GET` `/<type>/:id` - **Read** (by id)
- `PUT` `/<type>/:id` - **Updated** (by id)
- `DELETE` `/<type>/:id` - **Delete** (by id)

### Domain Transfer Objects (DTOs)

The server uses [Domain Transfer Objects](https://en.wikipedia.org/wiki/Data_transfer_object) as public interfaces for the inputs (query, params, and body) and outputs (response) for each endpoint. DTOs translate to [OpenAPI](/api.md) schemas and control the generated code used by each client.

### Background Jobs

Immich uses a [worker](https://github.com/immich-app/immich/blob/main/server/src/utils/misc.ts#L266) to run background jobs. These jobs include:

- Thumbnail Generation
- Metadata Extraction
- Video Transcoding
- Smart Search
- Facial Recognition
- Storage Template Migration
- Sidecar (see [XMP Sidecars](/features/xmp-sidecars.md))
- Background jobs (file deletion, user deletion)

:::info
This list closely matches what is available on the [Administration > Jobs](/administration/jobs-workers/#jobs) page, which provides some remote queue management capabilities.
:::

### Machine Learning

The machine learning service is written in [Python](https://www.python.org/) and uses [FastAPI](https://fastapi.tiangolo.com/) for HTTP communication.

All machine learning related operations have been externalized to this service, `immich-machine-learning`. Python is a natural choice for AI and machine learning. It also has some pretty specific hardware requirements. Running it as a separate container makes it possible to run the container on a separate machine, or easily disable it entirely.

Each request to the machine learning service contains the relevant metadata for the model task, model name, and so on. These settings are stored in Postgres along with other system configs. For each request, the microservices container fetches these settings in order to attach them to the request.

Internally, the machine learning service downloads, loads and configures the specified model for a given request before processing the text or image payload with it. Models that have been loaded are cached and reused across requests. A thread pool is used to process each request in a different thread so as not to block the async event loop.

All models are in ONNX format. This format has wide industry support, meaning that most other model formats can be exported to it and many hardware APIs support it. It's also quite fast.

Machine learning models are also quite _large_, requiring _quite a bit_ of memory. We are always looking for ways to improve and optimize this aspect of this container specifically.

### Postgres

Immich persists data in Postgres, which includes information about access and authorization, users, albums, asset, sharing settings, etc.

:::info
See [Database Migrations](./database-migrations.md) for more information about how to modify the database to create an index, modify a table, add a new column, etc.
:::

### Redis

Immich uses [Redis](https://redis.com/) via [BullMQ](https://docs.bullmq.io/) to manage job queues. Some jobs trigger subsequent jobs. For example, Smart Search and Facial Recognition relies on thumbnail generation and automatically run after one is generated.

---

### Docs Developer Database Migrations (`docs/docs/developer/database-migrations.md`)

# Database Migrations

After making any changes in the `server/src/schema`, a database migration need to run in order to register the changes in the database. Follow the steps below to create a new migration.

1. Run the command

```bash
mise //server:migrations generate <migration-name>
```

2. Check if the migration file makes sense.
3. Move the migration file to folder `./server/src/schema/migrations` in your code editor.
4. Run the command

```bash
mise //server:migrations sync-order
```

The last step adds the migration to the `ORDER` manifest, which records the order migrations run in. It is committed so that two branches adding a migration conflict in git instead of silently merging out of order, which would stop the server from starting for anyone who ran them in the wrong order.

The server will automatically detect `*.ts` file changes and restart. Part of the server start-up process includes running any new migrations, so it will be applied immediately.

## Reverting a Migration

If you need to undo the most recently applied migration—for example, when developing or testing on schema changes—run:

```bash
mise //server:migrations revert
```

This command rolls back the latest migration and brings the database schema back to its previous state.

---

### Docs Developer Devcontainers (`docs/docs/developer/devcontainers.md`)

---
title: Devcontainers
sidebar_position: 3
---

# Development with Dev Containers

Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.

Get started fast!

[](https://codespaces.new/immich-app/immich/)

[Learn more about Dev Containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers)

## Prerequisites

Before getting started, ensure you have:

- **Docker Desktop** (latest version)
  - [Mac](https://docs.docker.com/desktop/install/mac-install/)
  - [Windows](https://docs.docker.com/desktop/install/windows-install/) (with WSL2 backend recommended)
  - [Linux](https://docs.docker.com/desktop/install/linux-install/)
- **Visual Studio Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
- **Git** for cloning the repository
- At least **8GB of RAM** (16GB recommended)
- **20GB of free disk space**

:::tip Alternative Development Environments
While this guide focuses on VS Code, you have many options for Dev Container development:

**Local Editors:**

- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) - Full JetBrains IDE support
- [neovim](https://github.com/jamestthompson3/nvim-remote-containers) - Lightweight terminal-based editor
- [Emacs](https://github.com/emacs-lsp/lsp-docker) - Extensible text editor
- [DevContainer CLI](https://github.com/devcontainers/cli) - Command-line interface

**Cloud-Based Solutions:**

- [GitHub Codespaces](https://github.com/features/codespaces) - Fully integrated with GitHub, excellent devcontainer.json support
- [GitPod](https://www.gitpod.io) - SaaS platform with recent Dev Container support (historically used gitpod.yml)

**Self-Hostable Options:**

- [Coder](https://coder.com) - Enterprise-focused, requires Terraform knowledge, self-managed
- [DevPod](https://devpod.sh) - Client-only tool with excellent devcontainer.json support, works with any provider (local, cloud, or on-premise). Check [quick-start guide](#quick-start-guide-for-devpod-with-docker)
  :::

## Dev Container Services

The Dev Container environment consists of the following services:

| Service          | Container Name            | Description                                               | Ports                                                                   |
| ---------------- | ------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- |
| Server & Web     | `immich-server`           | Runs both API server and web frontend in development mode | 2283 (API)<br/>3000 (Web)<br/>9230 (Workers Debug)<br/>9231 (API Debug) |
| Database         | `database`                | PostgreSQL database                                       | 5432                                                                    |
| Cache            | `redis`                   | Valkey cache server                                       | 6379                                                                    |
| Machine Learning | `immich-machine-learning` | Immich ML model inference server                          | 3003                                                                    |

## Getting Started

### Step 1: Clone the Repository

```bash
git clone https://github.com/immich-app/immich.git
cd immich
```

### Step 2: Configure Environment Variables

The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.

:::important Configuration
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.

```bash
# Set temporarily for current session
export UPLOAD_LOCATION=/opt/dev_upload_folder

# Or add to your shell profile for persistence
# (~/.bashrc, ~/.zshrc, ~/.bash_profile, etc.)
echo 'export UPLOAD_LOCATION=/opt/dev_upload_folder' >> ~/.bashrc
source ~/.bashrc
```

:::

### Step 3: Launch the Dev Container

:::tip
Immich development makes extensive use of specialized [base images](https://github.com/immich-app/base-images) for its docker-compose based development. For this reason, you won't be able to use VSCode's **_Clone Repository in a Container Volume_** command.
:::

#### Using VS Code UI:

1. Open the cloned repository in VS Code
2. Press `F1` or `Ctrl/Cmd+Shift+P` to open the command palette
3. Type and select "Dev Containers: Rebuild and Reopen in Container"
4. Select "Immich - Backend, Frontend and ML" from the list
5. Wait for the container to build and start (this may take several minutes on first run)

#### Using VS Code Quick Actions:

1. Open the repository in VS Code
2. You should see a popup asking if you want to reopen in a container
3. Click "Reopen in Container"

#### Using Command Line:

```bash
# Using the DevContainer CLI
devcontainer up --workspace-folder .
```

## Environment Variable Details

### How Dev Containers Handle Environment Variables

Unlike the Immich developer setup based on Docker Compose which uses `.env` files, Immich Dev Containers read environment variables from your shell environment. This is configured in `.devcontainer/devcontainer.json`:

```json
"remoteEnv": {
    "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}",
    "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}",
    "DB_USERNAME": "${localEnv:DB_USERNAME:postgres}",
    "DB_DATABASE_NAME": "${localEnv:DB_DATABASE_NAME:immich}"
}
```

The `${localEnv:VARIABLE:default}` syntax reads from your shell environment with optional defaults.

### Upload Location Path Resolution

The `UPLOAD_LOCATION` environment variable controls where files are stored:

**Default:** `./Library` (relative to the `docker` directory)
**Resolved to:** `<immich-root>/docker/Library`

**Bind Mounts Created:**

```yaml
# From .devcontainer/server/container-compose-overrides.yml
- ${UPLOAD_LOCATION-./Library}/photos:/workspaces/immich/server/upload
- ${UPLOAD_LOCATION-./Library}/postgres:/var/lib/postgresql/data
```

### Database Configuration

These variables have sensible defaults (for development) but can be customized:

| Variable           | Default    | Description         |
| ------------------ | ---------- | ------------------- |
| `DB_PASSWORD`      | `postgres` | PostgreSQL password |
| `DB_USERNAME`      | `postgres` | PostgreSQL username |
| `DB_DATABASE_NAME` | `immich`   | Database name       |

### Setting Environment Variables

Add these to your shell profile (`~/.bashrc`, `~/.zshrc`, `~/.bash_profile`, etc.):

```bash
# Required
export UPLOAD_LOCATION=./Library  # or absolute path

# Optional (only if using non-default values)
export DB_PASSWORD=your_password
export DB_USERNAME=your_username
export DB_DATABASE_NAME=your_database
```

Remember to reload your shell configuration:

```bash
source ~/.bashrc  # or ~/.zshrc, etc.
```

## Git Configuration

### SSH Keys and Authentication

To use your SSH keys for GitHub access inside the Dev Container:

1. **Start SSH Agent** on your host machine:

   ```bash
   eval "$(ssh-agent -s)"
   ssh-add ~/.ssh/id_rsa  # or your key path
   ```

2. **VS Code automatically forwards your SSH agent** to the container

For detailed instructions, see the [VS Code guide on sharing Git credentials](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials).

### Commit Signing

To use your SSH key for commit signing, see the [GitHub guide on SSH commit signing](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key).

## Development Workflow

### Automatic Setup

When the Dev Container starts, it automatically:

1. **Runs post-create script** (`container-server-post-create.sh`):
   - Adjusts file permissions for the `node` user
   - Installs dependencies: `pnpm install` in all packages
   - Builds TypeScript SDK: `pnpm --filter @immich/sdk build`

2. **Starts development servers** via VS Code tasks:
   - `Immich API Server (Nest)` - API server with hot-reloading on port 2283
   - `Immich Web Server (Vite)` - Web frontend with hot-reloading on port 3000
   - Both servers watch for file changes and recompile automatically

3. **Configures port forwarding**:
   - Web UI: http://localhost:3000 (opens automatically)
   - API: http://localhost:2283
   - Debug ports: 9230 (workers), 9231 (API)

:::info
The Dev Container setup replaces the `mise dev` command from the traditional setup. All services start automatically when you open the container.
:::

### Accessing Services

Once running, you can access:

| Service  | URL                   | Description                                                                                    |
| -------- | --------------------- | ---------------------------------------------------------------------------------------------- |
| Web UI   | http://localhost:3000 | Main web interface                                                                             |
| API      | http://localhost:2283 | REST API endpoints (Not used directly, web UI will expose this over http://localhost:3000/api) |
| Database | localhost:5432        | PostgreSQL (username: `postgres`) (Not used directly)                                          |

### Connecting Mobile Apps

To connect the mobile app to your Dev Container:

1. Find your machine's IP address
2. In the mobile app, use: `http://YOUR_IP:3000/api`
3. Ensure your firewall allows connections on port 2283

### Making Code Changes

- **Server code** (`/server`): Changes trigger automatic restart
- **Web code** (`/web`): Changes trigger hot module replacement
- **Database migrations**: Run `mise //:sql`
- **API changes**: Regenerate TypeScript SDK with `mise //:open-api`

## Testing

### Running Tests

The Dev Container supports multiple ways to run tests:

```bash
# Server
mise //server:test          # unit tests
mise //server:test-medium   # medium / integration tests

# Web
mise //web:test             # unit tests

# E2E
mise //e2e:test             # API tests
mise //e2e:test-web         # web UI tests (Playwright)

# Run all checks for a component
mise //server:checklist
mise //web:checklist
```

### Additional Commands

```bash
# API generation
mise //:open-api             # Generate OpenAPI specs
mise //:open-api-typescript  # Generate TypeScript SDK
mise //:open-api-dart        # Generate Dart SDK

# Database
mise //server:sql            # Sync database schema
```

### Debugging

The Dev Container is pre-configured for debugging:

1. **API Server Debugging**:
   - Set breakpoints in VS Code
   - Press `F5` or use "Run and Debug" panel
   - Select "Attach to Server" configuration
   - Debug port: 9231

2. **Worker Debugging**:
   - Use "Attach to Workers" configuration
   - Debug port: 9230

3. **Web Debugging**:
   - Use browser DevTools
   - VS Code debugger for Chrome/Edge extensions supported

## Troubleshooting

### Common Issues

#### Permission Errors

**Problem**: `EACCES` or permission denied errors  
**Solution**:

- The Dev Container runs as the `node` user (UID 1000)
- If your host UID differs, you may see permission issues
- Try rebuilding the container: "Dev Containers: Rebuild Container"

#### Container Won't Start

**Problem**: Dev Container fails to start or build  
**Solution**:

1. Check Docker is running: `docker ps`
2. Clean Docker resources: `docker system prune -a`
3. Check available disk space
4. Review Docker Desktop resource limits

#### Port Already in Use

**Problem**: "Port 3000/2283 is already in use"  
**Solution**:

1. Check for conflicting services: `lsof -i :3000` (macOS/Linux)
2. Stop conflicting services or change port mappings
3. Restart Docker Desktop

#### Upload Location Not Set

**Problem**: Errors about missing UPLOAD_LOCATION  
**Solution**:

1. Set the environment variable: `export UPLOAD_LOCATION=./Library`
2. Add to your shell profile for persistence
3. Restart your terminal and VS Code

#### Database Connection Failed

**Problem**: Cannot connect to PostgreSQL  
**Solution**:

1. Ensure all containers are running: `docker ps`
2. Check logs: "Dev Containers: Show Container Log"
3. Verify database credentials match environment variables

### Getting Help

If you encounter issues:

1. Check container logs: View → Output → Select "Dev Containers"
2. Rebuild without cache: "Dev Containers: Rebuild Container Without Cache"
3. Review [common Docker issues](https://docs.docker.com/desktop/troubleshoot/)
4. Ask in [Discord](https://discord.immich.app) `#contributing` channel

### Quick-start guide for DevPod with docker

You will need DevPod CLI (check [DevPod CLI installation guide](https://devpod.sh/docs/getting-started/install)) and Docker Desktop.

```sh
# Step 1: Clone the Repository
git clone https://github.com/immich-app/immich.git
cd immich

# Step 2: Prepare DevPod (if you haven't already)
devpod provider add docker
devpod provider use docker

# Step 3: Build 'immich-server-dev' docker image first manually
docker build -f server/Dockerfile.dev -t immich-server-dev .

# Step 4: Now you can start devcontainer
devpod up .
```

## Mobile Development

While the Dev Container focuses on server and web development, you can connect mobile apps for testing:

### Connecting iOS/Android Apps

1. **Ensure API is accessible**:

   ```bash
   # Find your machine's IP
   # macOS
   ipconfig getifaddr en0
   # Linux
   hostname -I
   # Windows (in WSL2)
   ip addr show eth0
   ```

2. **Configure mobile app**:
   - Server URL: `http://YOUR_IP:2283/api`
   - Ensure firewall allows port 2283

3. **For full mobile development**, see the [mobile development guide](/developer/setup) which covers:
   - Flutter setup
   - Running on simulators/devices
   - Mobile-specific debugging

## Advanced Configuration

### Custom VS Code Extensions

Add extensions to `.devcontainer/devcontainer.json`:

```json
"customizations": {
  "vscode": {
    "extensions": [
      "your.extension-id"
    ]
  }
}
```

### Additional Services

To add services (e.g., Redis Commander), modify:

1. `/docker/docker-compose.dev.yml` - Add service definition
2. `/.devcontainer/server/container-compose-overrides.yml` - Add overrides if needed

### Resource Limits

Adjust Docker Desktop resources:

- **macOS/Windows**: Docker Desktop → Settings → Resources
- **Linux**: Modify Docker daemon configuration

Recommended minimums:

- CPU: 4 cores
- Memory: 8GB
- Disk: 20GB

## Next Steps

- Read the [architecture overview](/developer/architecture)
- Learn about [database migrations](/developer/database-migrations)
- Explore [API documentation](https://api.immich.app/)
- Join `#immich` on [Discord](https://discord.immich.app)

---

### Docs Developer Directories (`docs/docs/developer/directories.md`)

---
title: Directories
---

# Repository Folder Structure

Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](https://en.wikipedia.org/wiki/Monorepo) and includes the following folders:

| Folder              | Description                                                          |
| :------------------ | :------------------------------------------------------------------- |
| `.github/`          | Github templates and action workflows                                |
| `.vscode/`          | VSCode debug launch profiles                                         |
| `packages/cli`      | Source code for the CLI                                              |
| `packages/sdk`      | Source code for the generated OpenAPI SDK                            |
| `packages/scripts`  | Scripts for version bumps and draft releases                         |
| `docker/`           | Docker compose resources for dev, test, production                   |
| `design/`           | Screenshots and logos for the README                                 |
| `docs/`             | Source code for the [https://immich.app](https://immich.app) website |
| `machine-learning/` | Source code for the `immich-machine-learning` docker image           |
| `mobile/`           | Source code for the mobile app, both Android and iOS                 |
| `server/`           | Source code for the `immich-server` docker image                     |
| `web/`              | Source code for the `web`                                            |

---

### Docs Developer Pr Checklist (`docs/docs/developer/pr-checklist.md`)

# PR Checklist

A minimal devcontainer is supplied with this repository. All commands can be executed directly inside this container to avoid tedious installation of the environment.
:::warning
The provided devcontainer isn't complete at the moment. At least all dockerized steps in the Makefile won't work (`mise dev`, ....). Feel free to contribute!
:::
When contributing code through a pull request, please check the following:

## Web Checks

- [ ] `mise //web:lint` (linting via ESLint)
- [ ] `mise //web:format` (formatting via Prettier)
- [ ] `mise //web:check-svelte` (type checking via SvelteKit)
- [ ] `mise //web:check-typescript` (type checking via `tsc`)
- [ ] `mise //web:test` (unit tests)

:::tip AIO
Run all web checks with `mise //web:checklist`
:::

:::tip Auto Fix
Use `mise //web:lint-fix` and `mise //web:format-fix` to automatically correct some issues.
:::

## Documentation

- [ ] `mise //docs:format` (formatting via Prettier)
- [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation.

:::tip Auto Fix
Use `mise //docs:format-fix` to automatically fix formatting.
:::

## Server Checks

- [ ] `mise //server:lint` (linting via ESLint)
- [ ] `mise //server:format` (formatting via Prettier)
- [ ] `mise //server:check` (type checking via `tsc`)
- [ ] `mise //server:test` (unit tests)

:::tip AIO
Run all server checks with `mise //server:checklist`
:::

:::tip Auto Fix
Use `mise //server:lint-fix` and `mise //server:format-fix` to automatically correct some issues.
:::

## Mobile Checklist

- [ ] `mise //mobile:codegen` (auto-generate files using build_runner)
- [ ] `mise //mobile:lint` (static analysis via Dart Analyzer and DCM)
- [ ] `mise //mobile:format` (formatting via Dart Formatter)
- [ ] `mise //mobile:test` (unit tests)

:::tip
Run all these commands at once with `mise //mobile:checklist`
:::

:::tip Auto Fix
You can use `mise //mobile:lint-fix` to potentially correct some issues automatically for `mise //mobile:lint`.
:::

## Machine Learning Checklist

- [ ] `mise //machine-learning:lint` (linting via ruff)
- [ ] `mise //machine-learning:format` (formatting via ruff)
- [ ] `mise //machine-learning:check` (type checking via mypy)
- [ ] `mise //machine-learning:test` (unit tests via pytest)

:::tip AIO
Run all machine learning checks with `mise //machine-learning:checklist`
:::

## OpenAPI

The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/api.md) for more details.

## Database Migrations

A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/developer/database-migrations.md) for more details.

---

### Docs Developer Setup (`docs/docs/developer/setup.md`)

---
sidebar_position: 2
---

# Setup

:::warning
Make sure to read the [`CONTRIBUTING.md`](https://github.com/immich-app/immich/blob/main/CONTRIBUTING.md) before you dive into the code.
:::

:::note
If there's a feature you're planning to work on, just give us a heads up in [#contributing](https://discord.com/channels/979116623879368755/1071165397228855327) on [our Discord](https://discord.immich.app) so we can:

1. Let you know if it's something we would accept into Immich
2. Provide any guidance on how something like that would ideally be implemented
3. Ensure nobody is already working on that issue/feature so we don't duplicate effort

Thanks for being interested in contributing 😊
:::

## Environment

### Services

This environment includes the services below. Additional details are available in each service's README.

- Server - [`/server`](https://github.com/immich-app/immich/tree/main/server)
- Web app - [`/web`](https://github.com/immich-app/immich/tree/main/web)
- Machine learning - [`/machine-learning`](https://github.com/immich-app/immich/tree/main/machine-learning)
- Redis
- PostgreSQL development database with exposed port `5432` so you can use any database client to access it

All the services are packaged to run with a single Docker Compose command.

:::tip mise
[mise](https://mise.jdx.dev) is used throughout the project to manage tool versions and run tasks. [Install mise](https://mise.jdx.dev/installing-mise.html), then from the repo root run `mise trust` and `mise install` to get all required tools. Tasks for each service can be run from the repo root using `mise //namespace:task` (e.g. `mise //server:lint`). To list all available tasks, run `mise tasks ls --all`.
:::

### Server and web apps

1. Clone the project repo.
2. Run `cp docker/example.env docker/.env`.
3. Edit `docker/.env` to provide values for the required variable `UPLOAD_LOCATION`.
4. Install dependencies - `mise x -- pnpm i`
5. From the root directory, run:

```bash title="Start development server"
mise dev
```

5. Access the dev instance in your browser at http://localhost:3000, or connect via the mobile app.

All the services will be started with hot-reloading enabled for a quick feedback loop.

You can access the web from `http://your-machine-ip:3000` or `http://localhost:3000` and access the server from the mobile app at `http://your-machine-ip:3000`

**Notes:**

- The "web" development container runs with uid 1000. If that uid does not have read/write permissions on the mounted volumes, you may encounter errors

#### Connect web to a remote backend

If you only want to do web development connected to an existing, remote backend, run from the repo root:

```bash
IMMICH_SERVER_URL=https://demo.immich.app/ mise //web:start
```

This will install all dependencies (including the SDK) and start the dev server in one step. To connect to the hosted demo server specifically, use the shorthand:

```bash
mise //web:start-demo
```

If you're using PowerShell on Windows you may need to set the env var separately like so:

```powershell
$env:IMMICH_SERVER_URL = "https://demo.immich.app/"
mise //web:start
```

#### `@immich/ui`

To see local changes to `@immich/ui` in Immich, do the following:

1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
2. Build the `@immich/ui` project via `pnpm run build`
3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yml` file (`../../ui:/usr/src/ui`)
4. Uncomment the corresponding alias in the `web/vite.config.ts` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui/packages/ui')`)
5. Uncomment the import statement in `web/src/app.css` file `@import '../../../ui/packages/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';`
6. Start up the stack via `mise dev`
7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`)

### Mobile app

#### Setup

1. Run `mise //mobile:install` to install Flutter dependencies.
2. Run `mise //mobile:translation` to generate the translation file.
3. Run `mise //mobile:checkout` to update the dependencies and codegen artifacts.
4. Change to the `mobile/` directory and run `flutter run` to start the app.

:::important Workflow
Always run `mise //mobile:checkout` after switching branches.
:::

The app includes a small Rust core (`native/`) that is compiled from source during the build. With mise the Rust toolchain is installed for you; with your own Flutter install, install [rustup](https://rustup.rs) once and the build fetches the pinned toolchain itself. The first build takes a little longer because of this.

##### iOS Code Signing

The Immich Apple Team ID and bundle IDs are specified in `mobile/ios/Signing.xcconfig`. For local development, we provide an override mechanism.

Create `mobile/ios/Signing.local.xcconfig` and populate it with the necessary values needed to build and sign Immich yourself. This local override file is gitignored.

```
IMMICH_TEAM_ID = ABCDE12345
IMMICH_BUNDLE_ID_PROD = com.customuniqueid.immich
IMMICH_BUNDLE_ID_DEV = com.customuniqueid.immichdev
IMMICH_GROUP_ID = group.com.customuniqueid.immich
```

The environment values are used across Immich's targets and schemes to prevent redundant edits by contributors.

#### Translation

To add a new translation text, enter the key-value pair in the `i18n/en.json` in the root of the immich project. Then run:

```bash
mise //mobile:translation
```

The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above.

#### UI components and widget previews

Shared design-system widgets (buttons, inputs, forms) live in the
[`immich_ui` package](https://github.com/immich-app/immich/tree/main/mobile/packages/ui/)
under `mobile/packages/ui/`. Components are defined in `lib/src/components/`
and have matching previews in `lib/src/previews/`.

To inspect a component in isolation with a light/dark toggle and hot reload,
launch [Flutter's Widget Previewer](https://docs.flutter.dev/tools/widget-previewer):

```bash
cd mobile/packages/ui
flutter widget-preview start
```

In VS Code or Android Studio with the Flutter plugin, the previewer
auto-starts when you open the **Flutter Widget Preview** tab in the sidebar.

## IDE setup

### Lint / format extensions

Setting these in the IDE give a better developer experience, auto-formatting code on save, and providing instant feedback on lint issues.

### Dart Code Metrics

The mobile app uses DCM (Dart Code Metrics) for linting and metrics calculation. Please refer to the [Getting Started](https://dcm.dev/docs/) page for more information on setting up DCM

Note: Activating the license is not required.

### VSCode

Install `Flutter`, `DCM`, `Prettier`, `ESLint` and `Svelte` extensions. These extensions are listed in the `extensions.json` file under `.vscode/` and should appear as workspace recommendations.

Here are the settings we use, they should be active as workspace settings (`settings.json`):

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Docs Developer Testing (`docs/docs/developer/testing.md`)

# Testing

## Server

### Unit tests

Unit tests are run with `mise //server:test`.
You need to run `mise //server:install` before _once_.

### End to end tests

The e2e tests can be run by first starting up a test production environment via:

```bash
mise e2e
```

Before you can run the tests, you need to run the following commands _once_:

- `mise //e2e:ci-setup` (installs e2e, SDK, and CLI dependencies)
- `mise //:open-api`

Once the test environment is running, the e2e tests can be run via:

```bash
mise //e2e:test
```

The tests check various things including:

- Authentication and authorization
- Query param, body, and url validation
- Response codes
- Thumbnail generation
- Metadata extraction
- Library scanning

---

### Docs Developer Translations (`docs/docs/developer/translations.md`)

# Translations

:::tip
You can request a new language [here](https://hosted.weblate.org/new-lang/immich/immich/).
:::

## Weblate

[Weblate](https://weblate.org/) is a "libre software web-based continuous localization system". Immich localization efforts are managed on their [hosted platform](https://hosted.weblate.org/projects/immich/immich/).

## International message format

Plurals, numbers, dates and other locale specific message formats can be handled by using the [ICU message format](https://unicode-org.github.io/icu/userguide/format_parse/messages/). Internally, this is handled by the [intl-messageformat](https://www.npmjs.com/package/intl-messageformat) library. Their [documentation](https://formatjs.io/docs/intl-messageformat/) includes common, editable examples via a "live editor" feature, which can be useful to test and debug message formats.

## Progress

Immich currently supports the following languages:

<a href="https://hosted.weblate.org/engage/immich/">
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
</a>

---

### Docs Developer Troubleshooting (`docs/docs/developer/troubleshooting.md`)

# Troubleshooting

:::tip
A great option to get assistance with troubleshooting is to join our [Discord](https://discord.immich.app) server, where we have a dedicated channel for `#contributing`.
:::

## Known Issues

### Running on Windows

Running Immich on Windows can be frustrating and there are lots of ways it can go wrong. Where possible we recommend using Docker on Linux. However, several people have had success running Immich on Windows using Docker via WSL2.

### NTFS Mounted Volumes

The docker-compose.dev.yml and docker-compose.prod.yml use volume mounts for the postgres database. On start-up, postgres will try to `chown` the data directory, but fail. See [this post](https://forums.docker.com/t/data-directory-var-lib-postgresql-data-pgdata-has-wrong-ownership/17963/24) for more information about this issue and possible solutions.

---

### Docs Features Casting (`docs/docs/features/casting.md`)

# Chromecast support

Immich supports the Google's Cast protocol so that photos and videos can be cast to devices such as a Chromecast and a Nest Hub. This feature is considered experimental and has several important limitations listed below. Currently, this feature is only supported by the web client, support on Android and iOS is planned for the future.

## Enable Google Cast Support

Google Cast support is disabled by default. The web UI uses Google-provided scripts and must retrieve them from Google servers when the page loads. This is a privacy concern for some and is thus opt-in.

You can enable Google Cast support through `Account Settings > Features > Cast > Google Cast`

<img src={require('./img/gcast-enable.webp').default} width="70%" title='Enable Google Cast Support' />

## Limitations

To use casting with Immich, there are a few prerequisites:

1. Your instance must be accessed via an HTTPS connection in order for the casting menu to show.
2. Your instance must be publicly accessible via HTTPS and a DNS record for the server must be accessible via Google's DNS servers (`8.8.8.8` and `8.8.4.4`)
3. Videos must be in a format that is compatible with Google Cast. For more info, check out [Google's documentation](https://developers.google.com/cast/docs/media)

---

### Docs Features Command Line Interface (`docs/docs/features/command-line-interface.md`)

# The Immich CLI

Immich has a command line interface (CLI) that allows you to perform certain actions from the command line.

## Features

- Upload photos and videos to Immich
- Check server version

More features are planned for the future.

:::tip Google Photos Takeout
If you are looking to import your Google Photos takeout, we recommend this community maintained tool [immich-go](https://github.com/simulot/immich-go)
:::

## Requirements

- Node.js 22 or above
- Npm

If you can't install node/npm, there is also a Docker version available below.

## Installation (NPM)

```bash
npm i @immich/cli
```

NOTE: if you previously installed the legacy CLI, you will need to uninstall it first:

```bash
npm uninstall -g immich
```

## Installation (Docker)

If npm is not available on your system you can try the Docker version

```bash
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest
```

Please modify the `IMMICH_INSTANCE_URL` and `IMMICH_API_KEY` environment variables as suitable. You can also use a Docker env file to store your sensitive API key.

This `docker run` command will directly run the command `immich` inside the container. You can directly append the desired parameters (see under "usage") to the commandline like this:

```bash
docker run -it -v "$(pwd)":/import:ro -e IMMICH_INSTANCE_URL=https://your-immich-instance/api -e IMMICH_API_KEY=your-api-key ghcr.io/immich-app/immich-cli:latest upload -a -c 5 --recursive directory/
```

## Usage

<details>
<summary>Usage</summary>

```
$ immich
Usage: immich [options] [command]

Command line interface for Immich

Options:
  -V, --version                       output the version number
  -d, --config-directory <directory>  Configuration directory where auth.yml will be stored (default: "~/.config/immich/", env:
                                      IMMICH_CONFIG_DIR)
  -u, --url [url]                     Immich server URL (env: IMMICH_INSTANCE_URL)
  -k, --key [key]                     Immich API key (env: IMMICH_API_KEY)
  -h, --help                          display help for command

Commands:
  login|login-key <url> <key>         Login using an API key
  logout                              Remove stored credentials
  server-info                         Display server information
  upload [options] [paths...]         Upload assets
  help [command]                      display help for command
```

</details>

## Commands

The upload command supports the following options:

<details>
<summary>Options</summary>

```
/* Detailed source-code truncated for AI context efficiency. */
```

</details>

Note that the above options can read from environment variables as well.

## Quick Start

You begin by authenticating to your Immich server. For instance:

```bash
# immich login [url] [key]
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
```

This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/immich/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.

Once you are authenticated, you can upload assets to your Immich server.

```bash
immich upload file1.jpg file2.jpg
```

By default, subfolders are not included. To upload a directory including subfolder, use the --recursive option:

```bash
immich upload --recursive directory/
```

If you are unsure what will happen, you can use the `--dry-run` option to see what would happen without actually performing any actions.

```bash
immich upload --dry-run --recursive directory/
```

By default, the upload command will hash the files before uploading them. This is to avoid uploading the same file multiple times. If you are sure that the files are unique, you can skip this step by passing the `--skip-hash` option. Note that Immich always performs its own deduplication through hashing, so this is merely a performance consideration. If you have good bandwidth it might be faster to skip hashing.

```bash
immich upload --skip-hash --recursive directory/
```

You can automatically create albums based on the folder name by passing the `--album` option. This will automatically create albums for each uploaded asset based on the name of the folder they are in.

```bash
immich upload --album --recursive directory/
```

You can also choose to upload all assets to a specific album with the `--album-name` option.

```bash
immich upload --album-name "My summer holiday" --recursive directory/
```

It is possible to skip assets matching a glob pattern by passing the `--ignore` option. See [the library documentation](docs/features/libraries.md) on how to use glob patterns. You can add several exclusion patterns if needed.

```bash
immich upload --ignore **/Raw/** --recursive directory/
```

```bash
immich upload --ignore **/Raw/** **/*.tif --recursive directory/
```

By default, hidden files are skipped. If you want to include hidden files, use the `--include-hidden` option:

```bash
immich upload --include-hidden --recursive directory/
```

You can set the visibility of uploaded assets to `archive`, `timeline`, `hidden`, or `locked` with the `--visibility` option:

```bash
immich upload --visibility archive --recursive directory/
```

You can use the `--json-output` option to get a json printed which includes
three keys: `newFiles`, `duplicates` and `newAssets`. Due to some logging
output you will need to strip the first three lines of output to get the json.
For example to get a list of files that would be uploaded for further
processing:

```bash
immich upload --dry-run --json-output . | tail -n +6 | jq .newFiles[]
```

### Obtain the API Key

The API key can be obtained in the user setting panel on the web interface. You can also specify permissions for the key to limit its access.

---

### Docs Features Duplicates Utility (`docs/docs/features/duplicates-utility.md`)

# Duplicates Utility

Immich comes with a duplicates utility to help you detect assets that look visually similar. The duplicate detection feature relies on machine learning and is enabled by default. For more information about when the duplicate detection job runs, see [Jobs and Workers](/administration/jobs-workers). Once an asset has been processed and added to a duplicate group, it becomes available to review in the "Review duplicates" utility, which can be found [here](https://my.immich.app/utilities/duplicates).

## Reviewing duplicates

The review duplicates page allows the user to individually select which assets should be kept and which ones should be trashed. When more than one asset is kept, there is an option to automatically put the kept assets into a stack.

### Automatic preselection

When using "Deduplicate All" or viewing suggestions, Immich automatically preselects which assets to keep based on:

1. **Image size in bytes** — larger files are preferred as they typically have higher quality.
2. **Count of EXIF data** — assets with more metadata are preferred.

### Synchronizing metadata

When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept asset. This synchronization only happens when **exactly one** asset is kept and at least one asset is trashed. When more than one asset is kept, metadata is not merged — the assets keep their own metadata and are simply removed from the duplicate group. The following metadata is synchronized:

| Name        | Description                                                                                                                    |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Album       | The kept asset will be added to _every_ album that the other assets in the group belong to.                                    |
| Favorite    | If any of the assets in the group have been added to favorites, the kept asset will also be added to favorites.                |
| Rating      | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept asset. |
| Description | Descriptions from each asset are combined together and synchronized to the kept asset.                                         |
| Visibility  | The most restrictive visibility is applied to the kept asset.                                                                  |
| Location    | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates.                 |
| Tag         | Tags from all assets in the group are merged and applied to the kept asset.                                                    |

---

### Docs Features Editing (`docs/docs/features/editing.mdx`)

# Editing

Immich supports non-destructive editing of photos. This means that any edits you make to an asset do not modify the original file, but instead create a new version of the asset with the edits applied. You can always revert back to the original asset if needed.

## Supported Edits

Currently, Immich supports the following types of edits:

- Cropping
- Rotation
- Mirroring

<img src={require('./img/web-edit-interface.webp').default} title="Web edit interface" />

## Download

When you download an edited asset, Immich provides the edited version of the asset by default. However, you can choose to download the original version if needed.

<img src={require('./img/web-edit-download.webp').default} title="Web edit download" />

---

### Docs Features Facial Recognition (`docs/docs/features/facial-recognition.md`)

# Facial Recognition

## Overview

Immich recognizes faces in your photos and videos and groups them together into people. You can then assign names to these people and search for them.

The list of people is shown in the Explore page.

<img src={require('./img/facial-recognition-1.webp').default} title='Facial Recognition 1' />

Upon clicking on a person, a list of assets that contain their face will be shown.

<img src={require('./img/facial-recognition-2.webp').default} title='Facial Recognition 2' />

The asset detail view will also show the faces that are recognized in the asset.

<img src={require('./img/facial-recognition-3.webp').default} title='Facial Recognition 3' />

## Actions

Additional actions you can do include:

- Changing the feature photo of the person
- Hiding the faces of a person from the Explore page and detail view
- Setting a person's date of birth, so that the age of the person can be shown at the time the photo was taken
- Merging two or more detected people into one person
- Favoriting a person to pin them to the top of the list

It can be found from the app bar when you access the detail view of a person.

<img src={require('./img/facial-recognition-4.webp').default} title='Facial Recognition 4' />

## How Face Detection Works

Face detection sends the generated preview image to the machine learning service for processing. The service checks if it has the relevant model downloaded and downloads it if not. The image is decoded, pre-processed and passed to the face detection model (with hardware acceleration if configured). The bounding boxes and scores outputted from this model are used to crop and preprocess the image once again to be passed to a facial recognition model (also accelerated if configured). The embeddings from the recognition model, together with the bounding boxes and scores from the face detection model, are then sent back to the server to be added to the database. The embeddings in particular are indexed so they can be searched quickly during facial recognition clustering.

## How Facial Recognition Works

The facial recognition algorithm we use is derived from [DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok), a popular clustering algorithm. It essentially treats each detected face as a point in a graph and aims to group points that are close to each other.

:::note
An important concept is whether something is a _core point_. A core point has a minimum number of points around it within a certain distance. A non-core point can only be assigned to a cluster if it can reach a core point; a non-core point can't be used to extend a cluster even if it's part of one. In Immich, the _Minimum Recognized Faces_ setting controls the threshold to be considered a core point.
:::

For each face, it looks around it to find other faces within a certain distance. Faces within this distance are considered similar, so it then checks if any of these faces are associated with a person.

If there is an existing person, it assigns the person of the most similar face to the face being processed.

If there is none, then it has to determine something from the DBSCAN algorithm: whether the face is a _core point_. If there are a certain number of similar faces (by default 3, including the face being considered), then this face is a core point. A new person is created for this face and the face is assigned to it. When other faces are processed, if they're similar to this face, they'll see that it has an associated person and can be assigned to that person.

However, if there aren't enough similar faces, no new person will be created. Instead, the face will wait for all the other faces to be processed to see if any matches that previously didn't have an associated person now do. If they do, then the face will be assigned to that person. If not, this face will be considered an outlier, such as a stranger in the background of an image.

The algorithm has some subtle differences compared to DBSCAN:

- DBSCAN doesn't have a concept of incremental clustering: it clusters all points at once. In contrast, facial recognition has to evolve as more assets are added without re-clustering everything each time.
  - The algorithm described above works within a set of queued assets. Once these faces are processed and a new round of faces are detected, the behavior will not be the same as traditional DBSCAN since it preserves the clusters (people) generated from the previous round.
    - Facial recognition tries to wait for face detection and thumbnail generation to complete before starting for this reason: the larger the set of faces in the queue, the better the results will be.
    - Re-running facial recognition on all assets afterwards does behave like DBSCAN, however.
- DBSCAN is designed for range-based searches (i.e. points within a distance), but high-dimensional vector indices are generally optimized for getting the closest K results. The recognition algorithm doesn't try to get _all_ similar faces within a distance for performance reasons. Instead, it searches for a small number of matches for each face. The end result should be very similar if not identical, but with possibly different performance characteristics.
  - Because of this, part of the recognition process is handled during a nightly job to ensure that unassigned faces with potential matches can be recognized.

:::tip
If you didn't import your assets at once or if the server was able to process jobs faster than you could upload them, it's possible that the clustering was suboptimal. If you haven't put effort into the current results, it may be worth re-running facial recognition on all assets for the best starting point. If it's too late for that, you can also manually assign a selection of unassigned faces and queue _Missing_ for Facial Recognition to help it learn and assign more faces automatically.
:::

## Configuration

Navigating to Administration > Settings > Machine Learning Settings > Facial Recognition will show the options available.

:::tip
It's better to only tweak the parameters here than to set them to something very different unless you're ready to test a variety of options. If you do need to set a parameter to a strict setting, relaxing other settings can be a good option to compensate, and vice versa.

You can learn how the tune the result in this [Guide](/guides/better-facial-clusters)
:::

### Facial recognition model

There are a few different models available; the default is typically considered the best. On more constrained systems where the default is too intensive, you can choose a smaller model instead.

### Minimum detection score

This setting affects whether a result from the face detection model is filtered out as a false positive. It may seem tempting to set this low to detect more faces, but it can lead to false positives that are difficult to deal with and can harm facial recognition. It is strongly recommended not to go below 0.5 for this setting. Setting it to a very high number like 0.9 is also not recommended: the default is already biased toward precision, so a threshold that high leads to many undetected faces.

After changing this setting, it will only apply to new face detection jobs. To apply the new setting to all assets, you need to re-run face detection for all assets.

### Maximum recognition distance

The distance threshold described in How Facial Recognition Works. The default works well for most people, but it may be worth lowering it if the library has twins or otherwise very similar looking people. A threshold that's too low just means needing to merge duplicate people after facial recognition, whereas a threshold too high can produce unsalvageable results. It is strongly recommended not to go below 0.3 or above 0.7.

### Minimum recognized faces

The core point threshold described in How Facial Recognition Works. This setting has a few implications. First, it takes effect immediately in that people with fewer faces than this are hidden from view. Secondly, it makes clustering more robust as it prevents loosely-related faces from being linked to each other by requiring a certain level of density.

Increasing this setting is a good idea if you increase the recognition distance or reduce the minimum detection score. Setting it to 1 effectively disables the concept of core points, but can be an option if you prefer a more hands-on approach.

---

### Docs Features Folder View (`docs/docs/features/folder-view.md`)

# Folder View

Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.

You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders)

## Enable folder view

<img src={require('./img/folder-view-enable.webp').default} width="80%" title='Folder view enable' />

## Usage

You can then navigate to the view from the sidebar to explore the folders and files in your library.

<img src={require('./img/folder-access.webp').default} width="30%" title='Folder view access' />

<img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder view' />

---

### Docs Features Hardware Transcoding (`docs/docs/features/hardware-transcoding.md`)

# Hardware Transcoding

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

This feature allows you to use a GPU to accelerate transcoding and reduce CPU load.
Note that hardware transcoding produces significantly larger videos than software transcoding with similar settings, typically with lower quality. Using slow presets and preferring more efficient codecs can narrow this gap.
As this is a new feature, it is still experimental and may not work on all systems.

:::info
You do not need to redo any transcoding jobs after enabling hardware acceleration. The acceleration device will be used for any jobs that run after enabling it.
:::

## Supported APIs

- NVENC (NVIDIA)
- Quick Sync (Intel)
- RKMPP (Rockchip)
- VAAPI (AMD / NVIDIA / Intel)

## Limitations

- The instructions and configurations here are specific to Docker Compose. Other container engines may require different configuration.
- Only Linux and Windows (through WSL2) servers are supported.
- WSL2 does not support Quick Sync.
- Raspberry Pi is currently not supported.
- Two-pass mode is only supported for NVENC. Other APIs will ignore this setting.
- By default, only encoding is currently hardware accelerated. This means the CPU is still used for software decoding and tone-mapping.
  - You can benefit from end-to-end acceleration by enabling hardware decoding in the video transcoding settings.
- Hardware dependent
  - Codec support varies, but H.264 and HEVC are usually supported.
    - Notably, NVIDIA and AMD GPUs do not support VP9 encoding.
  - Newer devices tend to have higher transcoding quality.

## Prerequisites

#### NVENC

- You must have the official NVIDIA driver installed on the server.
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.

#### QSV

- For VP9 to work:
  - You must have a 9th gen Intel CPU or newer
  - If you have an 11th gen CPU or older, then you may need to follow [these][jellyfin-lp] instructions as Low-Power mode is required
  - Additionally, if the server specifically has an 11th gen CPU and is running kernel 5.15 (shipped with Ubuntu 22.04 LTS), then you will need to upgrade this kernel (from [Jellyfin docs][jellyfin-kernel-bug])

#### RKMPP

For RKMPP to work:

- You must have a supported Rockchip ARM SoC.
- Only RK3588 supports hardware tonemapping, other SoCs use slower software tonemapping while still using hardware encoding.
- Tonemapping requires `/usr/lib/aarch64-linux-gnu/libmali.so.1` to be present on your host system. Install the [`libmali`][libmali-rockchip] release that corresponds to your Mali GPU (`libmali-valhall-g610-g13p0-gbm` on RK3588) and modify the [`hwaccel.transcoding.yml`][hw-file] file:
  - under `rkmpp` uncomment the 3 lines required for OpenCL tonemapping by removing the `#` symbol at the beginning of each line
  - `- /dev/mali0:/dev/mali0`
  - `- /etc/OpenCL:/etc/OpenCL:ro`
  - `- /usr/lib/aarch64-linux-gnu/libmali.so.1:/usr/lib/aarch64-linux-gnu/libmali.so.1:ro`

## Setup

<ComposeBuilder />

#### Basic Setup

1. If you do not already have it, download the latest [`hwaccel.transcoding.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
2. In the `docker-compose.yml` under `immich-server`, uncomment the `extends` section and change `cpu` to the appropriate backend.

   Note: For VAAPI on WSL2, be sure to use `vaapi-wsl` rather than `vaapi`

3. Redeploy the `immich-server` container with these updated settings.
4. In the Admin page under `Video transcoding settings`, change the hardware acceleration setting to the appropriate option and save.

   Note: For Jasper Lake and Elkhart Lake CPUs, you will need to set the `Hardware Acceleration` -> `Constant quality mode` to `CQP`

5. (Optional) Enable hardware decoding for optimal performance.

<details>
<summary>immich.json</summary>

If you use a [configuration file](/install/config-file.md), use the `accel` option to select the hardware (e.g. `qsv` for Intel or `nvenc` for Nvidia). Set `accelDecode` to `true` if you want hardware decoding.

```json
{
  "ffmpeg": {
    "accel": "qsv",
    "accelDecode": true
  }
}
```

</details>

#### Single Compose File

Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.transcoding.yml`][hw-file] file into the `immich-server` service directly.

For example, the `qsv` section in this file is:

```yaml
devices:
  - /dev/dri:/dev/dri
```

You can add this to the `immich-server` service instead of extending from `hwaccel.transcoding.yml`:

```yaml
immich-server:
  container_name: immich_server
  image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
  # Note the lack of an `extends` section
  devices:
    - /dev/dri:/dev/dri
  volumes:
  ...
```

Once this is done, you can continue to step 3 of "Basic Setup".

#### All-In-One - Unraid Setup

##### QSV

1. Unraid > Docker > (Stop) Immich container > Edit
2. Scroll down and select `Add another Path, Port, Variable, Label or Device`
3. In the drop-down menu, select `Device` and an entry with any name and the value `/dev/dri`.
4. Continue to step 4 of "Basic Setup".

##### NVENC

1. In the container app, add this environmental variable: Key=`NVIDIA_VISIBLE_DEVICES` Value=`all`
2. While still in the container app, change the container from Basic Mode to Advanced Mode and add the following parameter to the Extra Parameters field: `--runtime=nvidia`
3. Restart the container app.
4. Continue to step 4 of "Basic Setup".

## Tips

- You may want to choose a slower preset than for software transcoding to maintain quality and efficiency
- While you can use VAAPI with NVIDIA and Intel devices, prefer the more specific APIs since they're more optimized for their respective devices
- You can confirm the device is being recognized and used by checking its utilization (via `nvtop` for NVIDIA, `intel_gpu_top` for Intel, etc.) when transcoding. A lack of error logs when transcoding also indicates that it's being used.

[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.transcoding.yml
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
[jellyfin-lp]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#low-power-encoding
[jellyfin-kernel-bug]: https://jellyfin.org/docs/general/post-install/transcoding/hardware-acceleration/intel#known-issues-and-limitations-on-linux
[libmali-rockchip]: https://github.com/tsukumijima/libmali-rockchip/releases

---

### Docs Features Libraries (`docs/docs/features/libraries.md`)

# External Libraries

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

:::info
Currently an external library can only belong to a single user which is selected when the library is initially created.
:::

External libraries track assets stored in the filesystem outside of Immich. When the external library is scanned, Immich will load videos and photos from disk and create the corresponding assets. These assets will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. Later, if a file is modified outside of Immich, you need to scan the library for the changes to show up.

If an external asset is deleted from disk, Immich will move it to trash on rescan. To restore the asset, you need to restore the original file. After 30 days the file will be removed from trash, and any changes to metadata within Immich will be lost.

:::caution

If you add metadata to an external asset in any way (i.e. add it to an album or edit the description), that metadata is only stored inside Immich and will not be persisted to the external asset file. If you move an asset to another location within the library all such metadata will be lost upon rescan. This is because the asset is considered a new asset after the move. This is a known issue and will be fixed in a future release.

:::

:::caution

Due to aggressive caching it can take some time for a refreshed asset to appear correctly in the web view. You need to clear the cache in your browser to see the changes. This is a known issue and will be fixed in a future release. In Chrome, you need to open the developer console with F12, then reload the page with F5, and finally right click on the reload button and select "Empty Cache and Hard Reload".

:::

### Import Paths

External libraries use import paths to determine which files to scan. Each library can have multiple import paths so that files from different locations can be added to the same library. Import paths are scanned recursively, and if a file is in multiple import paths, it will only be added once. Each import file must be a readable directory that exists on the filesystem; the import path dialog will alert you of any paths that are not accessible.

If the import paths are edited in a way that an external file is no longer in any import path, it will be removed from the library in the same way a deleted file would. If the file is moved back to an import path, it will be added again as if it was a new file.

### Troubleshooting

Sometimes, an external library will not scan correctly. This can happen if Immich can't access the files. Here are some things to check:

- In the docker-compose file, are the volumes mounted correctly?
- Are the volumes also mounted to any worker containers?
- Are the import paths set correctly, and do they match the path set in docker-compose file?
- Make sure you don't use symlinks in your import libraries, and that you aren't linking across docker mounts.
- Are the permissions set correctly?
- Make sure you are using forward slashes (`/`) and not backward slashes.

To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/mnt/photos`, check it with `ls /mnt/photos`. If you are using a dedicated microservices container, make sure to add the same mount point and check for availability within the microservices container as well.

### Exclusion Patterns

By default, all files in the import paths will be added to the library. If there are files that should not be added, exclusion patterns can be used to exclude them. Exclusion patterns are glob patterns matched against the full file path. If a file matches an exclusion pattern, it will not be added to the library. If it has already been imported, it will be offlined on the next library scan. Exclusion patterns can be added in the Scan Settings page for each library.

Some basic examples:

- `**/*.tif` will exclude all files with the extension `.tif`
- `**/hidden.jpg` will exclude all files named `hidden.jpg`
- `**/Raw/**` will exclude all files in any directory named `Raw`
- `**/*.{tif,jpg}` will exclude all files with the extension `.tif` or `.jpg`
- `**/*.{arw,dng}` will exclude all files with the extension `.arw` or `.dng`

Exclusion patterns are case-insensitive for both new files and already imported assets. For example, `**/*.{arw,dng}` also excludes files with uppercase or mixed-case extensions such as `.ARW` and `.DnG`.

Note that `*` is a wildcard matching zero or more characters (i.e., within a filename or single directory name). `**` matches zero or more subdirectories, recursively. It also includes any/all files within a subdirectory, i.e., when used at the end of a pattern. For example, `**/exclude_me/**` will exclude all files in any directory named `exclude_me`, as well as all files in any subdirectories of `exclude_me`, recursively.

Special characters such as @ should be escaped, for instance:

- `**/\@eaDir/**` will exclude all files in any directory named `@eaDir`

:::info
For more information on the glob syntax supported by exclusion patterns, refer to the [Picomatch documentation](https://github.com/micromatch/picomatch#globbing-features).
:::

### Automatic watching (EXPERIMENTAL)

This feature is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan.

If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a [periodic library refresh](#set-custom-scan-interval) to pull in your changes.

#### Troubleshooting

If you encounter an `ENOSPC` error, you need to increase your file watcher limit. In sysctl, this key is called `fs.inotify.max_user_watches` and has a default value of 8192. Increase this number to a suitable value greater than the number of files you will be watching. Note that Immich has to watch all files in your import paths including any ignored files.

```
ERROR [LibraryService] Library watcher for library c69faf55-f96d-4aa0-b83b-2d80cbc27d98 encountered error: Error: ENOSPC: System limit for number of file watchers reached, watch '/media/photo.jpg'
```

In rare cases, the library watcher can hang, preventing Immich from starting up. In this case, disable the library watcher in the configuration file. If the watcher is enabled from within Immich, the app must be started without the microservices. Disable the microservices in the docker compose file, start Immich, disable the library watcher in the admin settings, close Immich, re-enable the microservices, and then Immich can be started normally.

### Nightly job

There is an automatic scan job that is scheduled to run once a day. Its schedule is configurable, see [Set Custom Scan Interval](#set-custom-scan-interval).

This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page.

### Deleting a Library

When deleting an external library, all assets inside are immediately deleted along with the library. Note that while a library can take a long time to fully delete in the background, it is immediately removed from the library list. If the deletion process is interrupted (for example, due to server restart), it will be cleaned up in the next nightly cron job. The cleanup process can also be manually initiated by clicking the "Scan All Libraries" button in the library list.

## Usage

Let's show a concrete example where we add an existing gallery to Immich. Here, we have the following folders we want to add:

- `/home/user/old-pics`: a folder containing childhood photos.
- `/mnt/nas/christmas-trip`: photos from a christmas trip. The subfolder `/mnt/nas/christmas-trip/Raw` contains the raw files directly from the DSLR. We don't want to import the raw files to Immich
- `/mnt/media/videos`: Videos from the same christmas trip.

First, we need to plan how we want to organize the libraries. The christmas trip photos should belong to its own library since we want to exclude the raw files. The videos and old photos can be in the same library since we want to import all files. We could also add all three folders to the same library if there are no files matching the Raw exclusion pattern in the other folders.

### Mount Docker Volumes

<ComposeBuilder query="storage.externalLibraries.0.path=&storage.externalLibraries.0.readOnly=true" />

The `immich-server` container will need access to the gallery. Modify your docker compose file as follows

```diff title="docker-compose.yml"
  immich-server:
    volumes:
      - ${UPLOAD_LOCATION}:/data
+     - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro
+     - /home/user/old-pics:/mnt/media/old-pics:ro
+     - /mnt/media/videos:/mnt/media/videos:ro
+     - /mnt/media/videos2:/mnt/media/videos2 # WARNING: Immich will be able to delete the files in this folder, as it does not end with :ro
+     - "C:/Users/user_name/Desktop/my media:/mnt/media/my-media:ro" # import path in Windows system.
```

:::tip
The `ro` flag at the end only gives read-only access to the volumes.
This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/features/xmp-sidecars)).
:::

:::info
_Remember to run `docker compose up -d` to register the changes. Make sure you can see the mounted path in the container._
:::

### Create A New Library

These actions must be performed by the Immich administrator.

- Click on your avatar in the upper right corner.
- Click on `Administration -> External Libraries`.
- Click on `Create Library`.
- Select which user owns the library, this **can not** be changed later
- You are now entering the library management page.
- Click on `Add` in the `Folders` section.
- Enter `/mnt/media/christmas-trip` then click Add.
- Click on `Edit` Library and rename it to "Christmas Trip".

NOTE: We have to use the `/mnt/media/christmas-trip` path and not the `/mnt/nas/christmas-trip` path since all paths have to be what the Docker containers see.

Next, we'll add an exclusion pattern to filter out raw files.

- Click on `Add` in the `Exclusion Patterns` section.
- Enter `**/Raw/**` and click Add.
- Click on `Scan`

The christmas trip library will now be scanned in the background. In the meantime, let's add the videos and old photos to another library.

- Go back to `Administration -> External Libraries`.
- Click on `Create Library`.
- Select which user owns the library,
- You are now entering the library management page.
- Click on `Add` in the `Folders` section.
- Enter `/mnt/media/old-pics` then click Add
- Click on `Add` in the `Folders` section.
- Enter `/mnt/media/videos` then click Add
- Click on `Scan`
- Click on `Edit` Library and rename it to "Old videos and photos".

Within seconds, the assets from the old-pics and videos folders should show up in the main timeline.

### Folder view

Folder view provides an additional view besides the timeline that is similar to a file explorer. It allows you to navigate through the folders and files in the library. This feature is handy for a highly curated and customized external library or a nicely configured storage template.

You can enable this feature under [`Account Settings > Features > Folders`](https://my.immich.app/user-settings?isOpen=feature+folders)

<img src={require('./img/folder-view-1.webp').default} width="100%" title='Folder-view' />

### Set Custom Scan Interval

:::note
Only an admin can do this.
:::

You can define a custom interval for the trigger external library rescan under Administration -> Settings -> External Library.  
You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/).

<img src={require('./img/library-custom-scan-interval.webp').default} width="75%" title='Set custom scan interval for external library' />

---

### Docs Features Locked Folder (`docs/docs/features/locked-folder.md`)

# Locked Folder

The Locked Folder keeps selected photos and videos out of normal library views and protects access to them with a PIN.

Assets can be moved to the Locked Folder from the asset actions menu. Access to the Locked Folder is protected by a PIN.

## Unlocking the Locked Folder

Unlocking the Locked Folder temporarily gives the current session access to locked assets.

While the session is unlocked, locked assets may also appear elsewhere in Immich where they are relevant, such as search results or utilities such as Review large files.

When the session is locked again, locked assets are hidden from those views.

## Session duration

Unlocking the Locked Folder initially grants elevated access to the current session for 15 minutes.

If the session is still being used near the end of that period, the expiry can be extended to keep the session unlocked for a short time longer.

Selecting **Lock** ends the elevated access immediately. Signing out also ends access because the session is destroyed.

## Moving assets out of the Locked Folder

Assets can be moved back out of the Locked Folder while the session is unlocked. Once moved out, they return to their normal visibility in Immich.

---

### Docs Features Ml Hardware Acceleration (`docs/docs/features/ml-hardware-acceleration.md`)

# Hardware-Accelerated Machine Learning

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

This feature allows you to use a GPU to accelerate machine learning tasks, such as Smart Search and Facial Recognition, while reducing CPU load.
As this is a new feature, it is still experimental and may not work on all systems.

:::info
You do not need to redo any machine learning jobs after enabling hardware acceleration. The acceleration device will be used for any jobs that run after enabling it.
:::

## Supported Backends

- ARM NN (Mali)
- CUDA (NVIDIA GPUs with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher)
- ROCm (AMD GPUs)
- OpenVINO (Intel GPUs such as Iris Xe and Arc)
- RKNN (Rockchip)

## Limitations

- The instructions and configurations here are specific to Docker Compose. Other container engines may require different configuration.
- Only Linux and Windows (through WSL2) servers are supported.
- ARM NN is only supported on devices with Mali GPUs. Other Arm devices are not supported.
- Some models may not be compatible with certain backends. CUDA is the most reliable.
- Search latency isn't improved by ARM NN due to model compatibility issues preventing its use. However, smart search jobs do make use of ARM NN.

## Prerequisites

#### ARM NN

- Make sure you have the appropriate linux kernel driver installed
  - This is usually pre-installed on the device vendor's Linux images
- `/dev/mali0` must be available in the host server
  - You may confirm this by running `ls /dev` to check that it exists
- You must have the closed-source `libmali.so` firmware (possibly with an additional firmware file)
  - Where and how you can get this file depends on device and vendor, but typically, the device vendor also supplies these
  - The `hwaccel.ml.yml` file assumes the path to it is `/usr/lib/libmali.so`, so update accordingly if it is elsewhere
  - The `hwaccel.ml.yml` file assumes an additional file `/lib/firmware/mali_csffw.bin`, so update accordingly if your device's driver does not require this file
- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for ARM NN specific settings
  - In particular, the `MACHINE_LEARNING_ANN_FP16_TURBO` can significantly improve performance at the cost of very slightly lower accuracy

#### CUDA

- The GPU must have compute capability 5.2 or greater.
- The server must have the official NVIDIA driver installed.
- The installed driver must be >= 545 (it must support CUDA 12.3).
- On Linux (except for WSL2), you also need to have [NVIDIA Container Toolkit][nvct] installed.

#### ROCm

- On Linux, The [AMDGPU driver module](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html) needs to be installed on the server and, if secure boot is used, the signing key of DKMS [needs to be enrolled in UEFI BIOS](https://wiki.debian.org/SecureBoot)
- The GPU must be supported by ROCm. If it isn't officially supported, you can attempt to use the `HSA_OVERRIDE_GFX_VERSION` environmental variable: `HSA_OVERRIDE_GFX_VERSION=<a supported version, e.g. 10.3.0>`. If this doesn't work, you might need to also set `HSA_USE_SVM=0`.
- The ROCm image is quite large and requires at least 35GiB of free disk space. However, pulling later updates to the service through Docker will generally only amount to a few hundred megabytes as the rest will be cached.
- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/install/environment-variables) setting).
- MIGraphX is a new backend for AMD cards, which compiles models at runtime. As such, the first few inferences will be slow.

#### OpenVINO

- Integrated GPUs are more likely to experience issues than discrete GPUs, especially for older processors or servers with low RAM.
- Ensure the server's kernel version is new enough to use the device for hardware acceleration.
- Expect higher RAM usage when using OpenVINO compared to CPU processing.

#### OpenVINO-WSL

- Ensure your container can access the /dev/dri directory, you can verify this by doing `docker exec -t immich_machine_learning ls -la /dev/dri`. If this is not the case execute `getent group render` and `getent group video` on the WSL host, then add those groups to hwaccel.ml.yaml
  ```yaml
  openvino-wsl:
    devices:
      - /dev/dri:/dev/dri
      - /dev/dxg:/dev/dxg
    volumes:
      - /dev/bus/usb:/dev/bus/usb
      - /usr/lib/wsl:/usr/lib/wsl
    group_add:
      - 44 # Replace this number with the number you found with getent group video
      - 992 # Replace this number with the number you found with getent group render
  ```

#### RKNN

- You must have a supported Rockchip SoC: only RK3566, RK3568, RK3576 and RK3588 are supported at this moment.
- Make sure you have the appropriate linux kernel driver installed
  - This is usually pre-installed on the device vendor's Linux images
- RKNPU driver V0.9.8 or later must be available in the host server
  - You may confirm this by running `cat /sys/kernel/debug/rknpu/version` to check the version
- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for RKNN specific settings
  - In particular, setting `MACHINE_LEARNING_RKNN_THREADS` to 2 or 3 can _dramatically_ improve performance for RK3576 and RK3588 compared to the default of 1, at the expense of multiplying the amount of RAM each model uses by that amount.

## Setup

<ComposeBuilder />

1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
2. In `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
3. Still in the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
4. Redeploy the `immich-machine-learning` container with these updated settings.

### Confirming Device Usage

You can confirm the device is being recognized and used by checking its utilization. There are many tools to display this, such as `nvtop` for NVIDIA or Intel, `intel_gpu_top` for Intel, and `radeontop` for AMD.

You can also check the logs of the `immich-machine-learning` container. When a Smart Search or Face Detection job begins, or when you search with text in Immich, you should either see a log for `Available ORT providers` containing the relevant provider (e.g. `CUDAExecutionProvider` in the case of CUDA), or a `Loaded ANN model` log entry without errors in the case of ARM NN.

#### Single Compose File

Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.ml.yml`][hw-file] file into the `immich-machine-learning` service directly.

For example, the `cuda` section in this file is:

```yaml
deploy:
  resources:
    reservations:
      devices:
        - driver: nvidia
          count: 1
          capabilities:
            - gpu
```

You can add this to the `immich-machine-learning` service instead of extending from `hwaccel.ml.yml`:

```yaml
immich-machine-learning:
  container_name: immich_machine_learning
  # Note the `-cuda` at the end
  image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda
  # Note the lack of an `extends` section
  deploy:
    resources:
      reservations:
        devices:
          - driver: nvidia
            count: 1
            capabilities:
              - gpu
  volumes:
    - model-cache:/cache
  env_file:
    - .env
  restart: always
```

Once this is done, you can redeploy the `immich-machine-learning` container.

#### Multi-GPU

If you want to utilize multiple NVIDIA or Intel GPUs, you can set the `MACHINE_LEARNING_DEVICE_IDS` environmental variable to a comma-separated list of device IDs and set `MACHINE_LEARNING_WORKERS` to the number of listed devices. You can run a command such as `nvidia-smi -L` or `glxinfo -B` to see the currently available devices and their corresponding IDs.

For example, if you have devices 0 and 1, set the values as follows:

```
MACHINE_LEARNING_DEVICE_IDS=0,1
MACHINE_LEARNING_WORKERS=2
```

In this example, the machine learning service will spawn two workers, one of which will allocate models to device 0 and the other to device 1. Different requests will be processed by one worker or the other.

This approach can be used to simply specify a particular device as well. For example, setting `MACHINE_LEARNING_DEVICE_IDS=1` will ensure device 1 is always used instead of device 0.

Note that you should increase job concurrencies to increase overall utilization and more effectively distribute work across multiple GPUs. Additionally, each GPU must be able to load all models. It is not possible to distribute a single model to multiple GPUs that individually have insufficient VRAM, or to delegate a specific model to one GPU.

[hw-file]: https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml
[nvct]: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html

## Tips

- If you encounter an error when a model is running, try a different model to see if the issue is model-specific.
- You may want to increase concurrency past the default for higher utilization. However, keep in mind that this will also increase VRAM consumption.
- Larger models benefit more from hardware acceleration, if you have the VRAM for them.
- Compared to ARM NN, RKNPU has:
  - Wider model support (including for search, which ARM NN does not accelerate)
  - Less heat generation
  - Very slightly lower accuracy (RKNPU always uses FP16, while ARM NN by default uses higher precision FP32 unless `MACHINE_LEARNING_ANN_FP16_TURBO` is enabled)
  - Varying speed (tested on RK3588):
    - If `MACHINE_LEARNING_RKNN_THREADS` is at the default of 1, RKNPU will have substantially lower throughput for ML jobs than ARM NN in most cases, but similar latency (such as when searching)
    - If `MACHINE_LEARNING_RKNN_THREADS` is set to 3, it will be somewhat faster than ARM NN at FP32, but somewhat slower than ARM NN if `MACHINE_LEARNING_ANN_FP16_TURBO` is enabled
    - When other tasks also use the GPU (like transcoding), RKNPU has a significant advantage over ARM NN as it uses the otherwise idle NPU instead of competing for GPU usage
  - Lower RAM usage if `MACHINE_LEARNING_RKNN_THREADS` is at the default of 1, but significantly higher if greater than 1 (which is necessary for it to fully utilize the NPU and hence be comparable in speed to ARM NN)

---

### Docs Features Mobile App (`docs/docs/features/mobile-app.mdx`)

import Icon from '@mdi/react';
import { mdiCloudOffOutline, mdiCloudCheckOutline } from '@mdi/js';
import MobileAppDownload from '/docs/partials/_mobile-app-download.md';
import MobileAppLogin from '/docs/partials/_mobile-app-login.md';
import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';

# Mobile App

## Download

<MobileAppDownload />

:::info Android verification
Below are the SHA-256 fingerprints for the certificates signing the android applications.

- Google Play releases:
  `5A:22:C1:83:47:54:05:F5:49:C4:EB:9F:B2:6C:2E:93:A3:EF:9C:57:66:15:0A:7A:F3:8C:8D:3F:E5:15:CC:D6`
- GitHub releases:
  `86:C5:C4:55:DF:AF:49:85:92:3A:8F:35:AD:B3:1D:0C:9E:0B:95:7D:7F:94:C2:D2:AF:6A:24:38:AA:96:00:20`
- F-Droid releases:
  `FA:8B:43:95:F4:A6:47:71:A0:53:D1:C7:57:73:5F:A2:30:13:74:F5:3D:58:0D:D1:75:AA:F7:A1:35:72:9C:BF`

:::

## Login

<MobileAppLogin />

## Backup

<MobileAppBackup />

## Sync only selected photos

If you have a large number of photos on the device, and you would prefer not to backup all the photos, then it might be prudent to only backup selected photos from device to the Immich server.

First, you need to enable the Storage Indicator in your app's settings. Navigate to **<ins>Settings -> Photo Grid</ins>** and enable **`Show Storage indicator on asset tiles`**; this makes it easy to distinguish local-only assets and synced assets.

:::note

This will enable a small cloud icon on the bottom right corner of the asset tile, indicating that the asset is synced to the server:

1. <Icon path={mdiCloudOffOutline} size={1} /> - Local-only asset; not synced to the server
2. <Icon path={mdiCloudCheckOutline} size={1} /> - Asset is synced to the server

:::

Now make sure that the local album is selected in the backup screen (steps 1-2 above). You can find these albums listed in **<ins>Library -> On this device</ins>**. To selectively upload photos from these albums, simply select the local-only photos and tap on the `Upload` button in the dynamic bottom menu.

<img
  src={require('./img/mobile-upload-selected-photos.webp').default}
  width="40%"
  title="Upload button after photos selection"
/>

## Free Up Space

**Free Up Space** allows you to remove local media files from your device that have already been successfully backed up to your Immich server (and are not in Immich trash). This helps reclaim storage on your mobile device without losing your memories.

### How it works

<img src={require('./img/free-up-space.webp').default} title="Free up space" />

1.  **Configuration:**
    - **Cutoff date:** Free Up Space will only look for photos and videos **on or before** this date. Photos removed from the device don't show up in other (messaging) apps and have to be shared from Immich in order to send them.
    - **Keep favorites:** This works the same way `Keep albums` does. By default, favorited assets are preserved on your device.
    - **Keep albums:** Hold all photos and videos in the selected albums on your device, regardless of other settings. By default, `WhatsApp` [related albums](#external-app-dependencies) are selected to be kept on the device. Assets not already on the device will not be re-downloaded.
    - **Keep on device:** You can choose to restrict removal to `Always keep` **All photos** or **All videos**, regardless of other settings. This setting can hamper freeing up space significantly — with 80 GB of videos and 40 GB photos, selecting `Always keep photos` retains thousands of photos on your device.

2.  **Scan & Review:** Before any files are removed, you are presented with a review screen to verify which items will be deleted and how much storage is reclamable.
3.  **Deletion:** Confirmed items are moved to your device's native Trash/Recycle Bin. For large queues, Immich processes deletion in batches for stability (`2000` assets per batch on Android, `10000` per batch on iOS).

:::info reclaim storage
To use the reclaimed space right away, you must empty the system/gallery trash manually outside of Immich.
:::

Provided the server is healthy and [backed up](/administration/backup-and-restore.md), assets removed by Free Up Space can always be accessed in the Immich app.

### iCloud Photos

If you use **iCloud Photos** alongside Immich, it is vital to understand how deletion affects your data. After using **Free Up Space**, the photo will be stored **only** on your Immich server (and your phone's "Recently Deleted" folder for 30 days).

Assets that are part of an **iCloud Shared Album** are automatically excluded from the cleanup scan because iCloud does not allow removing the items in Shared Album from the device.

:::warning iCloud & Backups
If, in addition to Immich, you rely on iCloud as a secondary backup (as part of your [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup strategy), you should instead use `Optimize iPhone Storage` in [iCloud Photos](https://support.apple.com/en-us/105061).

iCloud utilizes a two-way sync; this means deleting a photo, or using Free Up Space from your iPhone will **also delete it from iCloud** and all other devices (Mac, iPad) where you're signed in with the same Apple Account. See [Apple Support](https://support.apple.com/en-us/108922#iCloud_photo_library) for more info.
:::

### External App Dependencies (WhatsApp, etc.) \{#external-app-dependencies\}

Android applications like **WhatsApp** rely on local files to display media in chat history.

If Immich backs up your WhatsApp folder and you run **Free Up Space**, the local copies of these images will be deleted. Consequently, **media in your WhatsApp chats will appear blurry or missing.** You will only be able to view these photos inside the Immich app; they will no longer be visible within the WhatsApp interface.

**Recommendation:** If keeping chat history intact is important, exclude WhatsApp with `Keep albums` in Free Up Space and review the deletion list carefully. You have to enable [Album Sync](#album-sync) for WhatsApp to show up in the list. Alternatively, don't [back up](#backup) WhatsApp with Immich.

## Album Sync

You can sync or mirror an album from your phone to the Immich server on your account. For example, if you select Recents, Camera and Videos album for backup, the corresponding album with the same name will be created on the server. Once the assets from those albums are uploaded, they will be put into the target albums automatically.

### Album Synchronization Highlights

- **One-Way Sync:** Synchronization is one-way, from the device to the server.

- **Name Matching:** If an album on the server has the same name as the album on the device, images from the device will be merged with the existing images in the server album.

- **Shared Albums:** If the matching album on the server is shared, the new photos merged into the album will also be shared.

- **Album Structure:** When an album is created for the first time, its structure is based on the initial state. Future updates made on the phone (such as deleting or repositioning photos) will not be reflected in Immich.

- **User-Specific Sync:** Album synchronization is unique to each server user and does not sync between different users or partners.

- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/features/libraries) for further details.

### Synchronizing albums from the past

<img src={require('./img/backup-album-sync.webp').default} width="400px" title="Backup album synchronization option" />

Albums can be synchronized to the server even if they did not exist on the server before. You can enable this feature at any time and use the **Reorganize into album** button to backfill existing uploads into their corresponding albums.

:::info Sync albums delete/move photos
If you delete/move photos in the local album on your device, it will not be reflected in the album on the server **even if** you click Sync albums
It will only reflect files you add.
:::

## Read-only/kid Mode

You can set the app to read-only mode to prevent accidental deletion of photos from your device, and only allow viewing photos on the timeline.

To toggle this feature, long-press the profile icon or go to `Settings > Advanced > Read-only Mode`.

<img src={require('./img/read-only-mode.webp').default} width="800px" title="Read-only mode" />

---

### Docs Features Mobile Backup (`docs/docs/features/mobile-backup.md`)

---
sidebar_position: 1
---

# Mobile Backup

## Overview

Immich supports uploading photos and videos from your mobile device to the server automatically.

When backup is enabled, Immich will upload new photos and videos from selected albums when you open or resume the app, as well as periodically in the background.

<img
src={require('./img/enable-backup-button.webp').default}
width="400px"
title="Upload button"
/>

## General Features

### Backup albums selection

<img
src={require('./img/backup-album-selection.webp').default}
width="400px"
title="Backup album selection"
/>

You can select which albums on your mobile device to back up to the server. You can also exclude specific albums (by double-tapping on them) from being backed up. This is useful for iOS users since assets can belong to multiple albums. For example, you may want to back up all assets except those in the "Videos" album.

### Deduplication

When you first select albums for backup, Immich calculates a checksum for each file's content. This checksum identifies assets already on the server—whether uploaded via CLI, web interface, or another device. Files matching existing assets are skipped, preventing duplicate uploads and saving bandwidth.

### Networking requirements

By default, Immich will only upload photos and videos when connected to Wi-Fi. You can change this behavior in the backup settings page.

<img
src={require('./img/backup-options.webp').default}
width="400px"
title="Upload button"
/>

### Backup album synchronization

<img
src={require('./img/backup-album-sync.webp').default}
width="400px"
title="Backup album synchronization option"
/>

When enabled, Immich automatically creates albums on the server that mirror the albums on your mobile device. Photos and videos are organized into these server-side albums to match your device's album structure, making it easy to find and browse your content the same way you do on your phone.

This is a one-way sync from your device to the server. You can enable this feature at any time and use the **Reorganize into album** button to backfill existing uploads into their corresponding albums.

## Platform Specific Features

### Android

<img
src={require('./img/android-backup-options.webp').default}
width="400px"
title="Android backup options"
/>

- It is a well-known problem that some Android models are very strict with battery optimization settings, which can cause a problem with the background worker. Please visit [Don't kill my app](https://dontkillmyapp.com/) for a guide on disabling this setting on your phone.
- You can allow the background task to run only when the device is charging.
- You can set the minimum delay from the time a photo is taken to when the background upload task will run.

### iOS

- You must enable **Background App Refresh** for the app to work in the background. You can enable it in the Settings app under General > Background App Refresh.

<div style={{textAlign: 'center'}}>
<img src={require('./img/background-app-refresh.webp').default} width="30%" title="background-app-refresh" />
</div>

- iOS automatically manages background tasks; the app cannot control when the background upload task will run. The more frequently you open the app, the more often background tasks will run.

#### iCloud Backup

Local albums containing assets from iCloud and marked for backup in Immich will be pulled from iCloud and temporarily stored in the app's cache folder. Once the hashing and uploading process is completed, the temporary files will be emptied.

This process may consume additional data and storage space on your device, especially if you have a large number of iCloud photos and videos. Please ensure you have sufficient storage space and monitor your data usage if you are not connected to Wi-Fi.

---

### Docs Features Monitoring (`docs/docs/features/monitoring.md`)

# Monitoring

## Overview

Immich provides a variety of performance metrics to allow for local monitoring and insights. This integration is primarily in the form of Prometheus metrics. However, exporting traces is also possible due to the use of OpenTelemetry instrumentation.

:::note
This is an opt-in feature intended for you to monitor immich's performance. This data isn't sent anywhere beyond what you've configured.
:::

## Prometheus

Prometheus is a tool that collects metrics from a number of sources you configure. It operates in a "pull" strategy - that is, it periodically requests metrics from each defined source. This means that the source doesn't send anything until it's requested. It also means that the source -- immich, in this case -- has to expose an endpoint for Prometheus to target when it requests metrics.

### Metrics

These metrics come in a variety of forms:

- Counters, which can only increase. Example: the number of times an endpoint has been called.
- Gauges, which can increase or decrease within a certain range. Example: CPU utilization.
- Histograms, where each observation is assigned to a certain number of "buckets". Example: response time, where each bucket is a number of milliseconds. This one is a bit more complicated.
  - Buckets in this case are _cumulative_; that is, an observation is placed not only into the smallest bucket that contains it, but also to all buckets larger than this. For example, if a histogram has three buckets for 1ms, 5ms and 10ms, an observation of 3ms will be bucketed into both 5ms and 10ms.

The metrics in immich are grouped into API (endpoint calls and response times), host (memory and CPU utilization), and IO (internal database queries, image processing, and so on). Each group of metrics can be enabled or disabled independently.

### Configuration

Immich will not expose an endpoint for metrics by default. To enable this endpoint, you can add the `IMMICH_TELEMETRY_INCLUDE=all` environmental variable to your `.env` file. Note that only the server container currently use this variable.

:::tip
`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/install/environment-variables.md#prometheus).
:::

The next step is to configure a new or existing Prometheus instance to scrape this endpoint. The following steps assume that you do not have an existing Prometheus instance, but the steps will be similar either way.

You can start by defining a Prometheus service in the Compose file:

```yaml
immich-prometheus:
  container_name: immich_prometheus
  ports:
    # this exposes the default port for Prometheus so you can interact with it
    - 9090:9090
  image: prom/prometheus
  volumes:
    # the Prometheus configuration file - a barebones one is provided to get started
    - ./prometheus.yml:/etc/prometheus/prometheus.yml
    # a named volume defined in the bottom of the Compose file; it can also be a mounted folder
    - prometheus-data:/prometheus
```

You will also need to add `prometheus-data` to the list of volumes in the bottom of the Compose file:

```yaml
volumes:
  model-cache:
  prometheus-data:
```

The last piece is the [configuration file][prom-file]. This file defines (among other things) the sources Prometheus should target. Download it and place it in the same folder as the Compose file.

:::tip
The provided file is just a starting point. There are a ton of ways to configure Prometheus, so feel free to experiment!
:::

After bringing down the containers with `docker compose down` and back up with `docker compose up -d`, a Prometheus instance will now collect metrics from the immich server and microservices containers. Note that we didn't need to expose any new ports for these containers - the communication is handled in the internal Docker network.

:::note
To see exactly what metrics are made available, you can additionally add `8081:8081` (API metrics) and `8082:8082` (microservices metrics) to the immich_server container's ports.
Visiting the `/metrics` endpoint for these services will show the same raw data that Prometheus collects.
To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/install/environment-variables/#general).
:::

### Usage

So after setting up Prometheus, how do you actually view the metrics? The simplest way is to use Prometheus directly. Visiting Prometheus will show you a web UI where you can search for and visualize metrics. You can also view the status of your data sources and configure settings, but this is beyond the scope of this guide.

## Grafana

For a dedicated tool with nice presentation, you can use Grafana instead. This connects to Prometheus (and possibly other sources) for sophisticated data visualization.

Setting up Grafana is similar to Prometheus. You can add a service for it:

```yaml
immich-grafana:
  container_name: immich_grafana
  command: ['./run.sh', '-disable-reporting'] # this is to disable Grafana's telemetry
  ports:
    - 3000:3000
  image: grafana/grafana
  volumes:
    # stores your pretty dashboards and panels
    - grafana-data:/var/lib/grafana
```

And add another volume for it:

```yaml
volumes:
  model-cache:
  prometheus-data:
  grafana-data:
```

After bringing down the services and back up again, you can now visit Grafana to view your metrics. On the first login, enter `admin` for both username and password and update your password. You can then go to the settings and add a data source with `http://immich-prometheus:9090` to point Grafana to your Prometheus instance.

### Usage

You can make your first dashboard to get started. Don't forget to save it frequently, or you'll lose all your progress!

You can then make a new panel, specifying Prometheus as the data source for it.

-- TODO: add images and more details here

## Structured Logging

In addition to Prometheus metrics, Immich supports structured JSON logging which is ideal for log aggregation systems like Grafana Loki, ELK Stack, Datadog, Splunk, and others.

### Configuration

By default, Immich outputs human-readable console logs. To enable JSON logging, set the `IMMICH_LOG_FORMAT` environment variable:

```bash
IMMICH_LOG_FORMAT=json
```

:::tip
The default is `IMMICH_LOG_FORMAT=console` for human-readable logs with colors during development. For production deployments using log aggregation, use `IMMICH_LOG_FORMAT=json`.
:::

### JSON Log Format

When enabled, logs are output in structured JSON format:

```json
{"level":"log","pid":36,"timestamp":1766533331507,"message":"Initialized websocket server","context":"WebsocketRepository"}
{"level":"warn","pid":48,"timestamp":1766533331629,"message":"Unable to open /build/www/index.html, skipping SSR.","context":"ApiService"}
{"level":"error","pid":36,"timestamp":1766533331690,"message":"Failed to load plugin immich-core:","context":"Error"}
```

This format includes:

- `level`: Log level (log, warn, error, etc.)
- `pid`: Process ID
- `timestamp`: Unix timestamp in milliseconds
- `message`: Log message
- `context`: Service or component that generated the log

For more information on log formats, see [`IMMICH_LOG_FORMAT`](/install/environment-variables.md#general).

[prom-file]: https://github.com/immich-app/immich/releases/latest/download/prometheus.yml

---

### Docs Features Partner Sharing (`docs/docs/features/partner-sharing.md`)

# Partner Sharing

Immich allows you to share your library with other users. They can then view your library and download the assets. You can manage Partner Sharing from the [User Settings](docs/features/user-settings.md) page on the web.

Partner sharing includes:

- Access to all non-archived and trashed photos and videos.
- Access to all metadata, including GPS information.
- Access to share assets via shared links, albums, etc.

Partner sharing does _not_ include:

- Already existing partner albums
- If an asset is favorited
- People and facial recognition data

:::note

- Partner sharing is one-way. To view your partner's assets, they must also share them with you.
- Partner sharing may result in displaying duplicate assets on the main timeline, as duplicates are only detected on a per-user basis.

:::

## Sharing with a Partner

<img src={require('./img/partner-sharing-1.webp').default} width="70%" title='Add Partner 1' />

<img src={require('./img/partner-sharing-2.webp').default} width="70%" title='Add Partner 2' />

<img src={require('./img/partner-sharing-4.webp').default} width="70%" title='Add Partner 4' />

## Viewing Partner Assets

Access partner assets via the Sharing page.

<img src={require('./img/partner-sharing-3.webp').default} width="70%" title='Access to the Shared Library' />

## Timeline Integration

Partner shared photos can be displayed in the main timeline. This feature can be enabled on a per-partner basis and can be viewed and updated on both the web and mobile app.

### Web

The option can be found at [`Account Settings > Partner Sharing > Show in timeline`](https://my.immich.app/user-settings?isOpen=partner-sharing)

<img src={require('./img/partner-sharing-5.webp').default} width="70%" title='Partner Sharing for the web interface' />

### Mobile App

From the partner’s view, toggle the button

<img src={require('./img/partner-sharing-6.webp').default} width="30%" title='Partner Sharing for the mobile app' />

<img src={require('./img/partner-sharing-8.webp').default} width="30%" title='Partner Sharing for the mobile app' />

## Removing Access

In order to remove a partner, you can go to `User > Account Settings > Sharing` and click on the X button.

<img src={require('./img/partner-sharing-7.webp').default} width="70%" title='Remove Partner' />

---

### Docs Features Reverse Geocoding (`docs/docs/features/reverse-geocoding.md`)

# Reverse Geocoding

Immich supports local [Reverse Geocoding](https://en.wikipedia.org/wiki/Reverse_geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. This data is loaded into the Postgres database on each minor version upgrade, allowing all queries to be run on your own server.

## Extraction

During Exif Extraction, assets with latitudes and longitudes are reverse geocoded to determine their City, State, and Country.

## Usage

Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/features/searching.md).

<img src={require('./img/reverse-geocoding-mobile3.webp').default} width='33%' title='Reverse Geocoding' />
<img src={require('./img/reverse-geocoding-mobile1.webp').default} width='33%' title='Reverse Geocoding' />
<img src={require('./img/reverse-geocoding-mobile2.webp').default} width='33%' title='Reverse Geocoding' />

---

### Docs Features Searching (`docs/docs/features/searching.md`)

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Searching

Immich uses Postgres as its search database for both metadata and contextual CLIP search.

Contextual CLIP search is powered by the [VectorChord](https://github.com/tensorchord/VectorChord) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata.

## Advanced Search Filters

In addition, Immich offers advanced search functionality, allowing you to find specific content using customizable search filters. These filters include location, one or more faces, specific albums, and more. You can try out the search filters on the [Demo site](https://demo.immich.app).

You can search the following types of content:

| Type                                | Description                                           |
| ----------------------------------- | ----------------------------------------------------- |
| People                              | Faces that are recognized in your photos/videos.      |
| Contextual                          | Content of the photos and videos.                     |
| File name or extension              | Full or partial file's name, or file's extension      |
| Full path or folder                 | Full or partial folder names from the original path.  |
| Description                         | Description added to assets.                          |
| Optical Character Recognition (OCR) | Text in images                                        |
| Locations                           | Cities, states, and countries from reverse geocoding. |
| Tags                                | Tags assigned or extracted from assets.               |
| Camera                              | make, model and lens model                            |
| Time frame                          | Start and end date of a specific time bucket          |
| Media type                          | Image or video or both                                |
| Display options                     | In Archive, in Favorites or Not in any album          |
| Star rating                         | User-assigned star rating                             |

<img src={require('./img/advanced-search-filters.webp').default} width="70%" title='Advanced search filters' />

### Full path or folder

Use this mode when you know a folder name or part of the original asset path.

Example: for /John/Projects/3D_Printing/2026-07-01/IMG_0001.jpg, searches like Projects, 3D, Printing, or 2026 match the asset.

## Configuration

Navigating to `Administration > Settings > Machine Learning Settings > Smart Search` will show the options available.

### CLIP models

The default search model is fast, but there are many other options that can provide better search results. The tradeoff of using these models is that they're slower and/or use more memory (both when indexing images with background Smart Search jobs and when searching).

The first step of choosing the right model for you is to know which languages your users will search in.

If your users will only search in English, then the [CLIP][huggingface-clip] section is the first place to look. This is a curated list of the models that generally perform the best for their size class. The models here are ordered from higher to lower quality. This means that the top models will generally rank the most relevant results higher and have a higher capacity to understand descriptive, detailed, and/or niche queries. The models are also generally ordered from larger to smaller, so consider the impact on memory usage, job processing and search speed when deciding on one. The smaller models in this list are not too different in quality and many times faster.

[Multilingual models][huggingface-multilingual-clip] are also available so users can search in their native language. Use these models if you expect non-English searches to be common. They can be separated into two search patterns:

- `nllb` models expect the search query to be in the language specified in the user settings
- `xlm` and `siglip2` models understand search text regardless of the current language setting

`nllb` models tend to perform the best and are recommended when users primarily searches in their native, non-English language. `xlm` and `siglip2` models are more flexible and are recommended for mixed language search, where the same user might search in different languages at different times.

For more details, check the tables below to see how they compare in memory usage, speed and quality by language.

Once you've chosen a model, follow these steps:

1. Copy the name of the model (e.g. `ViT-B-16-SigLIP__webli`)
2. Go to the [Smart Search settings][smart-search-settings]
3. Paste the model name into the Model Name section
4. Save the settings
5. Go to the [Job Status page][job-status-page]
6. Click "All" next to "Smart Search" to begin re-processing your assets with the new model
7. (Optional) Confirm that the logs for the server and machine learning service don't have relevant errors

In rare instances, changing the model might leave bits of the old model's incompatible data in the database, causing errors when processing Smart Search jobs. If you notice errors like this in the logs, you can change the model back to the previous one and save, then repeat steps 3-7.

Please note that memory and execution time values are only _estimates_: actual usage will be different depending on many factors. As such, it's mainly intended as a way to compare the relative tradeoffs of each model.

<details>
<summary>Reference</summary>

Memory and execution time estimates were obtained without acceleration on a 7800x3D processor running bare metal Linux. All testing and evaluation was done at f32 precision (the default in Immich).

**Execution Time (ms)**: After warming up the model with one pass, the mean execution time of 100 passes with the same input.

**Memory (MiB)**: The peak RSS usage of the process after performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.

**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.

**Pareto Optimal**: Whether the model is not completely outclassed by another model. Try to use models that are optimal for the languages relevant to you. Specifically, for a given model and language, if there's another model that's better for that language in at least one respect (memory usage, execution time, recall) while being at least as good for that language in every other way, then the model is not optimal for that language.

</details>

---

<details>
<summary>English</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 85.99      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 85.96      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 85.96      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 85.93      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 85.78      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 85.75      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 85.62      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 85.53      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 85.48      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 85.47      | ✅             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 85.09      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 85.03      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 84.86      | ✅             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 84.61      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 84.17      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 83.51      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 83.28      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.24      | ❌             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.23      | ❌             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 83.19      | ✅             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 82.54      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 82.43      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 82.36      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 82.28      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 81.9       | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 81.9       | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 80.82      | ❌             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 80.65      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 80.16      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 79.78      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 78.64      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 78.6       | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 78.06      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 78.06      | ❌             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 77.62      | ✅             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 77.47      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 76.91      | ❌             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 76.43      | ✅             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 76.35      | ❌             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 73.83      | ✅             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 73.62      | ❌             |
| RN50x64__openai                                      | 5079         | 48.79               | 73.34      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 72.99      | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 72.76      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 72.59      | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 70.8       | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 70.01      | ❌             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 69.9       | ✅             |
| RN101__openai                                        | 1111         | 3.21                | 69.3       | ❌             |
| RN50__openai                                         | 913          | 2.39                | 69.02      | ✅             |
| RN50__cc12m                                          | 914          | 2.37                | 64.59      | ✅             |
| RN101__yfcc15m                                       | 1111         | 3.22                | 55.21      | ❌             |
| RN50__yfcc15m                                        | 908          | 2.34                | 53.63      | ✅             |
</details>
<details>
<summary>Arabic</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 77.3       | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 76.44      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 74.03      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 73.19      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 69.31      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 69.29      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 69.29      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 68.64      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 68.35      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 68.25      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 68.23      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 67.56      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 67.28      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 66.89      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 66.52      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 64.1       | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 61.71      | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 60.7       | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 59.66      | ✅             |
</details>
<details>
<summary>Bengali</summary>
| Model                            | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|----------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1       | 4226         | 75.05               | 76.16      | ✅             |
| nllb-clip-large-siglip__mrl      | 4248         | 75.44               | 75.83      | ❌             |
| nllb-clip-base-siglip__mrl       | 4696         | 16.95               | 73.75      | ✅             |
| nllb-clip-base-siglip__v1        | 4675         | 15.17               | 73.34      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli  | 3029         | 6.87                | 36.43      | ✅             |
| ViT-SO400M-14-SigLIP2__webli     | 3622         | 27.63               | 26.56      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli | 3611         | 27.84               | 26.54      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli | 3854         | 56.57               | 26.19      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli | 3940         | 72.25               | 26.19      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli | 4050         | 107.67              | 25.92      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli   | 6585         | 146.84              | 25.15      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli   | 6475         | 64.51               | 24.18      | ❌             |
| ViT-L-16-SigLIP2-384__webli      | 3057         | 51.7                | 21.44      | ❌             |
| ViT-L-16-SigLIP2-512__webli      | 3358         | 92.59               | 21.11      | ❌             |
| ViT-L-16-SigLIP2-256__webli      | 2830         | 23.77               | 20.94      | ✅             |
</details>
<details>
<summary>Chinese (Simplified)</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 79.7       | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 78.94      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 75.22      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 74.8       | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 73.91      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 72.8       | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 72.77      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 72.41      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 72.36      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 71.59      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 71.37      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 71.3       | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 71.11      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 70.95      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 70.51      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 67.48      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 66.84      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 65.7       | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 63.38      | ❌             |
</details>
<details>
<summary>Croatian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 87.46      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 87.19      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 82.98      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 82.92      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 81.93      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 73.77      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 73.21      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 73.2       | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 72.95      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 72.89      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 72.88      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 72.85      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 72.69      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 70.73      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 70.45      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 70.43      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 69.97      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 54.31      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 53.3       | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 35.64      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 35.17      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 33.65      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 33.55      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 20.05      | ✅             |
</details>
<details>
<summary>Cusco Quechua</summary>
| Model                       | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|-----------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl | 4248         | 75.44               | 38.08      | ✅             |
| nllb-clip-large-siglip__v1  | 4226         | 75.05               | 37.87      | ✅             |
| nllb-clip-base-siglip__mrl  | 4696         | 16.95               | 33.41      | ✅             |
| nllb-clip-base-siglip__v1   | 4675         | 15.17               | 33.06      | ✅             |
</details>
<details>
<summary>Czech</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 73.76      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 71.57      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 69.86      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 67.49      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 67.15      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 63.62      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 63.35      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 63.09      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 63.07      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 62.98      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 62.82      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 62.73      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 62.29      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 62.12      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 61.74      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 61.52      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 61.01      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 54.81      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 54.31      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 33.58      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 33.48      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 32.38      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 32.32      | ❌             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 22.89      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 22.66      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 22.6       | ❌             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 22.25      | ❌             |
</details>
<details>
<summary>Danish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 87.16      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 86.88      | ❌             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 84.18      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 84.03      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 83.75      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 83.32      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 83.25      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 82.3       | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 82.19      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 81.87      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 81.44      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 81.42      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 80.0       | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 79.82      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 79.08      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 75.07      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 74.84      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 67.68      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 67.2       | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 65.59      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 65.36      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 42.31      | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 41.46      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 40.52      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 31.31      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 30.97      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 30.87      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 30.51      | ❌             |
</details>
<details>
<summary>Dutch</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 80.05      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 79.81      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 79.72      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 79.72      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 79.64      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 79.49      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 79.41      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 79.31      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 78.92      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 78.48      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 78.22      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 78.0       | ✅             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 77.22      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 76.69      | ❌             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 75.94      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 75.6       | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 75.33      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 75.04      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 72.97      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 72.72      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 72.06      | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 72.06      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 70.81      | ✅             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 69.82      | ✅             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 67.54      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 66.77      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 66.6       | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 65.67      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 65.29      | ✅             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 41.1       | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 34.29      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 29.65      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 29.56      | ❌             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 29.54      | ✅             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 29.36      | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 27.76      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 27.76      | ❌             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 25.67      | ✅             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 25.59      | ❌             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 25.53      | ❌             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 25.52      | ✅             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 22.31      | ❌             |
| RN50x64__openai                                      | 5079         | 48.79               | 22.27      | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 21.8       | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 20.69      | ❌             |
</details>
<details>
<summary>Filipino</summary>
| Model                            | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|----------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl      | 4248         | 75.44               | 67.57      | ✅             |
| nllb-clip-large-siglip__v1       | 4226         | 75.05               | 65.64      | ✅             |
| nllb-clip-base-siglip__mrl       | 4696         | 16.95               | 61.21      | ✅             |
| nllb-clip-base-siglip__v1        | 4675         | 15.17               | 59.42      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli  | 3029         | 6.87                | 36.81      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli   | 6585         | 146.84              | 35.72      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli   | 6475         | 64.51               | 34.75      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli | 3940         | 72.25               | 34.63      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli | 4050         | 107.67              | 34.39      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli | 3854         | 56.57               | 34.27      | ❌             |
| ViT-SO400M-14-SigLIP2__webli     | 3622         | 27.63               | 34.14      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli | 3611         | 27.84               | 33.98      | ❌             |
| ViT-L-16-SigLIP2-384__webli      | 3057         | 51.7                | 30.57      | ❌             |
| ViT-L-16-SigLIP2-512__webli      | 3358         | 92.59               | 30.57      | ❌             |
| ViT-L-16-SigLIP2-256__webli      | 2830         | 23.77               | 30.05      | ✅             |
| ViT-L-16-SigLIP-384__webli       | 3396         | 47.6                | 24.92      | ❌             |
| ViT-L-16-SigLIP-256__webli       | 3160         | 23.84               | 24.02      | ❌             |
| ViT-B-16-SigLIP2__webli          | 3038         | 5.81                | 23.37      | ✅             |
| ViT-B-32-SigLIP2-256__webli      | 3061         | 3.31                | 22.69      | ✅             |
</details>
<details>
<summary>Finnish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 84.27      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.93      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 79.41      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 78.94      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 75.49      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 63.46      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 63.16      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 63.08      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 63.03      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 62.28      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 61.92      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 61.81      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 61.76      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 61.05      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 57.8       | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 57.69      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 57.05      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 40.26      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 40.06      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 31.75      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 31.74      | ❌             |
</details>
<details>
<summary>French</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 86.5       | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 86.5       | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 86.39      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 86.15      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 86.1       | ❌             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 86.07      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 86.06      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 85.89      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 85.67      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 85.67      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 85.63      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 85.39      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 85.35      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 84.97      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 83.8       | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 82.96      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 82.91      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 82.52      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 81.21      | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 80.23      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 79.85      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 79.47      | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 79.3       | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 77.49      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 76.82      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 75.94      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 75.3       | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 75.24      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 69.33      | ❌             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 64.41      | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 62.86      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 59.27      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 59.09      | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 58.25      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 58.25      | ❌             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 56.97      | ✅             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 56.21      | ✅             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 53.36      | ✅             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 53.33      | ✅             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 53.26      | ❌             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 53.22      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 46.34      | ❌             |
| RN50x64__openai                                      | 5079         | 48.79               | 46.3       | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 45.95      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 45.69      | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 42.48      | ❌             |
| RN101__openai                                        | 1111         | 3.21                | 40.16      | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 40.1       | ❌             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 38.27      | ✅             |
| RN50__openai                                         | 913          | 2.39                | 37.8       | ✅             |
</details>
<details>
<summary>German</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 87.32      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 87.29      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 87.29      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 87.21      | ✅             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 87.18      | ❌             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 87.14      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 87.07      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 86.83      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 86.81      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 86.75      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 86.74      | ✅             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 86.68      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 86.56      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 86.16      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 84.54      | ❌             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 84.41      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 84.25      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 83.8       | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 82.59      | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 81.53      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 81.34      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 81.15      | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 81.05      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 78.35      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 76.56      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 76.0       | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 75.21      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 75.14      | ❌             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 65.86      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 56.87      | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 47.19      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 43.36      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 43.0       | ❌             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 41.81      | ✅             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 40.43      | ✅             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 40.41      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 40.41      | ❌             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 37.71      | ✅             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 37.64      | ✅             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 36.04      | ✅             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 35.9       | ❌             |
| RN50x64__openai                                      | 5079         | 48.79               | 34.19      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 33.1       | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 32.25      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 30.56      | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 29.2       | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 25.77      | ❌             |
| RN101__openai                                        | 1111         | 3.21                | 25.46      | ❌             |
| RN50__openai                                         | 913          | 2.39                | 24.92      | ✅             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 24.13      | ✅             |
</details>
<details>
<summary>Greek</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 74.58      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 73.28      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 71.28      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 69.16      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 68.21      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 64.69      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 61.64      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 61.03      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 60.63      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 60.41      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 60.1       | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 60.06      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 60.06      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 59.44      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 59.44      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 59.43      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 58.78      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 53.42      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 53.24      | ❌             |
</details>
<details>
<summary>Hebrew</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 88.04      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 87.09      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 83.93      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 83.84      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 80.78      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 74.59      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 72.73      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 72.25      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 72.19      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 72.15      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 72.08      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 72.07      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 72.06      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 71.78      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 70.55      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 70.03      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 69.34      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 60.33      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 58.49      | ❌             |
</details>
<details>
<summary>Hindi</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 62.02      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 61.67      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 58.68      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 58.54      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 38.54      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 36.95      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 36.62      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 36.06      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 35.76      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 35.34      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 35.17      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 34.94      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 34.91      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 34.19      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 33.56      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 32.06      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 31.85      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 27.87      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 27.08      | ❌             |
</details>
<details>
<summary>Hungarian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 85.59      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 85.25      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 81.74      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 80.34      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 80.14      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 74.94      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 74.2       | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 74.03      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 73.96      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 73.95      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 73.9       | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 73.59      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 73.12      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 72.5       | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 72.33      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 71.83      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 70.57      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 58.31      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 56.74      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 38.26      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 37.97      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 28.75      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 28.26      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 24.88      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 24.39      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 24.29      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 24.16      | ❌             |
</details>
<details>
<summary>Indonesian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 85.46      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 85.12      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 85.01      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 84.99      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 84.65      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 84.62      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 84.58      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 84.11      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 84.1       | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 84.06      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 83.69      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 83.61      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 82.31      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 81.97      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 80.93      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 79.84      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 77.12      | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 77.02      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 74.15      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 71.44      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 69.94      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 65.87      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 65.19      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 59.95      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 59.38      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 57.88      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 57.52      | ✅             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 54.11      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 50.02      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 23.25      | ❌             |
</details>
<details>
<summary>Italian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 87.17      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 86.91      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 86.83      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 86.77      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 86.67      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 86.42      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 86.35      | ✅             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 86.34      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 86.18      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 86.17      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 85.84      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 85.8       | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 85.7       | ✅             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 85.67      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 83.32      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 82.95      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 82.73      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 82.72      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 81.07      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 80.8       | ✅             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 80.6       | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 80.35      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 78.79      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 76.62      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 76.51      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 76.08      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 75.29      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 75.29      | ❌             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 74.84      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 56.32      | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 47.25      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 43.09      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 42.99      | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 40.29      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 40.29      | ❌             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 39.67      | ✅             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 39.03      | ✅             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 36.14      | ✅             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 35.89      | ❌             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 35.59      | ❌             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 35.56      | ✅             |
| RN50x64__openai                                      | 5079         | 48.79               | 33.53      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 32.19      | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 30.95      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 28.85      | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 25.75      | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 25.18      | ❌             |
| RN101__openai                                        | 1111         | 3.21                | 24.48      | ❌             |
| RN50__openai                                         | 913          | 2.39                | 23.89      | ✅             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 23.39      | ✅             |
</details>
<details>
<summary>Japanese</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 83.95      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 82.21      | ❌             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 81.55      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 78.72      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 78.53      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 75.93      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 66.86      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 65.59      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 65.48      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 65.36      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 64.47      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 64.17      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 64.08      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 63.69      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 63.33      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 63.02      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 58.39      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 56.38      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 53.16      | ❌             |
</details>
<details>
<summary>Korean</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 80.56      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 80.53      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 77.09      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 77.08      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 76.97      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 76.92      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 76.58      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 76.2       | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 75.95      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 75.86      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 75.67      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 75.49      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 74.6       | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 74.52      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 73.88      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 71.09      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 68.87      | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 67.94      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 66.39      | ✅             |
</details>
<details>
<summary>Maori</summary>
| Model                       | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|-----------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl | 4248         | 75.44               | 48.43      | ✅             |
| nllb-clip-large-siglip__v1  | 4226         | 75.05               | 46.12      | ✅             |
| nllb-clip-base-siglip__mrl  | 4696         | 16.95               | 42.8       | ✅             |
| nllb-clip-base-siglip__v1   | 4675         | 15.17               | 40.85      | ✅             |
</details>
<details>
<summary>Norwegian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 81.36      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 80.96      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 77.65      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 76.39      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 75.97      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 75.44      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 75.31      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 75.0       | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 74.96      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 74.92      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 74.44      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 74.37      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 73.11      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 72.63      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 71.71      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 67.81      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 65.55      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 62.56      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 60.94      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 59.62      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 59.49      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 46.3       | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 45.75      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 42.55      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 35.33      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 35.01      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 34.94      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 34.39      | ❌             |
</details>
<details>
<summary>Persian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 79.52      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 78.99      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 76.32      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 76.3       | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 76.11      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 75.56      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 75.38      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 74.92      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 74.86      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 74.73      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 74.32      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 74.31      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 73.42      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 72.56      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 71.9       | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 69.79      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 68.55      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 68.26      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 65.16      | ❌             |
</details>
<details>
<summary>Polish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.49      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 83.45      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.11      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 82.99      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 82.96      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 82.93      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 82.61      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 82.26      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 82.24      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 82.03      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 82.03      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 81.92      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 81.27      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 80.0       | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 79.65      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 76.75      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 76.52      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 75.1       | ✅             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 73.9       | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 65.03      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 64.89      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 51.6       | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 51.29      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 46.15      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 41.55      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 41.17      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 40.9       | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 40.76      | ✅             |
</details>
<details>
<summary>Portuguese</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 82.12      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 81.84      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 81.69      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 81.69      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 81.54      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 81.39      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 80.56      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 80.34      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 80.02      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 79.99      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 79.93      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 79.61      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 79.12      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 78.87      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 78.85      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 77.54      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 75.31      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 75.26      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 74.82      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 74.48      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 74.47      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 73.92      | ✅             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 73.58      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 73.02      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 71.44      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 71.16      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 69.69      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 69.32      | ✅             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 59.86      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 45.49      | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 37.86      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 36.01      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 35.75      | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 33.25      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 33.25      | ❌             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 32.83      | ✅             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 32.62      | ✅             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 30.86      | ❌             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 30.8       | ✅             |
| RN50x64__openai                                      | 5079         | 48.79               | 30.58      | ❌             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 30.18      | ✅             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 29.93      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 28.88      | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 28.49      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 23.9       | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 22.94      | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 22.55      | ❌             |
| RN50__openai                                         | 913          | 2.39                | 21.85      | ✅             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 21.3       | ✅             |
| RN101__openai                                        | 1111         | 3.21                | 21.14      | ❌             |
</details>
<details>
<summary>Romanian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 89.38      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 88.86      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 85.37      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 84.92      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 84.49      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 77.92      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 74.98      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 74.33      | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 74.05      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 74.03      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 73.94      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 73.27      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 73.22      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 72.91      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 72.43      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 71.93      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 71.5       | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 58.28      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 56.54      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 56.12      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 55.53      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 34.96      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 26.33      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 26.05      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 21.32      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 21.04      | ❌             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 20.76      | ❌             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 20.56      | ✅             |
</details>
<details>
<summary>Russian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 84.54      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 84.41      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 84.36      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 84.31      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 84.22      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 83.9       | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 83.69      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 83.5       | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.31      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 83.21      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 83.11      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 82.7       | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 82.69      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 80.91      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 79.75      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 79.35      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 78.91      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 78.06      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 76.44      | ✅             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 42.81      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 42.1       | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 24.95      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 24.25      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 20.85      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 20.44      | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 20.41      | ❌             |
</details>
<details>
<summary>Spanish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 85.47      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 85.44      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 85.32      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 85.22      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 85.15      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 84.81      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 84.68      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 84.6       | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 84.55      | ✅             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 84.27      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 84.15      | ✅             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 83.87      | ❌             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.74      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 83.61      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.15      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 81.7       | ❌             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 80.91      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 80.73      | ✅             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 80.69      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 80.3       | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 79.8       | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 79.71      | ✅             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 79.64      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 78.0       | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 77.83      | ❌             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 76.87      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 76.66      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 75.99      | ✅             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 71.96      | ❌             |
| ViT-H-14__laion2b-s32b-b79k                          | 4676         | 39.06               | 62.06      | ❌             |
| ViT-L-14__laion2b-s32b-b82k                          | 2233         | 20.56               | 53.78      | ❌             |
| ViT-L-14__laion400m_e32                              | 2218         | 19.73               | 50.13      | ❌             |
| ViT-L-14__laion400m_e31                              | 2183         | 19.87               | 50.0       | ❌             |
| ViT-B-16-plus-240__laion400m_e32                     | 1246         | 6.95                | 47.39      | ❌             |
| ViT-B-16-plus-240__laion400m_e31                     | 1263         | 6.94                | 47.39      | ❌             |
| ViT-B-32__laion2b_e16                                | 1004         | 2.38                | 46.47      | ✅             |
| ViT-B-32__laion2b-s34b-b79k                          | 1001         | 2.29                | 45.68      | ✅             |
| ViT-B-16__laion400m_e31                              | 991          | 5.04                | 44.0       | ✅             |
| ViT-B-16__laion400m_e32                              | 975          | 4.98                | 43.98      | ✅             |
| ViT-B-32__laion400m_e32                              | 1003         | 2.35                | 43.8       | ❌             |
| ViT-B-32__laion400m_e31                              | 999          | 2.28                | 43.73      | ✅             |
| RN50x64__openai                                      | 5079         | 48.79               | 43.01      | ❌             |
| ViT-L-14__openai                                     | 2212         | 19.91               | 42.96      | ❌             |
| ViT-L-14-336__openai                                 | 2616         | 43.45               | 41.67      | ❌             |
| RN50x16__openai                                      | 2221         | 15.87               | 40.21      | ❌             |
| RN50x4__openai                                       | 1416         | 5.85                | 36.06      | ❌             |
| ViT-B-16__openai                                     | 985          | 5.03                | 35.67      | ❌             |
| RN101__openai                                        | 1111         | 3.21                | 34.62      | ❌             |
| ViT-B-32__openai                                     | 1004         | 2.26                | 32.6       | ✅             |
| RN50__openai                                         | 913          | 2.39                | 31.79      | ✅             |
</details>
<details>
<summary>Swahili</summary>
| Model                           | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|---------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl     | 4248         | 75.44               | 69.51      | ✅             |
| nllb-clip-large-siglip__v1      | 4226         | 75.05               | 68.44      | ✅             |
| nllb-clip-base-siglip__mrl      | 4696         | 16.95               | 66.09      | ✅             |
| nllb-clip-base-siglip__v1       | 4675         | 15.17               | 63.98      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli | 3029         | 6.87                | 21.64      | ✅             |
</details>
<details>
<summary>Swedish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 77.12      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 76.37      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 73.41      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 72.83      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 72.51      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 72.2       | ❌             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 72.1       | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 72.06      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 71.84      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 71.7       | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 71.7       | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 71.61      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 71.51      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 71.45      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 71.23      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 67.48      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 66.93      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 66.37      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 64.86      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 62.35      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 61.51      | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 56.74      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 55.92      | ❌             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 48.5       | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 48.38      | ✅             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 48.06      | ❌             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 47.99      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 47.93      | ❌             |
| ViT-SO400M-14-SigLIP-384__webli                      | 4417         | 72.19               | 29.98      | ❌             |
</details>
<details>
<summary>Telugu</summary>
| Model                       | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|-----------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl | 4248         | 75.44               | 64.32      | ✅             |
| nllb-clip-large-siglip__v1  | 4226         | 75.05               | 62.34      | ✅             |
| nllb-clip-base-siglip__mrl  | 4696         | 16.95               | 60.72      | ✅             |
| nllb-clip-base-siglip__v1   | 4675         | 15.17               | 58.8       | ✅             |
</details>
<details>
<summary>Thai</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 79.99      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 79.07      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 76.13      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 75.23      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 74.04      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 66.03      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 45.87      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 45.69      | ❌             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 45.52      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 44.96      | ❌             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 44.75      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 44.66      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 43.99      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 43.91      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 43.06      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 41.86      | ❌             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 41.1       | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 37.35      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 35.28      | ❌             |
</details>
<details>
<summary>Turkish</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.91      | ✅             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.74      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 81.26      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 80.21      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 79.34      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 79.22      | ✅             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 78.9       | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 78.85      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 78.29      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 78.27      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 78.0       | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 77.81      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 77.67      | ✅             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 77.33      | ✅             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 76.42      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 72.44      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 69.84      | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 69.83      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 67.13      | ❌             |
| ViT-H-14-378-quickgelu__dfn5b                        | 5049         | 108.4               | 44.43      | ❌             |
| ViT-H-14-quickgelu__dfn5b                            | 4701         | 38.74               | 43.87      | ❌             |
| ViT-L-16-SigLIP-384__webli                           | 3396         | 47.6                | 35.1       | ❌             |
| ViT-L-16-SigLIP-256__webli                           | 3160         | 23.84               | 34.92      | ❌             |
| ViT-L-14-quickgelu__dfn2b                            | 2212         | 20.49               | 25.2       | ✅             |
| ViT-B-16-SigLIP-512__webli                           | 1828         | 26.17               | 24.55      | ✅             |
| ViT-B-16-SigLIP__webli                               | 1081         | 5.77                | 24.13      | ✅             |
| ViT-B-16-SigLIP-384__webli                           | 1128         | 13.53               | 24.08      | ❌             |
| ViT-B-16-SigLIP-256__webli                           | 1102         | 7.11                | 23.95      | ❌             |
</details>
<details>
<summary>Ukrainian</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.92      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.88      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 83.2       | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 79.99      | ✅             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 79.31      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 78.73      | ✅             |
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 78.33      | ✅             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 77.95      | ❌             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 77.56      | ✅             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 77.49      | ✅             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 77.02      | ❌             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 76.87      | ❌             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 76.31      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 75.91      | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 75.75      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 75.1       | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 73.3       | ✅             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 65.28      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 63.95      | ❌             |
</details>
<details>
<summary>Vietnamese</summary>
| Model                                                | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-384__webli                     | 3854         | 56.57               | 85.86      | ✅             |
| ViT-SO400M-14-SigLIP2-378__webli                     | 3940         | 72.25               | 85.73      | ❌             |
| ViT-SO400M-16-SigLIP2-512__webli                     | 4050         | 107.67              | 85.67      | ❌             |
| ViT-gopt-16-SigLIP2-384__webli                       | 6585         | 146.84              | 85.5       | ❌             |
| ViT-L-16-SigLIP2-384__webli                          | 3057         | 51.7                | 84.93      | ✅             |
| ViT-SO400M-16-SigLIP2-256__webli                     | 3611         | 27.84               | 84.84      | ✅             |
| ViT-L-16-SigLIP2-512__webli                          | 3358         | 92.59               | 84.78      | ❌             |
| ViT-SO400M-14-SigLIP2__webli                         | 3622         | 27.63               | 84.34      | ✅             |
| ViT-gopt-16-SigLIP2-256__webli                       | 6475         | 64.51               | 84.33      | ❌             |
| ViT-L-16-SigLIP2-256__webli                          | 2830         | 23.77               | 83.93      | ✅             |
| nllb-clip-large-siglip__mrl                          | 4248         | 75.44               | 83.69      | ❌             |
| nllb-clip-large-siglip__v1                           | 4226         | 75.05               | 83.19      | ❌             |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014         | 39.14               | 81.88      | ❌             |
| ViT-B-16-SigLIP2__webli                              | 3038         | 5.81                | 80.88      | ✅             |
| nllb-clip-base-siglip__mrl                           | 4696         | 16.95               | 79.79      | ❌             |
| nllb-clip-base-siglip__v1                            | 4675         | 15.17               | 79.38      | ❌             |
| ViT-B-32-SigLIP2-256__webli                          | 3061         | 3.31                | 77.73      | ✅             |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k         | 3030         | 3.2                 | 75.18      | ✅             |
| ViT-B-16-SigLIP-i18n-256__webli                      | 3029         | 6.87                | 73.05      | ✅             |
</details>

:::note
Feel free to make a feature request if there's a model you want to use that we don't currently support.
:::

[huggingface-clip]: https://huggingface.co/collections/immich-app/clip-654eaefb077425890874cd07
[huggingface-multilingual-clip]: https://huggingface.co/collections/immich-app/multilingual-clip-654eb08c2382f591eeb8c2a7
[smart-search-settings]: https://my.immich.app/admin/system-settings?isOpen=machine-learning+smart-search
[job-status-page]: https://my.immich.app/admin/queues

---

### Docs Features Sharing (`docs/docs/features/sharing.md`)

# Sharing

Immich supports local sharing, with users on the same Immich instance, and public sharing via public links.

## Local sharing

### Albums

Albums can be shared between users on the same Immich instance. The shared users can view and add their own photos and videos to the shared album.

After creating an album, you can access the sharing options by clicking on the share icon. When sharing an album, you can select the users you want to share the album with and assign them permissions either as editors (read-write) or viewers (read-only).

#### Web

<img src={require('./img/shared-album.webp').default} width='60%' title='Shared album option' caption='ok' />

<img src={require('./img/shared-album-user-selection.webp').default} width='30%' height='100%' title='Shared album user selection' />

#### Mobile App

<img src={require('./img/shared-album-mobile.webp').default} width='33%' title='Shared album option' />

### Partners

Partner sharing allows you to share your _entire_ library with other users of your choice. They can then view your library and download the assets.

You can read this guide to learn more about [partner sharing](/features/partner-sharing).

## Public sharing

You can create a public link to share a group of photos or videos, or an album, with anyone. The public link can be shared via email, social media, or any other method. There are a variety of options to customize the public link, such as setting an expiration date, password protection, and more. Public shared link is handy when you want to share a group of photos or videos with someone who doesn't have an Immich account and allow the shared user to upload their photos or videos to your account.

The public shared link is generated with a random URL, which acts as as a secret to avoid the link being guessed by unwanted parties, for instance.

```
https://my.immich.app/share/JUckRMxlgpo7F9BpyqGk_cZEwDzaU_U5LU5_oNZp1ETIBa9dpQ0b5ghNm_22QVJfn3k
```

### Creating a public share link

You can create a public share link by selecting the photos or videos, or from the share icon on an album.

<img src={require('./img/public-shared-link-individual.webp').default} width='60%' title='Creating public shared link from selection' />

<img src={require('./img/public-shared-link-album.webp').default} width='30%' title='Creating public shared link from album' />

### Customizing the public share link

You can customize the public share link by setting an expiration date, password protection, allow what actions can be performed on the shared assets, and more.

<img src={require('./img/public-shared-link-form.webp').default} width='33%' title='Creating public shared link from album' />

---

### Docs Features Supported Formats (`docs/docs/features/supported-formats.md`)

# Supported Media Formats

Immich supports a number of image and video formats, the most common of which are outlined here.

:::note
For the full list, refer to the [Immich source code](https://github.com/immich-app/immich/blob/main/server/src/utils/mime-types.ts).
:::

## Image formats

| Format      | Extension(s)                          |     Supported?     | Notes           |
| :---------- | :------------------------------------ | :----------------: | :-------------- |
| `AVIF`      | `.avif`                               | :white_check_mark: |                 |
| `BMP`       | `.bmp`                                | :white_check_mark: |                 |
| `GIF`       | `.gif`                                | :white_check_mark: |                 |
| `HEIC`      | `.heic`                               | :white_check_mark: |                 |
| `HEIF`      | `.heif`                               | :white_check_mark: |                 |
| `JPEG 2000` | `.jp2`                                | :white_check_mark: |                 |
| `JPEG`      | `.jfif` `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: |                 |
| `JPEG XL`   | `.jxl`                                | :white_check_mark: |                 |
| `MPO`       | `.mpo`                                | :white_check_mark: | Multi-Picture   |
| `PNG`       | `.png`                                | :white_check_mark: |                 |
| `PSD`       | `.psd`                                | :white_check_mark: | Adobe Photoshop |
| `RAW`       | `.raw`                                | :white_check_mark: |                 |
| `RW2`       | `.rw2`                                | :white_check_mark: |                 |
| `SVG`       | `.svg`                                | :white_check_mark: |                 |
| `TIFF`      | `.tif` `.tiff`                        | :white_check_mark: |                 |
| `WEBP`      | `.webp`                               | :white_check_mark: |                 |

## Video formats

| Format      | Extension(s)                |     Supported?     | Notes |
| :---------- | :-------------------------- | :----------------: | :---- |
| `3GPP`      | `.3gp` `.3gpp`              | :white_check_mark: |       |
| `AVI`       | `.avi`                      | :white_check_mark: |       |
| `FLV`       | `.flv`                      | :white_check_mark: |       |
| `M4V`       | `.m4v`                      | :white_check_mark: |       |
| `MATROSKA`  | `.mkv`                      | :white_check_mark: |       |
| `MP2T`      | `.mts` `.m2ts` `.m2t` `.ts` | :white_check_mark: |       |
| `MP4`       | `.mp4` `.insv`              | :white_check_mark: |       |
| `MPEG`      | `.mpg` `.mpe` `.mpeg`       | :white_check_mark: |       |
| `MXF`       | `.mxf`                      | :white_check_mark: |       |
| `QUICKTIME` | `.mov`                      | :white_check_mark: |       |
| `WEBM`      | `.webm`                     | :white_check_mark: |       |
| `WMV`       | `.wmv`                      | :white_check_mark: |       |

---

### Docs Features Tags (`docs/docs/features/tags.md`)

# Tags

Immich supports hierarchical tags, with the ability to read existing tags from the XMP `TagsList` field and IPTC `Keywords` field. Any changes to tags made through Immich are also written back to a [sidecar](/features/xmp-sidecars) file. You can re-run the metadata extraction jobs for all assets to import your existing tags.

## Enable tags feature

You can enable this feature from the [`Account Settings > Features > Tags`](https://my.immich.app/user-settings?isOpen=feature+tags).

<img src={require('./img/tag-enable.webp').default} width="50%" title='Tag view enable' />

## Creating tags

Tags can be created and added to a photo or a video by clicking on the `+ Add` button at the bottom of the info panel.

<img src={require('./img/tag-creation.webp').default} width="40%" title='Tag view enable' />

The tag prompt will appear, and you find and select a tag, or type in a new tag to create it.

<img src={require('./img/tag-form.webp').default} width="40%" title='Tag form' />

## Viewing tags

You can navigate to the `Tags` view from the side navigation bar or by clicking on the tag in the info panel.

<img src={require('./img/tag-view.webp').default} width="80%" title='Tag form' />

---

### Docs Features User Settings (`docs/docs/features/user-settings.md`)

# User Settings

Immich gives each user the ability to manage their own settings. This includes being able to update their profile, toggle certain feature, generate API keys, manage the logged in devices, a view of account usage statistics, and more.

<img src={require('./img/user-settings-3.webp').default} width="60%" title='User settings lists' />

You can access the [user settings](https://my.immich.app/user-settings) by clicking on the user icon on the top right corner of the screen and selecting the `Account Settings` option.

<img src={require('./img/user-settings-1.webp').default} width="33%" title='User settings location 1' />

<br/>

<img src={require('./img/user-settings-2.webp').default} width="33%" title='User settings location 2' />

---

:::tip Reset Password
The admin can reset a user password through the [User Management](/administration/user-management.mdx) screen.
:::

:::tip Reset Admin Password
The admin password can be reset using a [Server Command](/administration/server-commands.md)
:::

---

### Docs Features Xmp Sidecars (`docs/docs/features/xmp-sidecars.md`)

# XMP Sidecars

Immich supports XMP sidecar files — external `.xmp` files that store metadata for an image or video in XML format. During the metadata extraction job Immich will read & import metadata from `.xmp` files, and during the Sidecar Write job it will _write_ metadata back to `.xmp`.

:::tip
Tools like Lightroom, Darktable, digiKam and other applications can also be configured to write changes to `.xmp` files, in order to avoid modifying the original file.
:::

## Metadata Fields

Immich does not support _all_ metadata fields. Below is a table showing what fields Immich can _read_ and _write_. It's important to note that writes do not replace the entire file contents, but are merged together with any existing fields.

:::info
Immich automatically queues a Sidecar Write job after editing the description, rating, or updating tags.
:::

| Metadata        | Immich writes to XMP                             | Immich reads from XMP                                                                                                                                                                                                                          |
| --------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Description** | `dc:description`, `tiff:ImageDescription`        | `dc:description`, `tiff:ImageDescription`                                                                                                                                                                                                      |
| **Rating**      | `xmp:Rating`                                     | `xmp:Rating`                                                                                                                                                                                                                                   |
| **DateTime**    | `exif:DateTimeOriginal`, `photoshop:DateCreated` | In prioritized order:<br/>`exif:SubSecDateTimeOriginal`<br/>`exif:DateTimeOriginal`<br/>`xmp:SubSecCreateDate`<br/>`xmp:CreateDate`<br/>`xmp:CreationDate`<br/>`xmp:MediaCreateDate`<br/>`xmp:SubSecMediaCreateDate`<br/>`xmp:DateTimeCreated` |
| **Location**    | `exif:GPSLatitude`, `exif:GPSLongitude`          | `exif:GPSLatitude`, `exif:GPSLongitude`                                                                                                                                                                                                        |
| **Tags**        | `digiKam:TagsList`                               | In prioritized order: <br/>`digiKam:TagsList`<br/>`lr:HierarchicalSubject`<br/>`IPTC:Keywords`                                                                                                                                                 |

:::note
All other fields (e.g. `Creator`, `Source`, IPTC, Lightroom edits) remain in the `.xmp` file and are **not searchable** in Immich.
:::

## File Naming Rules

A sidecar must share the base name of the media file:

- ✅ `IMG_0001.jpg.xmp` ← preferred
- ✅ `IMG_0001.xmp` ← fallback
- ❌ `myphoto_meta.xmp` ← not recognized

If both `.jpg.xmp` and `.xmp` are present, Immich uses the **`.jpg.xmp`** file.

## CLI Support

1. **Detect** – Immich looks for a `.xmp` file placed next to each media file during upload.
2. **Copy** – Both the media and the sidecar file are copied into Immich’s internal library folder.  
   The sidecar is renamed to match the internal filename template, e.g.:  
   `upload/library/<user>/YYYY/YYYY-MM-DD/IMG_0001.jpg`  
   `upload/library/<user>/YYYY/YYYY-MM-DD/IMG_0001.jpg.xmp`
3. **Extract** – Selected metadata (title, description, date, rating, tags) is parsed from the sidecar and saved to the database.
4. **Write-back** – If you later update tags, rating, or description in the web UI, Immich will update **both** the database _and_ the copied `.xmp` file to stay in sync.

## External Library (Mounted Folder) Support

1. **Detect** – The `DISCOVER` job automatically associates `.xmp` files that sit next to existing media files in your mounted folder. No files are moved or renamed.
2. **Extract** – Immich reads and saves the same metadata fields from the sidecar to the database.
3. **Write-back** – If Immich has **write access** to the mount, any future metadata edits (e.g., rating or tags) are also written back to the original `.xmp` file on disk.

:::danger
If the mount is **read-only**, Immich cannot update either the sidecar **or** the database — **metadata edits will silently fail** with no warning see issue [#10538](https://github.com/immich-app/immich/issues/10538) for more details.
:::

## Admin Jobs

Immich provides two admin jobs for managing sidecars:

| Job        | What it does                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------- |
| `DISCOVER` | Finds new `.xmp` files next to media that don’t already have one linked                           |
| `SYNC`     | Re-reads existing `.xmp` files and refreshes metadata in the database (e.g. after external edits) |

---

### Docs Guides Better Facial Clusters (`docs/docs/guides/better-facial-clusters.md`)

# Better Facial Recognition Clusters

## Purpose

This guide explains how to optimize facial recognition in systems with large image libraries. By following these steps, you'll achieve better clustering of faces, reducing the need for manual merging.

---

## Important Notes

- **Best Suited For:** Large image libraries after importing a significant number of images.
- **Warning:** This method deletes all previously assigned names.
- **Tip:** **Always take a [backup](/administration/backup-and-restore#database) before proceeding!**

---

## Step-by-Step Instructions

### Objective

To enhance face clustering and ensure the model effectively identifies faces using qualitative initial data.

---

### Steps

#### 1. Adjust Machine Learning Settings

Navigate to:  
**Admin → Administration → Settings → Machine Learning Settings**

Make the following changes:

- **Maximum recognition distance (Optional):**  
  Lower this value, e.g., to **0.4**, if the library contains people with similar facial features.
- **Minimum recognized faces:**  
  Set this to a **high value** (e.g., 20 For libraries with a large amount of assets (~100K+), and 10 for libraries with medium amount of assets (~40K+)).
  > A high value ensures clusters only include faces that appear at least 20/`value` times in the library, improving the initial clustering process.

---

#### 2. Run Reset Jobs

Go to:  
**Administration → Job Queues**

Perform the following:

1. **FACIAL RECOGNITION → Reset**

> These reset jobs rebuild the recognition model based on the new settings.

---

#### 3. Refine Recognition with Lower Thresholds

Once the reset jobs are complete, refine the recognition as follows:

- **Step 1:**  
  Return to **Minimum recognized faces** in Machine Learning Settings and lower the value to **10** (In medium libraries we will lower the value from 10 to 5).

  > Run the job: **FACIAL RECOGNITION → MISSING Mode**

- **Step 2:**  
  Lower the value again to **3**.
  > Run the job: **FACIAL RECOGNITION → MISSING Mode**

:::tip try different values
For certain libraries with a larger or smaller amount of assets, other settings will be better or worse. It is recommended to try different values **​​before assigning names** and see which settings work best for your library.
:::

---

---

### Docs Guides Custom Locations (`docs/docs/guides/custom-locations.md`)

# Files Custom Locations

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

This guide explains how to store generated and raw files with docker's volume mount in different locations.

<ComposeBuilder query="storage.customFolders=true" />

:::caution Backup
It is important to remember to update the backup settings after following the guide to back up the new backup paths if using automatic backup tools, especially `profile/`.
:::

In our `.env` file, we will define the paths we want to use. Note that you don't have to define all of these: UPLOAD_LOCATION will be the base folder that files are stored in by default, with the other paths acting as overrides.

```diff title=".env"
# You can find documentation for all the supported environment variables [here](/install/environment-variables)

# Custom location where your uploaded, thumbnails, and transcoded video files are stored
- UPLOAD_LOCATION=./library
+ UPLOAD_LOCATION=/custom/path/immich/immich_files
+ THUMB_LOCATION=/custom/path/immich/thumbs
+ ENCODED_VIDEO_LOCATION=/custom/path/immich/encoded-video
+ PROFILE_LOCATION=/custom/path/immich/profile
+ BACKUP_LOCATION=/custom/path/immich/backups
...
```

After defining the locations of these files, we will edit the `docker-compose.yml` file accordingly and add the new variables to the `immich-server` container. These paths are where the mount attaches inside of the container, so don't change those.

```diff title="docker-compose.yml"
services:
  immich-server:
      volumes:
      - ${UPLOAD_LOCATION}:/data
+     - ${THUMB_LOCATION}:/data/thumbs
+     - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video
+     - ${PROFILE_LOCATION}:/data/profile
+     - ${BACKUP_LOCATION}:/data/backups
      - /etc/localtime:/etc/localtime:ro
```

After making this change, you have to move the files over to the new folders to make sure Immich can find everything it needs. If you haven't uploaded anything important yet, you can also reset Immich entirely by deleting the database folder.
Then restart Immich to register the changes:

```
docker compose up -d
```

:::note
Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device.
For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder.

The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated.

The storage metrics of the Immich server will track available storage at `UPLOAD_LOCATION`, so the administrator must set up some sort of monitoring to ensure the storage does not run out of space. The `profile/` folder is much smaller, usually less than 1 MB.
:::

---

### Docs Guides Custom Map Styles (`docs/docs/guides/custom-map-styles.md`)

# Custom Map Styles

You may decide that you'd like to modify the style document which is used to
draw the maps in Immich. In addition to visual customization, this also allows
you to pick your own map tile provider instead of the default one. The default
`style.json` for [light theme](https://tiles.immich.cloud/v1/style/light.json)
and [dark theme](https://tiles.immich.cloud/v1/style/dark.json)
can be used as a basis for creating your own style.

There are several sources for already-made `style.json` map themes, as well as
online generators you can use.

1. In **Immich**, navigate to **Administration --> Settings --> Map & GPS Settings** and expand the **Map Settings** subsection.
2. Paste the link to your JSON style in either the **Light Style** or **Dark Style**. (You can add different styles which will help make the map style more appropriate depending on whether you set **Immich** to Light or Dark mode.)
3. Save your selections. Reload the map, and enjoy your custom map style!

## Use MapTiler to build a custom style

Customizing the map style can be done easily using MapTiler, if you do not want to write an entire JSON document by hand.

1. Create a free account at https://cloud.maptiler.com
2. Once logged in, you can either create a brand new map by clicking on **New Map**, selecting a starter map, and then clicking **Customize**, OR by selecting a **Standard Map** and customizing it from there.
3. The **editor** interface is self-explanatory. You can change colors, remove visible layers, or add optional layers (e.g., administrative, topo, hydro, etc.) in the composer.
4. Once you have your map composed, click on **Save** at the top right. Give it a unique name to save it to your account.
5. Next, **Publish** your style using the **Publish** button at the top right. This will deploy it to production, which means it is able to be exposed over the Internet. MapTiler will present an interactive side-by-side map with the original and your changes prior to publication.<br/>
6. MapTiler will warn you that changing the map will change it across all apps using the map. Since no apps are using the map yet, this is okay.
7. Clicking on the name of your new map at the top left will bring you to the item's **details** page. From here, copy the link to the JSON style under **Use vector style**. This link will automatically contain your personal API key to MapTiler.

---

### Docs Guides Database Gui (`docs/docs/guides/database-gui.md`)

# Database GUI

A short guide on connecting [pgAdmin](https://www.pgadmin.org/) to Immich.

## 1. Install pgAdmin

Add a file `docker-compose-pgadmin.yml` next to your `docker-compose.yml` with the following content:

```
name: immich

services:
  pgadmin:
    image: dpage/pgadmin4
    container_name: pgadmin4_container
    restart: always
    ports:
      - "8888:80"
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@example.com
      PGADMIN_DEFAULT_PASSWORD: strong-password
    volumes:
      - pgadmin-data:/var/lib/pgadmin

volumes:
  pgadmin-data:
```

Change the values of `PGADMIN_DEFAULT_EMAIL` and `PGADMIN_DEFAULT_PASSWORD` in this file.

Run `docker compose -f docker-compose.yml -f docker-compose-pgadmin.yml up` to start immich along with `pgAdmin`.

## 2. Add a Server

Open [localhost:8888](http://localhost:8888) and login with the default credentials from above.

Right click on `Servers` and click on `Register >> Server..` then enter the values below in the `Connection` tab.

<img src={require('./img/pgadmin-add-new-server.webp').default} width="50%" title="new server option" />

:::note
The parameters used here match those specified in the example `.env` file. If you have changed your `.env` file, you'll need to adjust accordingly.
:::

| Name                 | Value             |
| -------------------- | ----------------- |
| Host name/address    | `immich_postgres` |
| Port                 | `5432`            |
| Maintenance database | `immich`          |
| Username             | `postgres`        |
| Password             | `postgres`        |

Click on "Save" to connect to the Immich database.

---

### Docs Guides Database Queries (`docs/docs/guides/database-queries.md`)

# Database Queries

:::danger
Keep in mind that mucking around in the database might set the Moon on fire. Avoid modifying the database directly when possible, and always have current backups.
:::

:::tip
Run `docker exec -it immich_postgres psql --dbname=<DB_DATABASE_NAME> --username=<DB_USERNAME>` to connect to the database via the container directly.

(Replace `<DB_DATABASE_NAME>` and `<DB_USERNAME>` with the values from your [`.env` file](/install/environment-variables#database)).
:::

## Assets

### Name

:::note
The `"originalFileName"` column is the name of the file at time of upload, including the extension.
:::

```sql title="Find by original filename"
SELECT * FROM "asset" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg';
SELECT * FROM "asset" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_
SELECT * FROM "asset" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle
```

```sql title="Find by path"
SELECT * FROM "asset" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg';
SELECT * FROM "asset" WHERE "originalPath" LIKE 'upload/library/admin/2023/%';
```

### ID

```sql title="Find by ID"
SELECT * FROM "asset" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9';
```

```sql title="Find by partial ID"
SELECT * FROM "asset" WHERE "id"::text LIKE '%ab431d3a%';
```

### Checksum

:::note
You can calculate the checksum for a particular file by using the command `sha1sum <filename>`.
:::

```sql title="Find by checksum (SHA-1)"
SELECT encode("checksum", 'hex') FROM "asset";
SELECT * FROM "asset" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex');
SELECT * FROM "asset" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation
```

```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)"
SELECT T1."checksum", array_agg(T2."id") ids FROM "asset" T1
  INNER JOIN "asset" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL
  WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum";
```

### Metadata

```sql title="Live photos"
SELECT * FROM "asset" WHERE "livePhotoVideoId" IS NOT NULL;
```

```sql title="By description"
SELECT "asset".*, "asset_exif"."description" FROM "asset_exif"
  JOIN "asset" ON "asset"."id" = "asset_exif"."assetId"
  WHERE TRIM("asset_exif"."description") <> ''; -- all files with a description
SELECT "asset".*, "asset_exif"."description" FROM "asset_exif"
  JOIN "asset" ON "asset"."id" = "asset_exif"."assetId"
  WHERE "asset_exif"."description" ILIKE '%string to match%'; -- search by string
```

```sql title="Without metadata"
SELECT "asset".* FROM "asset_exif"
  LEFT JOIN "asset" ON "asset"."id" = "asset_exif"."assetId"
  WHERE "asset_exif"."assetId" IS NULL;
```

```sql title="size < 100,000 bytes, smallest to largest"
SELECT * FROM "asset"
  JOIN "asset_exif" ON "asset"."id" = "asset_exif"."assetId"
  WHERE "asset_exif"."fileSizeInByte" < 100000
  ORDER BY "asset_exif"."fileSizeInByte" ASC;
```

### Type

```sql title="By type"
SELECT * FROM "asset" WHERE "asset"."type" = 'VIDEO';
SELECT * FROM "asset" WHERE "asset"."type" = 'IMAGE';
```

```sql title="Count by type"
SELECT "asset"."type", COUNT(*) FROM "asset" GROUP BY "asset"."type";
```

```sql title="Count by type (per user)"
SELECT "user"."email", "asset"."type", COUNT(*) FROM "asset"
  JOIN "user" ON "asset"."ownerId" = "user"."id"
  GROUP BY "asset"."type", "user"."email" ORDER BY "user"."email";
```

## Tags

```sql title="Count by tag"
SELECT "t"."value" AS "tag_name", COUNT(*) AS "number_assets" FROM "tag" "t"
  JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagId" JOIN "asset" "a" ON "ta"."assetId" = "a"."id"
  WHERE "a"."visibility" != 'hidden'
  GROUP BY "t"."value" ORDER BY "number_assets" DESC;
```

```sql title="Count by tag (per user)"
SELECT "t"."value" AS "tag_name", "u"."email" as "user_email", COUNT(*) AS "number_assets" FROM "tag" "t"
  JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagId" JOIN "asset" "a" ON "ta"."assetId" = "a"."id" JOIN "user" "u" ON "a"."ownerId" = "u"."id"
  WHERE "a"."visibility" != 'hidden'
  GROUP BY "t"."value", "u"."email" ORDER BY "number_assets" DESC;
```

## Users

```sql title="List all users"
SELECT * FROM "user";
```

```sql title="Get owner info from asset ID"
SELECT "user".* FROM "user" JOIN "asset" ON "user"."id" = "asset"."ownerId" WHERE "asset"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f';
```

## Persons

```sql title="Delete person and unset it for the faces it was associated with"
DELETE FROM "person" WHERE "name" = 'PersonNameHere';
```

## System

### Config

```sql title="Custom settings"
SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config';
```

(Only used when not using the [config file](/install/config-file))

### File properties

```sql title="Without thumbnails"
SELECT * FROM "asset"
WHERE (NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'thumbnail')
    OR NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'preview'))
AND "asset"."visibility" = 'timeline';
```

```sql title="Failed file movements"
SELECT * FROM "move_history";
```

## Postgres internal

```sql title="Change DB_PASSWORD"
ALTER USER <DB_USERNAME> WITH ENCRYPTED PASSWORD 'newpasswordhere';
```

---

### Docs Guides Docker Help (`docs/docs/guides/docker-help.md`)

# Docker Help

## Containers

```bash
docker ps                         # see a list of running containers
docker ps -a                      # see a list of running and stopped containers
```

## Attach to a Container

```bash
docker exec -it <id or name> <command>          # attach to a container with a command
docker exec -it immich_server bash
docker exec -it immich_machine_learning bash
```

## Logs

```bash
docker logs <id or name>          # see the logs for a specific container (by id or name)

docker logs immich_server
docker logs immich_machine_learning
```

:::tip Follow a log
Adding `--follow` to a `docker logs <id or name>` command will stream new logs, instead of immediately exiting, which is often useful for debugging.
:::

---

### Docs Guides External Library (`docs/docs/guides/external-library.md`)

# External Library

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

This guide walks you through adding an [External Library](/features/libraries).
This guide assumes you are running Immich in Docker and that the files you wish to access are stored
in a directory on the same machine.

# Mount the directory into the containers.

<ComposeBuilder query="storage.externalLibraries.0.path=&storage.externalLibraries.0.readOnly=true" />

Edit `docker-compose.yml` to add one or more new mount points in the section `immich-server:` under `volumes:`.
If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/features/xmp-sidecars)), remove `:ro` from the end of the mount point.

```diff
immich-server:
    volumes:
        - ${UPLOAD_LOCATION}:/data
+       - /home/user/photos1:/home/user/photos1:ro
+       - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two
```

Restart Immich by running `docker compose up -d`.

# Create the library

:::info
External library management requires administrator access and the steps below assume you are using an admin account.
:::

In the Immich web UI:

- click the **Administration** link in the upper right corner.
  <img src={require('./img/administration-link.webp').default} width="50%" title="Administration link" />

- Select the **External Libraries** tab and click the **Create Library** button
  <img src={require('./img/create-external-library.webp').default} width="80%" title="Create Library button" />

- In the dialog, select which user should own the new library
  <img src={require('./img/library-owner.webp').default} width="50%" title="Library owner dialog" />

- You are now entering the library management page.
  <img src={require('./img/library-management-page.webp').default} width="80%" title="Library management page" />

- Click `Add` in the Folder section to specify a path for scanning and enter **/home/user/photos1** as the path and click Add
  <img src={require('./img/edit-import-path.webp').default} width="50%" title="Add an import path" />

- Click the three-dots menu and select **Scan New Library Files**
  <img src={require('./img/scan-new-library-files.webp').default} width="50%" title="Scan New Library Files menu option" />

# Confirm stuff is happening

- Click **Administration**
  <img src={require('./img/administration-link.webp').default} width="50%" title="Administration link" />

- Select the **Jobs** tab
  <img src={require('./img/jobs-tab.webp').default} width="50%" title="Jobs tab" />

- You should see non-zero Active jobs for
  Library, Generate Thumbnails, and Extract Metadata.

---

### Docs Guides Python File Upload (`docs/docs/guides/python-file-upload.md`)

# Python File Upload

```python
#!/usr/bin/python3

import requests
import os
from datetime import datetime

API_KEY = 'YOUR_API_KEY'                # replace with a valid api key
BASE_URL = 'http://127.0.0.1:2283/api'  # replace as needed


def upload(file):
    stats = os.stat(file)

    headers = {
        'Accept': 'application/json',
        'x-api-key': API_KEY
    }

    data = {
        'fileCreatedAt': datetime.fromtimestamp(stats.st_mtime),
        'fileModifiedAt': datetime.fromtimestamp(stats.st_mtime),
        'isFavorite': 'false',
    }

    files = {
        'assetData': open(file, 'rb')
    }

    response = requests.post(
        f'{BASE_URL}/assets', headers=headers, data=data, files=files)

    print(response.json())
    # {'id': 'ef96f635-61c7-4639-9e60-61a11c4bbfba', 'duplicate': False}


upload('./test.jpg')
```

---

### Docs Guides Remote Access (`docs/docs/guides/remote-access.md`)

# Remote Access

This page gives a few pointers on how to access your Immich instance from outside your LAN.
You can read the [full discussion in Discord](https://discord.com/channels/979116623879368755/1122615710846308484)

:::danger
Never forward port 2283 directly to the internet without additional configuration. This will expose the web interface via http to the internet, making you susceptible to [man in the middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) attacks.
:::

## Option 1: VPN to home network

You may use a VPN service to open an encrypted connection to your Immich instance. OpenVPN and Wireguard are two popular VPN solutions. Here is a guide on setting up VPN access to your server - [Pihole documentation](https://docs.pi-hole.net/guides/vpn/wireguard/overview/)

### Pros

- Simple to set up and very secure.
- Single point of potential failure, i.e., the VPN software itself. Even if there is a zero-day vulnerability on Immich, you will not be at risk.
- Both Wireguard and OpenVPN are independently security-audited, so the risk of serious zero-day exploits are minimal.

### Cons

- If you don't have a static IP address, you would need to set up a [Dynamic DNS](https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/). [DuckDNS](https://www.duckdns.org/) is a free DDNS provider.
- VPN software needs to be installed and active on both server-side and client-side.
- Requires you to open a port on your router to your server.

## Option 2: Tailscale

If you are unable to open a port on your router for Wireguard or OpenVPN to your server, [Tailscale](https://tailscale.com/) is a good option. Tailscale mediates a peer-to-peer wireguard tunnel between your server and remote device, even if one or both of them are behind a [NAT firewall](https://en.wikipedia.org/wiki/Network_address_translation).

:::tip Video tutorial
You can learn how to set up Tailscale together with Immich with the [tutorial video](https://www.youtube.com/watch?v=Vt4PDUXB_fg) they created.
:::

### Pros

- Minimal configuration needed on server and client sides.
- You are protected against zero-day vulnerabilities on Immich.

### Cons

- The Tailscale client usually needs to run as root on your devices and it increases the attack surface slightly compared to a minimal Wireguard server. e.g., an [RCE vulnerability](https://github.com/tailscale/tailscale/security/advisories/GHSA-vqp6-rc3h-83cp) was discovered in the Windows Tailscale client in November 2022.
- Tailscale is a paid service. However, there is a generous [free tier](https://tailscale.com/pricing/) suitable for personal use.
- Tailscale needs to be installed and running on both server-side and client-side.

## Option 3: Reverse Proxy

A reverse proxy is a service that sits between web servers and clients. A reverse proxy can either be hosted on the server itself or remotely. Clients can connect to the reverse proxy via https, and the proxy relays data to Immich. This setup makes most sense if you have your own domain and want to access your Immich instance just like any other website, from outside your LAN. You can also use a DDNS provider like DuckDNS or no-ip if you don't have a domain. This configuration allows the Immich Android and iphone apps to connect to your server without a VPN or tailscale app on the client side.

If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/administration/reverse-proxy.md).

You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accessible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser.

A remote reverse proxy like [Cloudflare](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) increases security by hiding the server IP address, which makes targeted attacks like [DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/) harder.

### Pros

- No additional software needs to be installed client-side
- If you only need access to the web interface remotely, it is possible to set up access controls that shield you from zero-day vulnerabilities on Immich. [Cloudflare Access](https://www.cloudflare.com/zero-trust/products/access/) has a generous free tier.

### Cons

- Complex configuration
- Depending on your configuration, both the Immich web interface and API may be exposed to the internet. Immich is under very active development and the existence of severe security vulnerabilities cannot be ruled out.

---

### Docs Guides Remote Machine Learning (`docs/docs/guides/remote-machine-learning.md`)

# Remote Machine Learning

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';

To alleviate [performance issues on low-memory systems](/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user.

:::info
Smart Search and Face Detection will use this feature, but Facial Recognition will not. This is because Facial Recognition uses the _outputs_ of these models that have already been saved to the database. As such, its processing is between the server container and the database.
:::

:::danger
Image previews are sent to the remote machine learning container. Use this option carefully when running this on a public computer or a paid processing cloud. Additionally, as an internal service, the machine learning container has no security measures whatsoever. Please be mindful of where it's deployed and who can access it.
:::

1. Ensure the remote server has Docker installed
2. Copy the following `docker-compose.yml` to the remote server

:::info
If using hardware acceleration, the [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be added and the `docker-compose.yml` needs to be configured as described in the [hardware acceleration documentation](/features/ml-hardware-acceleration)
:::

```yaml
name: immich_remote_ml

services:
  immich-machine-learning:
    container_name: immich_machine_learning
    # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
    # Example tag: ${IMMICH_VERSION:-release}-cuda
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    # extends:
    #   file: hwaccel.ml.yml
    #   service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
    volumes:
      - model-cache:/cache
    restart: always
    ports:
      - 3003:3003

volumes:
  model-cache:
```

3. Start the remote machine learning container by running `docker compose up -d`

:::info
Version mismatches between both hosts may cause bugs and instability, so remember to update this container as well when updating the local Immich instance.
:::

4. Navigate to the [Machine Learning Settings](https://my.immich.app/admin/system-settings?isOpen=machine-learning)
5. Click _Add URL_
6. Fill the new field with the URL to the remote machine learning container, e.g. `http://ip:port`

## Forcing remote processing

Adding a new URL to the settings is recommended over replacing the existing URL (http://immich-machine-learning:3003). This is because it will allow machine learning tasks to be processed successfully when the remote server is down by falling back to the local machine learning container. If you do not want machine learning tasks to be processed locally when the remote server is not available, you can instead replace the existing URL and only provide the remote container's URL. If doing this, you can remove the `immich-machine-learning` section of the local `docker-compose.yml` file to save resources, as this service will never be used.

<ComposeBuilder query="machineLearning.external=true" />

Do note that this will mean that Smart Search and Face Detection jobs will fail to be processed when the remote instance is not available. This in turn means that tasks dependent on these features—Duplicate Detection and Facial Recognition—will not run for affected assets. If this occurs, you must manually click the _Missing_ button next to Smart Search and Face Detection in the [Job Status](http://my.immich.app/admin/queues) page for the jobs to be retried.

## Load balancing

While several URLs can be provided in the settings, they are tried sequentially; there is no attempt to distribute load across multiple containers. It is recommended to use a dedicated load balancer for such use-cases and specify it as the only URL. Among other things, it may enable the use of different APIs on the same server by running multiple containers with different configurations. For example, one might run an OpenVINO container in addition to a CUDA container, or run a standard release container to maximize both CPU and GPU utilization.

:::tip
The machine learning container can be shared among several Immich instances regardless of the models a particular instance uses. However, using different models will lead to higher peak memory usage.
:::

---

### Docs Guides Scaling Immich (`docs/docs/guides/scaling-immich.md`)

# Scaling Immich

Immich is built with modern deployment practices in mind, and the backend is designed to be able to run multiple instances in parallel. When doing this, the only requirement you need to be aware of is that every instance needs to be connected to the shared infrastructure. That means they should all have access to the same Postgres and Redis instances, and have the same files mounted into the containers.

Scaling can be useful for many reasons. Maybe you have a gaming PC that you want to use for transcoding and thumbnail generation, or perhaps you run a Kubernetes cluster across a handful of powerful servers that you want to make use of.

:::info
If you only have a single machine to run Immich on, scaling to multiple containers is unlikely to provide any benefit. An Immich container will run multiple background tasks at once, and you can increase their number from the admin panel.
:::

The details of how to scale across multiple machines will vary widely between different environments and require some knowledge to set up, and as such this guide gives no specific instructions. In some cases scaling up can be as easy as incrementing the amount of replicas on a Kubernetes deployment, in others it might need you to configure network tunnels or NFS mounts. The details are left as an exercise for the reader ;)

## Workers

By default, each running `immich-server` container comes with multiple internal workers. If you're scaling up only to handle more background tasks, you can choose to disable the worker responsible for the API. See [workers](../administration/jobs-workers.md) for more detail.

## Scaling down

In the same way you can scale up to multiple containers, you can also choose to scale down. All state is stored in Postgres, Redis, and the filesystem so there is no risk in stopping a running immich-server container, for example if you want to use your GPU to play some games. As long as there is an API worker running you will still be able to browse Immich, and jobs will wait to be processed until there is a worker available for them.

---

### Docs Guides Smtp Gmail (`docs/docs/guides/smtp-gmail.md`)

# SMTP settings using Gmail

This guide walks you through how to get the information you need to set up your Immich instance to send emails using Gmail's SMTP server.

## Create an app password

From your Google account settings

- Add [2-Step Verification](https://support.google.com/accounts/answer/185839) to your Google account (Required)
- [Create an app password](https://myaccount.google.com/apppasswords).

At the end of creating your app passwords, a password will be displayed; save it, it will be used for the password field when setting up the SMTP server in Immich.

<img src={require('./img/google-app-password.webp').default} title="Authorised redirect URIs" />

## Entering the SMTP credential in Immich

Entering your credential in Immich's email notification settings at `Administration -> Settings -> Notification Settings`

<img src={require('./img/email-settings.webp').default} width="80%" title="SMTP settings" />

---

### Docs Guides Smtp Microsoft365 (`docs/docs/guides/smtp-microsoft365.md`)

# SMTP settings using Microsoft 365

:::info Deprecated by Microsoft
Microsoft has announced the [deprecation](https://techcommunity.microsoft.com/blog/exchange/updated-exchange-online-smtp-auth-basic-authentication-deprecation-timeline/4489835) of SMTP Basic Auth.
We no longer recommend using this setup method. Alternate methods are available but not within the scope of the Immich docs.
:::

This guide walks you through how to get the information you need to set up your Immich instance to send emails using Microsoft's SMTP server.

## Create an app password

You will need to generate an app password to use your Microsoft email in Immich. Depending on if you have a personal or business account, you can use https://go.microsoft.com/fwlink/?linkid=2274139 or https://myaccount.microsoft.com/securtiy-info respectively.

## Entering the SMTP credential in Immich

Entering your credential in Immich's email notification settings at `Administration -> Settings -> Notification Settings`

Host: smtp-mail.outlook.com
Port: 587
username: your mail address
Password: app password you created earlier
SMTPS: set it to disabled

<img src={require('./img/email-ms-settings.webp').default} width="80%" title="SMTP settings" />

---

### Docs Guides Template Backup Script (`docs/docs/guides/template-backup-script.md`)

# Backup Script

[Borg](https://www.borgbackup.org/) is a feature-rich deduplicating archiving software with built-in versioning. We provide a template bash script that can be run daily/weekly as a [cron](https://wiki.archlinux.org/title/cron) job to back up your files and database. We encourage you to read the quick-start guide for Borg before running this script.

This script assumes you have a second hard drive connected to your server for on-site backup and ssh access to a remote machine for your third off-site copy. [BorgBase](https://www.borgbase.com/) is an alternative option for off-site backups with a competitive pricing structure. You may choose to skip off-site backups entirely by removing the relevant lines from the template script.

The database is saved to your Immich upload folder in the `database-backup` subdirectory. The database is then backed up and versioned with your assets by Borg. This ensures that the database backup is in sync with your assets in every snapshot.

:::info
This script makes backups of your database along with your photo/video library. This is redundant with the [automatic database backup tool](/administration/backup-and-restore#automatic-database-dumps) built into Immich. Using this script to backup your database has two advantages over the built-in backup tool:

- This script uses storage more efficiently by versioning your backups instead of making multiple copies.
- The database backups are performed at the same time as the library backup, ensuring that the backups of your database and the library are always in sync.

If you are using this script, it is therefore safe to turn off the built-in automatic database backups from your admin panel to save storage space.
:::

### Prerequisites

- Borg needs to be installed on your server as well as the remote machine. You can find instructions to install Borg [here](https://borgbackup.readthedocs.io/en/latest/installation.html).
- (Optional) To run this script as a non-root user, you should [add your username to the docker group](https://docs.docker.com/engine/install/linux-postinstall/).
- To run this script non-interactively, set up [passwordless ssh](https://www.redhat.com/sysadmin/passwordless-ssh) to your remote machine from your server. If you skipped the previous step, make sure this step is done from your root account.

To initialize the borg repository, run the following commands once.

```bash title='Borg set-up'
UPLOAD_LOCATION="/path/to/immich/directory"       # Immich database location, as set in your .env file
BACKUP_PATH="/path/to/local/backup/directory"

mkdir "$UPLOAD_LOCATION/database-backup"
borg init --encryption=none "$BACKUP_PATH/immich-borg"

## Remote set up
REMOTE_HOST="remote_host@IP"
REMOTE_BACKUP_PATH="/path/to/remote/backup/directory"

borg init --encryption=none "$REMOTE_HOST:$REMOTE_BACKUP_PATH/immich-borg"
```

Edit the following script as necessary and add it to your crontab. Note that this script assumes there are no `:`, `@`, or `"` characters in your paths. If these characters exist, you will need to escape and/or rename the paths.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Restoring

To restore from a backup, use the `borg mount` command.

```bash title='Restore from local backup'
BACKUP_PATH="/path/to/local/backup/directory"
mkdir /tmp/immich-mountpoint
borg mount "$BACKUP_PATH"/immich-borg /tmp/immich-mountpoint
cd /tmp/immich-mountpoint
```

```bash title='Restore from remote backup'
REMOTE_HOST="remote_host@IP"
REMOTE_BACKUP_PATH="/path/to/remote/backup/directory"
mkdir /tmp/immich-mountpoint
borg mount "$REMOTE_HOST:$REMOTE_BACKUP_PATH"/immich-borg /tmp/immich-mountpoint
cd /tmp/immich-mountpoint
```

You can find available snapshots in separate sub-directories at `/tmp/immich-mountpoint`. Restore the files you need, and unmount the Borg repository using `borg umount /tmp/immich-mountpoint`

---

### Docs Install All In One (`docs/docs/install/all-in-one.md`)

---
sidebar_position: 70
---

# All-In-One [Community]

:::note
This is a community contribution and not officially supported by the Immich team, but included here for convenience.

**Please report issues to the corresponding [Github Repository][github].**
:::

## Installation

For installation instructions, refer to the [Github Repository][github].

## Issues

For issues, open an issue on the associated [GitHub Repository][github].

[github]: https://github.com/imagegenius/docker-immich/

---

### Docs Install Config File (`docs/docs/install/config-file.md`)

---
sidebar_position: 100
---

# Config File

A config file can be provided as an alternative to the UI configuration.

:::note Interaction with the web UI
While the config file does not need to include all keys from the below example, specifying `IMMICH_CONFIG_FILE` will disable the ability to edit other properties from the Immich web UI.
:::

### Step 1 - Create a new config file

In JSON format, create a new config file (e.g. `immich-config.json`) and put it in a location mounted in the container that can be accessed by Immich.
YAML-formatted config files are also supported.
The default configuration looks like this:

<details>
<summary>immich-config.json</summary>

```
/* Detailed source-code truncated for AI context efficiency. */
```

</details>

:::tip
In Administration > Settings is a button to copy the current configuration to your clipboard.
So you can just grab it from there, paste it into a file and you're pretty much good to go.
:::

### Step 2 - Specify the file location

:::note
If you have any `microservices` workers, they will also need to have the config file mounted to their container.
:::

In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config.
For more information, refer to the [Environment Variables](/install/environment-variables.md) section.

:::info Docker Compose
In your `.env` file, the variables `UPLOAD_LOCATION` and `DB_DATA_LOCATION` concern the location on the host.
However, the variable `IMMICH_CONFIG_FILE` concerns the location inside the container, and informs the `immich-server` container that a configuration file is present.

It is recommended to reuse this variable in your `docker-compose.yml`:

```yaml
volumes:
  - ./immich-config.json:${IMMICH_CONFIG_FILE}
```

:::

---

### Docs Install Docker Compose (`docs/docs/install/docker-compose.mdx`)

---
sidebar_position: 30
---

# Docker Compose [Recommended]

Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose.

import ComposeBuilder from '/docs/partials/_compose-builder.mdx';
import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';

<ComposeBuilder />

<DockerComposeSteps />

:::info Docker version
If you get an error such as `unknown shorthand flag: 'd' in -d` or `open <location of your .env file>: permission denied`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by following the complete [Docker Engine install](https://docs.docker.com/engine/install/) procedure for your distribution, crucially the "Uninstall old versions" and "Install using the apt/rpm repository" sections. These replace the distro's Docker packages with Docker's official ones.

Note that the correct command really is `docker compose`, not `docker-compose`. If you try the latter on vanilla Ubuntu 22.04, it will fail in a different way:

```
The Compose file './docker-compose.yml' is invalid because:
'name' does not match any of the regexes: '^x-'
```

See the previous paragraph about installing from the official Docker repository.
:::

:::info Health check start interval
If you get an error `can't set healthcheck.start_interval as feature require Docker Engine v25 or later`, it helps to comment out the line for `start_interval` in the `database` section of the `docker-compose.yml` file.
:::

## Next Steps

Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instructions](/install/upgrading.md).

---

### Docs Install Environment Variables (`docs/docs/install/environment-variables.md`)

---
sidebar_position: 90
---

# Environment Variables

:::caution

To change environment variables, you must recreate the Immich containers.
Just restarting the containers does not replace the environment within the container!

In order to recreate the container using docker compose, run `docker compose up -d`.
In most cases docker will recognize that the `.env` file has changed and recreate the affected containers.
If this does not work, try running `docker compose up -d --force-recreate`.

:::

## Docker Compose

| Variable           | Description                     | Default | Containers               |
| :----------------- | :------------------------------ | :-----: | :----------------------- |
| `IMMICH_VERSION`   | Image tags                      |  `v3`   | server, machine learning |
| `UPLOAD_LOCATION`  | Host path for uploads           |         | server                   |
| `DB_DATA_LOCATION` | Host path for Postgres database |         | database                 |

:::tip
These environment variables are used by the `docker-compose.yml` file and do **NOT** affect the containers directly.
:::

## General

| Variable                            | Description                                                                                                                                                          |           Default            | Containers               | Workers            |
| :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- |
| `TZ`                                | Timezone                                                                                                                                                             |        <sup>\*1</sup>        | server                   | microservices      |
| `IMMICH_ENV`                        | Environment (production, development)                                                                                                                                |         `production`         | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL`                  | Log level (verbose, debug, log, warn, error)                                                                                                                         |            `log`             | server, machine learning | api, microservices |
| `IMMICH_LOG_FORMAT`                 | Log output format (`console`, `json`)                                                                                                                                |          `console`           | server                   | api, microservices |
| `IMMICH_MEDIA_LOCATION`             | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️                                                                            |           `/data`            | server                   | api, microservices |
| `IMMICH_CONFIG_FILE`                | Path to config file                                                                                                                                                  |                              | server                   | api, microservices |
| `IMMICH_HELMET_FILE`                | Path to a json file with [helmet](https://www.npmjs.com/package/helmet) options. Set to `false` to disable. Set to `true` to use `server/helmet.json`<sup>\*3</sup>. |           `false`            | server                   | api                |
| `NO_COLOR`                          | Set to `true` to disable color-coded log output                                                                                                                      |           `false`            | server, machine learning |                    |
| `CPU_CORES`                         | Number of cores available to the Immich server                                                                                                                       | auto-detected CPU core count | server                   |                    |
| `IMMICH_API_METRICS_PORT`           | Port for the OTEL metrics                                                                                                                                            |            `8081`            | server                   | api                |
| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics                                                                                                                                            |            `8082`            | server                   | microservices      |
| `IMMICH_PROCESS_INVALID_IMAGES`     | When `true`, generate thumbnails for invalid images                                                                                                                  |                              | server                   | microservices      |
| `IMMICH_TRUSTED_PROXIES`            | List of comma-separated IPs set as trusted proxies                                                                                                                   |                              | server                   | api                |
| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS`  | See [System Integrity](/administration/system-integrity)                                                                                                             |                              | server                   | api, microservices |
| `IMMICH_ALLOW_SETUP`                | When `false` disables the `/auth/admin-sign-up` and `/admin/database-backups/start-restore` endpoints                                                                |            `true`            | server                   | api                |

\*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`.
`TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution.

\*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead.

\*3: The [default configuration](https://helmetjs.github.io/#content-security-policy) sets `upgrade-insecure-requests`, which tells the browser to upgrade all requests to HTTPS. This breaks on HTTP-only deployments. If you cannot use HTTPS, you should use a custom helmet config file with `"upgrade-insecure-requests": null`.

## Workers

| Variable                 | Description                                                                                          | Default | Containers |
| :----------------------- | :--------------------------------------------------------------------------------------------------- | :-----: | :--------- |
| `IMMICH_WORKERS_INCLUDE` | Only run these workers.                                                                              |         | server     |
| `IMMICH_WORKERS_EXCLUDE` | Do not run these workers. Matches against default workers, or `IMMICH_WORKERS_INCLUDE` if specified. |         | server     |

:::info
Information on the current workers can be found [here](/administration/jobs-workers).
:::

## Ports

| Variable      | Description    |                  Default                   | Containers               |
| :------------ | :------------- | :----------------------------------------: | :----------------------- |
| `IMMICH_HOST` | Listening host |                 `0.0.0.0`                  | server, machine learning |
| `IMMICH_PORT` | Listening port | `2283` (server), `3003` (machine learning) | server, machine learning |

## Database

| Variable                            | Description                                                                            |  Default   | Containers                     |
| :---------------------------------- | :------------------------------------------------------------------------------------- | :--------: | :----------------------------- |
| `DB_URL`                            | Database URL                                                                           |            | server                         |
| `DB_HOSTNAME`                       | Database host                                                                          | `database` | server                         |
| `DB_PORT`                           | Database port                                                                          |   `5432`   | server                         |
| `DB_USERNAME`                       | Database user                                                                          | `postgres` | server, database<sup>\*1</sup> |
| `DB_PASSWORD`                       | Database password                                                                      | `postgres` | server, database<sup>\*1</sup> |
| `DB_DATABASE_NAME`                  | Database name                                                                          |  `immich`  | server, database<sup>\*1</sup> |
| `DB_SSL_MODE`                       | Database SSL mode                                                                      |            | server                         |
| `DB_VECTOR_EXTENSION`<sup>\*2</sup> | Database vector extension (one of [`vectorchord`, `pgvector`])                         |            | server                         |
| `DB_SKIP_MIGRATIONS`                | Whether to skip running migrations on startup (one of [`true`, `false`])               |  `false`   | server                         |
| `DB_STORAGE_TYPE`                   | Optimize concurrent IO on SSDs or sequential IO on HDDs ([`SSD`, `HDD`])<sup>\*3</sup> |   `SSD`    | database                       |

\*1: The values of `DB_USERNAME`, `DB_PASSWORD`, and `DB_DATABASE_NAME` are passed to the Postgres container as the variables `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` in `docker-compose.yml`.

\*2: If not provided, the appropriate extension to use is auto-detected at startup by inspecting the database. When multiple extensions are installed, the order of preference is VectorChord, pgvector.

\*3: Uses either [`postgresql.ssd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.ssd.conf) or [`postgresql.hdd.conf`](https://github.com/immich-app/base-images/blob/main/postgres/postgresql.hdd.conf) which mainly controls the Postgres `effective_io_concurrency` setting to allow for concurrenct IO on SSDs and sequential IO on HDDs.

:::info

All `DB_` variables must be provided to all Immich workers, including `api` and `microservices`.

`DB_URL` must be in the format `postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport/immichdatabasename`.
You can require SSL by adding `?sslmode=require` to the end of the `DB_URL` string, or require SSL and skip certificate verification by adding `?sslmode=require&uselibpqcompat=true`. This allows both immich and `pg_dumpall` (the utility used for database backups) to [properly connect](https://github.com/brianc/node-postgres/tree/master/packages/pg-connection-string#tcp-connections) to your database.

When `DB_URL` is defined, the `DB_HOSTNAME`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD` and `DB_DATABASE_NAME` database variables are ignored.

:::

## Redis

| Variable         | Description    | Default | Containers |
| :--------------- | :------------- | :-----: | :--------- |
| `REDIS_URL`      | Redis URL      |         | server     |
| `REDIS_SOCKET`   | Redis socket   |         | server     |
| `REDIS_HOSTNAME` | Redis host     | `redis` | server     |
| `REDIS_PORT`     | Redis port     | `6379`  | server     |
| `REDIS_USERNAME` | Redis username |         | server     |
| `REDIS_PASSWORD` | Redis password |         | server     |
| `REDIS_DBINDEX`  | Redis DB index |   `0`   | server     |

:::info
All `REDIS_` variables must be provided to all Immich workers, including `api` and `microservices`.

`REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration.
More information can be found in the upstream [ioredis] documentation.

When `REDIS_URL` or `REDIS_SOCKET` are defined, the `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_USERNAME`, `REDIS_PASSWORD`, and `REDIS_DBINDEX` variables are ignored.
:::

Redis (Sentinel) URL example JSON before encoding:

<details>
<summary>JSON</summary>

```json
{
  "sentinels": [
    {
      "host": "redis-sentinel-node-0",
      "port": 26379
    },
    {
      "host": "redis-sentinel-node-1",
      "port": 26379
    },
    {
      "host": "redis-sentinel-node-2",
      "port": 26379
    }
  ],
  "name": "redis-sentinel"
}
```

</details>

## Machine Learning

| Variable                                                    | Description                                                                                         |           Default           | Containers       |
| :---------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-------------------------: | :--------------- |
| `MACHINE_LEARNING_MODEL_TTL`                                | Inactivity time (s) before a model is unloaded (disabled if \<= 0)                                  |            `300`            | machine learning |
| `MACHINE_LEARNING_MODEL_TTL_POLL_S`                         | Interval (s) between checks for the model TTL (disabled if \<= 0)                                   |            `10`             | machine learning |
| `MACHINE_LEARNING_CACHE_FOLDER`                             | Directory where models are downloaded                                                               |          `/cache`           | machine learning |
| `MACHINE_LEARNING_REQUEST_THREADS`<sup>\*1</sup>            | Thread count of the request thread pool (disabled if \<= 0)                                         |     number of CPU cores     | machine learning |
| `MACHINE_LEARNING_MODEL_INTER_OP_THREADS`                   | Number of parallel model operations                                                                 |             `1`             | machine learning |
| `MACHINE_LEARNING_MODEL_INTRA_OP_THREADS`                   | Number of threads for each model operation                                                          |             `2`             | machine learning |
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup>                    | Number of worker processes to spawn                                                                 |             `1`             | machine learning |
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup>   | HTTP Keep-alive time in seconds                                                                     |             `2`             | machine learning |
| `MACHINE_LEARNING_WORKER_TIMEOUT`                           | Maximum time (s) of unresponsiveness before a worker is killed                                      | `300` (`900` if using ROCm) | machine learning |
| `MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL`                   | Comma-separated list of (textual) CLIP model(s) to preload and cache                                |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL`                    | Comma-separated list of (visual) CLIP model(s) to preload and cache                                 |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Comma-separated list of (recognition) facial recognition model(s) to preload and cache              |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION`   | Comma-separated list of (detection) facial recognition model(s) to preload and cache                |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__OCR__RECOGNITION`                | Comma-separated list of (recognition) OCR model(s) to preload and cache                             |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__OCR__DETECTION`                  | Comma-separated list of (detection) OCR model(s) to preload and cache                               |                             | machine learning |
| `MACHINE_LEARNING_PRELOAD__OCR__MAX_RESOLUTION`             | Resolution to preload the OCR detection model(s) for (must match the server's OCR resolution)       |            `736`            | machine learning |
| `MACHINE_LEARNING_ANN`                                      | Enable ARM-NN hardware acceleration if supported                                                    |           `True`            | machine learning |
| `MACHINE_LEARNING_ANN_FP16_TURBO`                           | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) |           `False`           | machine learning |
| `MACHINE_LEARNING_ANN_TUNING_LEVEL`                         | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive)                                        |             `2`             | machine learning |
| `MACHINE_LEARNING_DEVICE_IDS`<sup>\*4</sup>                 | Device IDs to use in multi-GPU environments                                                         |             `0`             | machine learning |
| `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION`       | Set the maximum number of faces that will be processed at once by the facial recognition model      |             `4`             | machine learning |
| `MACHINE_LEARNING_MAX_BATCH_SIZE__OCR`                      | Set the maximum number of boxes that will be processed at once by the OCR model                     |             `6`             | machine learning |
| `MACHINE_LEARNING_RKNN`                                     | Enable RKNN hardware acceleration if supported                                                      |           `True`            | machine learning |
| `MACHINE_LEARNING_RKNN_THREADS`                             | How many threads of RKNN runtime should be spun up while inferencing.                               |             `1`             | machine learning |
| `MACHINE_LEARNING_MODEL_ARENA`                              | Pre-allocates CPU memory to avoid memory fragmentation                                              |            true             | machine learning |
| `MACHINE_LEARNING_MODEL_REVISION`                           | Model revision to download; `main` selects the original exports and `v2` selects the newer exports  |           `main`            | machine learning |

\*1: It is recommended to begin with this parameter when changing the concurrency levels of the machine learning service and then tune the other ones.

\*2: Since each process duplicates models in memory, changing this is not recommended unless you have abundant memory to go around.

\*3: For scenarios like HPA in K8S. https://github.com/immich-app/immich/discussions/12064

\*4: Using multiple GPUs requires `MACHINE_LEARNING_WORKERS` to be set greater than 1. A single device is assigned to each worker in round-robin priority.

:::info

While the `textual` model is the only one required for smart search, some users may experience slow first searches
due to backups triggering loading of the other models into memory, which blocks other requests until completed.
To avoid this, you can preload the other models (`visual`, `recognition`, and `detection`) if you have enough RAM to do so.

Additional machine learning parameters can be tuned from the admin UI.

:::

## Prometheus

| Variable                   | Description                                                                                                           | Default | Containers | Workers            |
| :------------------------- | :-------------------------------------------------------------------------------------------------------------------- | :-----: | :--------- | :----------------- |
| `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all |         | server     | api, microservices |
| `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`                                          |         | server     | api, microservices |

## Secrets

The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security.

To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the “regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value.

| Regular Variable   | Equivalent Docker Secrets '\_FILE' Variable |
| :----------------- | :------------------------------------------ |
| `DB_HOSTNAME`      | `DB_HOSTNAME_FILE`<sup>\*1</sup>            |
| `DB_DATABASE_NAME` | `DB_DATABASE_NAME_FILE`<sup>\*1</sup>       |
| `DB_USERNAME`      | `DB_USERNAME_FILE`<sup>\*1</sup>            |
| `DB_PASSWORD`      | `DB_PASSWORD_FILE`<sup>\*1</sup>            |
| `DB_URL`           | `DB_URL_FILE`<sup>\*1</sup>                 |
| `REDIS_PASSWORD`   | `REDIS_PASSWORD_FILE`<sup>\*2</sup>         |

\*1: See the [official documentation][docker-secrets-docs] for
details on how to use Docker Secrets in the Postgres image.

\*2: See [this comment][docker-secrets-example] for an example of how
to use a Docker secret for the password in the Redis container.

[tz-list]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234
[docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets
[docker-secrets]: https://docs.docker.com/engine/swarm/secrets/
[ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis
[systemd-creds]: https://systemd.io/CREDENTIALS/

---

### Docs Install Kubernetes (`docs/docs/install/kubernetes.md`)

---
sidebar_position: 40
---

# Kubernetes

You can deploy Immich on Kubernetes using [the official Helm chart](https://github.com/immich-app/immich-charts/blob/main/README.md).

You can view some [examples](https://kubesearch.dev/#/immich) of how other people run Immich on Kubernetes, using the official chart or otherwise.

:::caution DNS in Alpine containers
Immich makes use of Alpine container images. These can encounter [a DNS resolution bug](https://stackoverflow.com/a/65593511) on Kubernetes clusters if the host
nodes have a search domain set, like:

```
$ cat /etc/resolv.conf
search home.lan
nameserver 192.168.1.1
```

:::

---

### Docs Install One Click (`docs/docs/install/one-click.md`)

---
sidebar_position: 65
---

# One-Click [Cloud Service]

:::note
This version of Immich is provided via cloud service providers' one-click marketplaces. Hosting costs are set by the cloud service providers.
Support for these are provided by the individual cloud service providers.

**Please report issues to the corresponding [Github Repository][github].**
:::

## Installation

Go to the provider's marketplace and choose Immich, then follow the provided instructions.

## One-Click Immich marketplace providers

### DigitalOcean

https://marketplace.digitalocean.com/apps/immich

### Vultr

https://www.vultr.com/marketplace/apps/immich

## Issues

For issues, open an issue on the associated [GitHub Repository][github].

[github]: https://github.com/immich-app/immich/

---

### Docs Install Portainer (`docs/docs/install/portainer.md`)

---
sidebar_position: 50
---

# Portainer

Install Immich using Portainer's Stack feature.

1. Go to "**Stacks**" in the left sidebar.
2. Click on "**Add stack**".
3. Give the stack a name (i.e. immich), and select "**Web Editor**" as the build method.
4. Copy the content of the `docker-compose.yml` file from the [GitHub repository](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml).
5. Replace `.env` with `stack.env` for all containers that need to use environment variables in the web editor.

<img
src={require('./img/dot-env.webp').default}
width="50%"
style={{border: '1px solid #ddd'}}
alt="Dot Env Example"
/>

8. Click on "**Advanced Mode**" in the **Environment Variables** section.

<img
src={require('./img/env-1.webp').default}
width="50%"
style={{border: '1px solid #ddd'}}
alt="Dot Env Example"
/>

9. Copy the content of the `example.env` file from the [GitHub repository](https://github.com/immich-app/immich/releases/latest/download/example.env) and paste into the editor.
10. Switch back to "**Simple Mode**".

<img
src={require('./img/env-2.webp').default}
width="50%"
style={{border: '1px solid #ddd'}}
alt="Dot Env Example"
/>

- Change the default `DB_PASSWORD`, and add custom database connection information if necessary.
- Change `DB_DATA_LOCATION` to a folder (absolute path) where the database will be saved to disk.
- Change `UPLOAD_LOCATION` to a folder (absolute path) where media (uploaded and generated) will be stored.

11. Click on "**Deploy the stack**".

:::tip
For more information on how to use the application, please refer to the [Post Installation](/install/post-install.mdx) guide.
:::

---

### Docs Install Post Install (`docs/docs/install/post-install.mdx`)

---
sidebar_position: 90
---

import RegisterAdminUser from '/docs/partials/_register-admin.md';
import UserCreate from '/docs/partials/_user-create.md';
import StorageTemplate from '/docs/partials/_storage-template.md';
import MobileAppDownload from '/docs/partials/_mobile-app-download.md';
import MobileAppLogin from '/docs/partials/_mobile-app-login.md';
import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';
import ServerBackup from '/docs/partials/_server-backup.md';

# Post installation steps

A list of common steps to take after installing Immich include:

## Step 1 - Register the admin user

<RegisterAdminUser />

## Step 2 - Create a new user (optional)

<UserCreate />

## Step 3 - Update the storage template

<StorageTemplate />

## Step 4 - Download the Mobile App

<MobileAppDownload />

## Step 5 - Login to the Mobile App

<MobileAppLogin />

## Step 6 - Upload Your Library

<MobileAppBackup />

## Step 7 - Setup Server Backups

<ServerBackup />

## Setting up optional features

- [External Libraries](/features/libraries.md): Adding your existing photo library to Immich
- [Hardware Transcoding](/features/hardware-transcoding.md): Speeding up video transcoding
- [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich

---

### Docs Install Qnap (`docs/docs/install/qnap.md`)

---
sidebar_position: 87
---

# Qnap [Community]

::: note
This is a community contribution and not officially supported by the Immich team, but included here for convenience.

Community support should be directed to Qnap-specific support platforms.
:::

Immich can easily be installed on a Qnap NAS using [Container Station](https://www.qnap.com/en/how-to/tutorial/article/container-station-quick-start-guide) within QTS. Container Station comes already installed on QTS, check [Container Station docs](https://www.qnap.com/en/how-to/tutorial/article/how-to-use-container-station-3) for detailed information on using Container Station.

## Step 1 - Qnap configuration checklist for running Immich

- The default admin user must be enabled to run containers.
  - To enable, open Control Panel, Privilege, Users to verify admin status is Enabled.
  - If not, login with another administrator user and uncheck the Disable this account checkbox in the Edit Account Profile window.

### Optional - Store Immich Data Outside Containers Folder

- To store Immich data outside the default Containers folder, providing easier direct access to the files or sharing by other services.
- Enable Advanced Folder Permissions.
  - Open Control Panel, Privilege, Shared Folders, Advanced Permissions tab, check Enable Advanced Folder Permissions and click Apply button.
- Recomended: Setup Immich data folder in Multimedia Shared Folder
  - In Control Panel, Privilege, Shared Folders, click Multimedia Edit Shared Folder Permissions button
  - Add admin user with read/write (RW) access to the folder
  - Click Apply changes to files and subfolders checkbox
  - Click Apply button
  - Open File Station, Multimedia folder, and add Immich folder

### Optional - Access existing photo files with External Libraries

- To allow Immich to access existing folders with photos on the Qnap storage, they must be accessable to the admin user and added as volume bindings in the docker compose.
- Enable Advanced Folder Permissions.
  - Open Control Panel, Privilege, Shared Folders, Advanced Permissions tab, check Enable Advanced Folder Permissions and click Apply button.
- Add admin account permissions for existing folders.
  - In Privilege > Shared Folders, click Edit Shared Folder Permissions button on folder row.
  - Use Shares folder tree to select existing folders with photo files.
  - Add admin user with read/write (RW) access to the folder
  - click Apply changes to files and subfolders checkbox
  - click Apply button

## Step 2 - Start Container Station

In the Qnap application menu open Container Station. Complete the Welcome screen where a new Shared Folder called Container is created by default.

If the welcome screen keeps appearing when opening Container Station restart the NAS. If it keeps happening, try to uninstall and reinstall Container Station.

## Step 3 - Download docker compose files

Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer.

## Step 4 - Populate the docker compose file with .env and custom values

Qnap Container Station's Application wizard cannot process env_file statements in a docker compose file. Values from the .env file must be moved into the compose file.

Open the docker-compose and .env files in text editors on your computer.

- In docker-compose.yml:
- Replace the `${IMMICH_VERSION:-release}` with the .env values (v3).
- Replace the other `${ENV_VALUE}` instances with values from the .env file
- Remove the env_file: sections from each service yml
- Recommended: Keep default value for postgres password or the immich_server container will error during startup.
- Recommended: Uncomment the DB_STORAGE_TYPE: 'HDD' line in the database: section if your NAS uses hard-drives, not SSDs.

- ### Optional: Persist the PostgreSQL database on the NAS
  - By default, Container Station may remove the database files when you recreate the application. To keep them across upgrades, create a folder on the NAS for the database, for example `/share/Container/immich/postgres`.
  - In the `database` service, map that folder to PostgreSQL's data directory:
    ```
    volumes:
      - type: bind
        source: /share/Container/immich/postgres
        target: /var/lib/postgresql/data
    ```
  - Make sure the folder is writable by the PostgreSQL container. Set this up before the first start. For an existing installation, migrate the current database files into the folder while PostgreSQL is stopped; mapping an empty folder does not copy the existing database. Keep the same host path when recreating or updating the application; changing it will make PostgreSQL start with a new, empty database.

- ### Optional: Store Immich Data Outside Containers Folder
  - In general, it's best practice to have Container Station applications store files under the `./Container` directory. Folders will automatically be created by Container Station.
  - Storing Immich-server data outside the Container folder allows easier navigation and sharing of files uploaded to Immich because the `./Container` directory has restrictive permission.
  - In services, immich-server yml, set volumes binding values for immich-server to access folder.
  - Comment out existing line:
    ```
      #- ${UPLOAD_LOCATION}:/data
    ```
  - Set external source for Immich default /data folder.
    ```
    - type: bind
      source: /share/Multimedia/Immich
      target: /data
    ```
- ### Optional: Access existing photo files with External Libraries
  - In services, immich-server yml, add volumes binding for existing folders and set unique target values.
    ```
    - type: bind
      source: /share/Multimedia/Photos
      target: /multimediaphotos
    ```
    ```
    - type: bind
      source: "/share/homes/[username]/Pictures Folder"
      target: /userpictures
    ```

## Step 6 - Upload docker compose to Containers folder

With Qnap File Station, open the Containers folder and create a folder for storing docker compose files.

Upload your edited compose file to the folder.

## Step 7 - Create Application in Container Station

Open Container Station, and select the "**Applications**" action on the left navigation bar and then click "**+ Create**".

In the Create Application screen, set "**Application name**" to a name such as _immich-app_.

Use the Upload > Local QNAP Device buttons to open the docker compose file in ./Containers/compose.

Click the Validate button to check for any syntax errors. A green checkmark should appear. Warnings will popup if any issues are found and you will not be able to create the application.

Modifying Advanced Settings is not required, but can be used to limit the Resources. Keep Default Web URL Port disabled, the docker yml file sets the outside port and Container Station crates the required networking rules for accessing Immich.

Click the Create button.

Once your containers are successfully running, navigate to the "**Containers**" section of Container Station, click on the "**immich-server**" container. In the Container Details section is the Port Forwarding value, click the copy button and paste the url into a new browser tab to open the Immich Web app.

If the website doesn't open in a web browser, open the immich_server page and open the Logs tab. Any application errors will be output into the terminal.

## Step 8 - Optional: Access existing photo files with External Libraries

After completing the setup wizard. Open the Administration page from the top right menu.

Click the External Libraries section and click Create Library.

Click the Folders add button.

Enter in the `target` value from the `volumes` binding in the docker compose `immich-server` section.

From the examples above '/multimediaphotos' and '/userpictures' are valid to enter in the Path textbox.

Click Add button.

## Updating Immich

To update Immich it is recommended to use the Container Station, Applications, Recreate process.

This brings up the docker compose text in the Recreate Application window.
Edit the docker compose text to update versions or just click Update button.

The latest version of the image major version will be downloaded.

If you did not configure a persistent host-folder mapping for `/var/lib/postgresql/data`, the postgres database may be cleared by this operation. The Immich data and backups will be preserved and can be used to restore the app. If you configured the optional mapping above, Container Station should reuse the database files from that folder when recreating the application, so a restore should not be needed for a routine upgrade. Keep regular backups in either case.

If the database was cleared, or the application asks you to set up or restore, choose Restore From Backup when the Immich website comes back online.

Manually refresh the browser if it doesn't automatically refresh the page.

Click Next on the Restore Your Library page. Select the most recent backup and click Restore button.

---

### Docs Install Requirements (`docs/docs/install/requirements.md`)

---
sidebar_position: 10
---

# Requirements

Hardware and software requirements for Immich:

## Hardware

- **OS**: Recommended Linux or \*nix 64-bit operating system (Ubuntu, Debian, etc).
  - Non-Linux OSes tend to provide a poor Docker experience and are strongly discouraged.
    Our ability to assist with setup or troubleshooting on non-Linux OSes will be severely reduced.
    If you still want to try to use a non-Linux OS, you can set it up as follows:
    - Windows: [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/) or [WSL 2](https://docs.docker.com/desktop/wsl/).
    - macOS: [Docker Desktop on Mac](https://docs.docker.com/desktop/install/mac-install/).
  - Immich runs well in a virtualized environment when running in a full virtual machine.
    The use of Docker in LXC containers is [not recommended](https://pve.proxmox.com/wiki/Linux_Container), but may be possible for advanced users.
    If you have issues, we recommend that you switch to a supported VM deployment.
- **RAM**: Minimum 6GB, recommended 8GB.
- **CPU**: Minimum 2 cores, recommended 4 cores.
  - Immich runs on the `amd64` and `arm64` platforms.
    Since `v3`, the machine learning container on `amd64` requires the `>= x86-64-v2` [microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels).
    Most CPUs released since ~2012 support this microarchitecture.
    If you are using a virtual machine, ensure you have selected a [supported microarchitecture](https://pve.proxmox.com/pve-docs/chapter-qm.html#_qemu_cpu_types).
    If you are unable to support this instruction set, the last version to support `x86-64-v1` is `v2.7.5`.
    Note that this release is no longer supported, and you must run a matching `immich-server` version.
- **Storage**: Recommended Unix-compatible filesystem (EXT4, ZFS, APFS, etc.) with support for user/group ownership and permissions.
  - The generation of thumbnails and transcoded video can increase the size of the photo library by 10-20% on average.

:::note RAM requirements
For a smooth experience, especially during asset upload, Immich requires at least 6GB of RAM.
For systems with only 4GB of RAM, Immich can be run with machine learning features disabled.
:::

:::tip Postgres setup
Good performance and a stable connection to the Postgres database is critical to a smooth Immich experience.
The Postgres database files are typically between 1-3 GB in size.
For this reason, the Postgres database (`DB_DATA_LOCATION`) should ideally use local SSD storage, and never a network share of any kind.
Additionally, if Docker resource limits are used, the Postgres database requires at least 2GB of RAM.
Windows users may run into issues with non-Unix-compatible filesystems, see below for more details.
:::

## Software

Immich requires [**Docker**](https://docs.docker.com/get-started/get-docker/) with the **Docker Compose plugin**:

- **Docker Engine**: This CLI variant is designed for Linux servers (or Windows via WSL2).
- **Docker Desktop**: This GUI variant is **not recommended** for Linux, but is available for Windows or macOS.

The Compose plugin will be installed by both Docker Engine and Desktop by following the linked installation guides; it can also be [separately installed](https://docs.docker.com/compose/install/).

:::note
Immich requires the command `docker compose`; the similarly named `docker-compose` is [deprecated](https://docs.docker.com/retired/#docker-compose-v1-replaced-by-compose-v2) and is no longer supported by Immich.
:::

### Special requirements for Windows users

<details>
<summary>Database storage on Windows systems</summary>

The Immich Postgres database (`DB_DATA_LOCATION`) must be located on a filesystem that supports user/group
ownership and permissions (EXT2/3/4, ZFS, APFS, BTRFS, XFS, etc.). It will not work on any filesystem formatted in NTFS or ex/FAT/32.
It will not work in WSL (Windows Subsystem for Linux) when using a mounted host directory (commonly under `/mnt`).
If this is an issue, you can change the bind mount to a Docker volume instead as follows:

Make the following change to `.env`:

```diff
- DB_DATA_LOCATION=./postgres
+ DB_DATA_LOCATION=pgdata
```

Add the following line to the bottom of `docker-compose.yml`:

```diff
volumes:
  model-cache:
+ pgdata:
```

</details>

---

### Docs Install Script (`docs/docs/install/script.md`)

---
sidebar_position: 20
---

# Install script [Experimental]

:::caution
This method is experimental and not currently recommended for production use. For production, please refer to installing with [Docker Compose](/install/docker-compose.mdx).
:::

## Requirements

Follow the [requirements page](/install/requirements) to get started.

The install script only supports Linux operating systems and requires Docker to be already installed on the system.

## Steps

In the shell, from a directory of your choice, run the following command:

```bash
curl -o- https://raw.githubusercontent.com/immich-app/immich/main/install.sh | bash
```

The script will perform the following actions:

1. Download [docker-compose.yml](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml), and the [.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file from the main branch of the [repository](https://github.com/immich-app/immich).
2. Start the containers.

The web application and mobile app will be available at `http://<machine-ip-address>:2283`

The directory which is used to store the library files is `./immich-app` relative to the current directory.

:::tip
For common next steps, see [Post Install Steps](/install/post-install.mdx).
:::

---

### Docs Install Synology (`docs/docs/install/synology.md`)

---
sidebar_position: 85
---

# Synology [Community]

:::note
This is a community contribution and not officially supported by the Immich team, but included here for convenience.

Community support should be directed to Synology-specific support platforms.
:::

Immich can easily be installed on a Synology NAS using Container Manager within DSM. If you have not installed Container Manager already, you can install it in the Packages Center. Refer to the [Container Manager docs](https://kb.synology.com/en-us/DSM/help/ContainerManager/docker_desc?version=7) for more information on using Container Manager.

## Step 1 - Download the required files

Create a directory of your choice (e.g. `./immich-app`) to house Immich. In general, it's best practice to have all Docker-based applications running under the `./docker` directory, so in this case, your directory structure will look like `./docker/immich-app`.

Now create a `./postgres` and `./library` directory as sub-directories of the `./docker/immich-app`.

When you're all done, you should have the following:

- `./docker/immich-app/postgres`
- `./docker/immich-app/library`

Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) to your computer. Upload the files to the `./docker/immich-app` directory, and rename `example.env` to `.env`. Note: If you plan to use the Synology Text editor to edit the `.env` file on the NAS within File Station, you will need to rename it to a temporary name (e.g. `example.txt`) in order to see 'Open with Text Editor' in the file context menu. Once saved, rename it back to `.env`.

## Step 2 - Populate the .env file with custom values

Follow [Step 2 in Docker Compose](/install/docker-compose#step-2---populate-the-env-file-with-custom-values) for instructions on customizing the `.env` file, and then return back to this guide to continue.

## Step 3 - Create a new project in Container Manager

Open Container Manager, and select the "**Project**" action on the left navigation bar and then click "**Create**".


In the settings of your new project, set "**Project name**" to a name you'll remember, such as _immich-app_. When setting the "**Path**", select the `./docker/immich-app` directory you created earlier. Doing so will prompt a message to use the existing `docker-compose.yml` already present in the directory for your project. Click "**OK**" to continue.



The following screen will give you the option to further customize your `docker-compose.yml` file. Take note of `DB_STORAGE_TYPE: 'HDD'` and uncomment if applicable for your Synology setup.



Skip the section asking to set-up a portal for Web Station, and then complete the wizard which will build and start the containers for your project.

Once your containers are successfully running, navigate to the "**Container**" section of Container Manager, right-click on the "**immich-server**" container, and choose the "**Details**".

Scroll to the bottom of the "**Details**" section and find the `IP Address` listed in the `Network` section. Take note of the container's IP address as you will need it for **Step 4**.



## Step 4 - Configure Firewall Settings

Once your project completes the build process, your containers will start. In order to be able to access Immich from your browser, you need to configure the firewall settings for your Synology NAS to allow communication between the Immich containers.

Open "**Control Panel**" on your Synology NAS, and select "**Security**". Navigate to "**Firewall**"



Click "**Edit Rules**" and add the following firewall rules:

- Add a "**Source IP**" rule for the IP address of your container that you obtained in Step 3 above



- Add a "**Ports**" rule for the port specified in the `docker-compose.yml`, which should be `2283`



## Next Steps

Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instructions](/install/upgrading.md).

<details>
  <summary>Updating Immich using Container Manager</summary>

Check the post installation and upgrade instructions at the links above before proceeding with this section.

## Step 1. Backup

Ensure your photos and videos are backed up. Your `.env` settings will define where they are stored. There is no need to delete any files or folders within the `docker` folder when doing a release upgrade unless instructed in the release notes.

## Step 2. Check release notes

Always check the [release notes](https://github.com/immich-app/immich/releases) before proceeding with an update!

## Step 3. Stop containers & clean up

Open **Container Manager**. Select **Project** then your Immich app



Select **Stop**



Select **Action** then **Clean**. This removes the containers.



Go to **Image** and select **Remove Unused Images**.



## Step 4. Build

Go to **Project**, select **Action** then **Build**. This will download, unpack, install and start the containers.



## Step 5. Update firewall rule

Without a fixed subnet, the default behavior is to automatically start the containers once installed. If `immich_server` runs for a few seconds and then stops, it may be because the firewall rule no longer matches the server IP address.

Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.


Go to Synology **Control Panel**. Select **Security** and **Firewall**.



In this example, the IP addresses mismatch and the firewall rule needs to be edited to match above.



To prevent future firewall issues, you may set a fixed subnet. [See Set Fixed Subnet](#set-fixed-subnet) for instructions.

</details>

<details id="set-fixed-subnet">
  <summary>Set Fixed Subnet</summary>

Docker by default assigns dynamic subnets to bridge networks which can change when rebuilding containers and can cause firewall rules to break. To avoid this, define a fixed subnet in your `docker-compose.yml`:

## Step 1. Determine current subnet

Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address.


## Step 2. Add network configuration

Add the following network configuration at the end of your `docker-compose.yml` file:

```yaml
networks:
  immich-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1
```

If your docker container is running on a different subnet then update accordingly.

## Step 3. Add network to each service

Add the network to each service (immich-server, immich-machine-learning, redis, database):

```yaml
services:
  immich-server:
    # other config options
    networks:
      - immich-network

  immich-machine-learning:
    # other config options
    networks:
      - immich-network

  redis:
    # other config options
    networks:
      - immich-network

  database:
    # other config options
    networks:
      - immich-network
```

Save your changes. Synology will ask if you want to save changes only or rebuild containers. Select rebuild containers.

## Step 4. Update Firewall Rules, if necessary

If your firewall rules were not already set for this subnet, the firewall rules will need to be updated. See [Step 4 - Configure Firewall Settings](#step-4---configure-firewall-settings).

</details>

---

### Docs Install Truenas (`docs/docs/install/truenas.md`)

---
sidebar_position: 80
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# TrueNAS [Community]

:::note
This is a community contribution and not officially supported by the Immich team, but included here for convenience.

Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).

**Please report app issues to the corresponding [GitHub Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).**
:::

:::warning
This guide covers the installation of Immich on TrueNAS Community Edition 24.10.2.2 (Electric Eel) and later.

We recommend keeping TrueNAS Community Edition and Immich relatively up to date with the latest versions to avoid any issues.

If you are using an older version of TrueNAS, we ask that you upgrade to the latest version before installing Immich. Check the [TrueNAS Community Edition Release Notes](https://www.truenas.com/docs/softwarereleases/) for more information on breaking changes, new features, and how to upgrade your system.
:::

Immich can easily be installed on TrueNAS Community Edition via the **Community** train application.
Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system.

## First Steps

### Setting up Storage Datasets

Before beginning app installation, [create the datasets](https://www.truenas.com/docs/scale/scaletutorials/storage/datasets/datasetsscale/) to use in the **Storage Configuration** section during installation.

In TrueNAS, Immich requires 2 datasets for the application to function correctly: `data` and `pgData`. You can set the datasets to any names to match your naming conventions or preferences.
You can organize these as one parent with two child datasets, for example `/mnt/tank/immich/data` and `/mnt/tank/immich/pgData`.

<img
src={require('./img/truenas/truenas00.webp').default}
width="40%"
alt="Immich App Widget"
className="border rounded-xl"
/>

:::info Datasets Permissions

The **pgData** dataset must be owned by the user `netdata` (UID 999) for Postgres to start.

The `data` dataset must have given the **_modify_** permission to the user who will run Immich.

Since TrueNAS Community Edition 24.10.2.2 and later, Immich can be run as any user and group, the default user being `apps` (UID 568) and the default group being `apps` (GID 568). This user, either `apps` or another user you choose, must have **_modify_** permissions on the **data** dataset.

For an easy setup:

- Create the parent dataset `immich` keeping the default **Generic** preset.
- Select `Dataset Preset` **Apps** instead of **Generic** when creating the `data` dataset. This will automatically give the correct permissions to the dataset. If you want to use another user for Immich, you can keep the **Generic** preset, but you will need to give the **_modify_** permission to that other user.
- For the `pgData` dataset, you can keep the default preset **Generic** as permissions can be set during the installation of the Immich app (See [Storage Configuration](#storage-configuration) section).
  :::

:::tip
To improve performance, Immich recommends using SSDs for the database. If you have a pool made of SSDs, you can create the `pgData` dataset on that pool.

Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Additional Storage: Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section below.
:::

:::warning
If you just created the datasets using the **Apps** preset, you can skip this warning section.

If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) set to `Passthrough` if you plan on using a [storage template](/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017)

To change or verify the ACL mode, go to the **Datasets** screen, select the **library** dataset, click on the **Edit** button next to **Dataset Details**, then click on the **Advanced Options** tab, scroll down to the **ACL Mode** section, and select `Passthrough` from the dropdown menu. Click **Save** to apply the changes. If the option is greyed out, set the **ACL Type** to `SMB/NFSv4` first, then you can change the **ACL Mode** to `Passthrough`.
:::

## Installing the Immich Application

To install the **Immich** application, go to **Apps**, click **Discover Apps**, and either begin typing Immich into the search field or scroll down to locate the **Immich** application widget.

<div style={{ marginBottom: '2rem', border: '1px solid #ccc', padding: '1rem', borderRadius: '8px' }}>

Click on the widget to open the **Immich** application details screen.
<img
src={require('./img/truenas/truenas01.webp').default}
width="50%"
alt="Immich App Widget"
className="border rounded-xl"
/>

</div>

<div style={{ marginBottom: '2rem', border: '1px solid #ccc', padding: '1rem', borderRadius: '8px' }}>

Click **Install** to open the Immich application configuration screen.
<img
src={require('./img/truenas/truenas02.webp').default}
width="100%"
alt="Immich App Details Screen"
className="border rounded-xl"
/>

</div>

Application configuration settings are presented in several sections, each explained below.
To find specific fields, click in the **Search Input Fields** search field, scroll down to a particular section, or click on the section heading on the navigation area in the upper-right corner.

### Application Name and Version

<img
src={require('./img/truenas/truenas03.webp').default}
width="100%"
alt="Install Immich Screen"
className="border rounded-xl mb-4"
/>

Keep the default value or enter a name in the **Application Name** field.  
Change it if you’re deploying a second instance.

Immich version within TrueNAS catalog (Different from Immich release version).

### Immich Configuration

<img
src={require('./img/truenas/truenas04.webp').default}
width="40%"
alt="Configuration Settings"
className="border rounded-xl mb-4"
/>

The **Timezone** is set to the system default, which usually matches your local timezone. You can change it to another timezone if you prefer.

**Enable Machine Learning** is enabled by default. It allows Immich to use machine learning features such as face recognition, image search, and smart duplicate detection. Untick this option if you do not want to use these features.

Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md)

**Database Password** should be set to a custom value using only the characters `A-Za-z0-9`. This password is used to secure the Postgres database.

**Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. Preferably, use a different password from the database password.

Keep the **Log Level** to the default `Log` value.

Leave **Hugging Face Endpoint** blank. (This is used to download ML models from a different source.)

Set **Database Storage Type** to the type of storage (**HDD** or **SSD**) that the pool where the **pgData** dataset is located uses.

**Additional Environment Variables** can be left blank.

<details>
<summary>Advanced users: Adding Environment Variables</summary>

Environment variables can be set by clicking the **Add** button and filling in the **Name** and **Value** fields.

<img
src={require('./img/truenas/truenas05.webp').default}
width="40%"
alt="Environment Variables"
className="border rounded-xl"
/>

These are used to add custom configuration options or to enable specific features.
More information on available environment variables can be found in the **[environment variables documentation](/install/environment-variables/)**.

:::info
Some environment variables are not available for the TrueNAS Community Edition app as they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings).

Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`.
:::

</details>

### User and Group Configuration

Application in TrueNAS runs as a specific user and group. Immich uses the default user `apps` (UID 568) and the default group `apps` (GID 568).

<img
src={require('./img/truenas/truenas06.webp').default}
width="40%"
alt="User and Group Configuration"
className="border rounded-xl"
/>

- **User ID**: Keep the default value `apps` (UID 568) or define a different one if needed.

- **Group ID**: Keep the default value `apps` (GID 568) or define a different one if needed.

:::warning
If you change the user or group, make sure that the datasets you created for Immich data storage have the correct permissions set for that user and group as specified in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above.
:::

### Network Configuration

<img
src={require('./img/truenas/truenas07.webp').default}
width="40%"
alt="Networking Settings"
className="border rounded-xl"
/>

- **Port Bind Mode**: This lets you expose the port to the host system, allowing you to access Immich from outside the TrueNAS system. Keep the default **_Publish port on the host for external access_** value unless you have a specific reason to change it.

- **Port Number**: Keep the default port `30041` or enter a custom port number.

- **Host IPs**: Leave the default blank value.

### Storage Configuration

:::danger Default Settings (Not recommended)
The default setting for datasets is **ixVolume (dataset created automatically by the system)**. This is not recommended as this results in your data being harder to access manually and can result in data loss if you delete the immich app. It is also harder to manage snapshots and replication tasks. It is recommended to use the **Host Path (Path that already exists on the system)** option instead.
:::

The storage configuration section allows you to set up the storage locations for Immich data. You can select the datasets created in the previous step.

<img
src={require('./img/truenas/truenas08.webp').default}
width="40%"
alt="Configure Storage Volumes"
className="border rounded-xl"
/>

For the Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Immich data storage, for example, `data`.

The Machine Learning cache can be left with default _Temporary_

For the Postgres Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Postgres data storage, for example, `pgData`.

:::info
**Postgres Data Storage**
Once **Host Path** is selected, a checkbox appears with **_Automatic Permissions_**. If you have not set the ownership of the **pgData** dataset to `netdata` (UID 999), tick this box as it will set the user ownership to `netdata` (UID 999) and the group ownership to `docker` (GID 999) automatically. If you have set the ownership of the **pgData** dataset to `netdata` (UID 999), you can leave this box unticked.
:::

### Additional Storage (Advanced Users)

<details>
<summary>External Libraries</summary>

:::danger Advanced Users Only
This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup.
:::

<img
src={require('./img/truenas/truenas09.webp').default}
width="40%"
alt="Add External Libraries with Additional Storage"
className="border rounded-xl"
/>

You may configure [external libraries](/features/libraries) by mounting them using **Additional Storage**.

The dataset that contains your external library files must at least give **read** access to the user running Immich (Default: `apps` (UID 568), `apps` (GID 568)).
If you want to be able to delete files or edit metadata in the external library using Immich, you will need to give the **modify** permission to the user running Immich.

- **Mount Path** is the location you will need to copy and paste into the external library settings within Immich.
- **Host Path** is the location on the TrueNAS Community Edition server where your external library is located.
- **Read Only** is a checkbox that you can tick if you want to prevent Immich from modifying the files in the external library. This is useful if you want to use Immich to view and search your external library without modifying it.

:::warning
Each mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`.

A general recommendation is to mount any external libraries to a path beginning with `/mnt` or `/media` followed by a unique name, such as `/mnt/external-libraries` or `/media/my-external-libraries`. If you plan to mount multiple external libraries, you can use paths like `/mnt/external-libraries/library1`, `/mnt/external-libraries/library2`, etc.
:::

</details>

<details>
<summary>Multiple Datasets for Immich Storage</summary>

:::danger Advanced Users Only
This feature should only be used by advanced users.
:::

Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/guides/custom-locations), but read on for the TrueNAS guide.

Each additional dataset has to give the permission **_modify_** to the user who will run Immich (Default: `apps` (UID 568), `apps` (GID 568))
As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, you have to create the datasets with the **Apps** preset to ensure the correct permissions are set, or you can set the permissions manually after creating the datasets.

Immich uses 6 folders for its storage: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. You can create a dataset for each of these folders or only for some of them.

To mount these datasets:

1. Add an **Additional Storage** entry for each dataset you want to use.
2. Select **Type** as **Host Path (Path that already exists on the system)**.
3. Enter the **Mount Path** with `/data/<folder-name>`. The `<folder-name>` is the name of the folder you want to mount, for example, `library`, `upload`, `thumbs`, `profile`, `encoded-video`, or `backups`.
   :::danger Important
   You have to write the full path, including `/data/`, as Immich expects the data to be in that location.  
   If you do not include this path, Immich will not be able to find the data and will not write the data to the location you specified.
   :::
4. Select the **Host Path** as the dataset you created for that folder, for example, `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, etc.

<img
src={require('./img/truenas/truenas10.webp').default}
width="40%"
alt="Use Multiple Datasets for Immich Storage with Additional Storage"
className="border rounded-xl"
/>

</details>



### Resources Configuration

<img
src={require('./img/truenas/truenas11.webp').default}
width="40%"
className="border rounded-xl"
/>

- **CPU**: Depending on your system resources, you can keep the default value of `2` threads or specify a different number. Immich recommends at least `8` threads.

- **Memory**: Limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB.

Both **CPU** and **Memory** are limits, not reservations. This means that Immich can use up to the specified amount of CPU threads and RAM, but it will not reserve that amount of resources at all times. The system will allocate resources as needed, and Immich will use less than the specified amount most of the time.

- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md).

The process for NVIDIA GPU passthrough requires additional steps.
More details here: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough)

### Install

Finally, click **Install**.
The system opens the **Installed Applications** screen with the Immich app in the **Deploying** state.
When the installation completes, it changes to **Running**.

<img
src={require('./img/truenas/truenas12.webp').default}
width="100%"
alt="Immich Installed"
className="border rounded-xl"
/>

Click **Web Portal** on the **Application Info** widget, or go to the URL `http://<your-truenas-ip>:30041` in your web browser to open the Immich web interface. This will show you the onboarding process to set up your first user account, which will be an administrator account.

After that, you can start using Immich to upload and manage your photos and videos.

:::tip
For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide.
:::

## Edit App Settings

- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
- Click **Edit** on the **Application Info** widget to open the **Edit Immich** screen.
- Change any settings you would like to change.
  - The settings on the edit screen are the same as on the install screen.
- Click **Update** at the very bottom of the page to save changes.
  - TrueNAS automatically updates, recreates, and redeploys the Immich container with the updated settings.

## Updating the App

:::danger
Make sure to read the general [upgrade instructions](/install/upgrading.md).
:::

When updates become available, TrueNAS alerts and provides easy updates.
To update the app to the latest version:

- Go to the **Installed Applications** screen and select Immich from the list of installed applications.
- Click **Update** on the **Application Info** widget from the **Installed Applications** screen.
- This opens an update window with some options
  - You may select an Image update too.
  - You may view the Changelog.
- Click **Upgrade** to begin the process and open a counter dialog that shows the upgrade progress.
  - When complete, the update badge and buttons disappear and the application Update state on the Installed screen changes from Update Available to Up to date.

## Migration

:::danger
Perform a backup of your Immich data before proceeding with the migration steps below. This is crucial to prevent any data loss if something goes wrong during the migration process.

The migration should also be performed when the Immich app is not running to ensure no data is being written while you are copying the data.
:::

### Migration from Old Storage Configuration

There are two ways to migrate from the old storage configuration to the new one, depending on whether you want to keep the old multiple datasets or if you want to move to a double dataset configuration with a single dataset for Immich data storage and a single dataset for Postgres data storage.

:::note Old TrueNAS Versions Permissions
If you were using an older version of TrueNAS (before 24.10.2.2), the datasets, except the one for **pgData** had only to be owned by the `root` user (UID 0). You might have to add the **modify** permission to the `apps` user (UID 568) or the user you want to run Immich as, to all of them, except **pgData**. The steps to add or change ACL permissions are described in the [TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/).
:::

<Tabs groupId="truenas-migration-tabs">
  <TabItem value="migrate-new-dataset" label="Migrate data to a new dataset (recommended)" default>

To migrate from the old storage configuration to the new one, you will need to create a new dataset for the Immich data storage and copy the data from the old datasets to the new ones. The steps are as follows:

1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are copying the data.
2. **Create a new dataset** for the Immich data storage, for example, `data`. As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, create the dataset with the **Apps** preset to ensure the correct permissions are set.
3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples:

```bash
sudo rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/
sudo rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/
sudo rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/
sudo rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/
sudo rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/
sudo rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/
```

Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset.

:::tip
If you were using **ixVolume (dataset created automatically by the system)** for some of Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example:

```bash
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/
```

If you also were storing your files in the **ixVolume**, the **_upload_** folder is named `uploads` instead of `upload`, so the command to run should be:

```bash
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/uploads/ /mnt/tank/immich/data/upload/
```

This means that depending on your old storage configuration, you might have to use a mix of paths in the `rsync` commands above.

If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data:

```bash
sudo rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/
```

:::

:::warning
Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example:

```bash
sudo rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/
```

Replace `library` with the name of the folder where you are copying the file.
:::

4. **Update the permissions** as the permissions of the data that have been copied has been preserved, to ensure that the `apps` user (UID 568) has the correct permissions on all the copied data. If you just created the dataset with the **Apps** preset, from the TrueNAS web interface, go to the **Datasets** screen, select the **data** dataset, click on the **Edit** button next to **Permissions**, tick the "Apply permissions recursively" checkbox, and click **Save**. This will apply the correct permissions to all the copied data.
5. **Update the Immich app** to use the new dataset:
   - Go to the **Installed Applications** screen and select Immich from the list of installed applications.
   - Click **Edit** on the **Application Info** widget.
   - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
   - For the **Data Storage**, select **Host Path (Path that already exists on the system)** and then select the new dataset you created for Immich data storage, for example, `data`.
   - For the **Postgres Data Storage**, verify that it is still set to the dataset you created for Postgres data storage, for example, `pgData`.
   - Click **Update** at the bottom of the page to save changes.

6. **Start the Immich app** from the TrueNAS web interface.

This will recreate the Immich container with the new storage configuration and start the app.

If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface.

:::tip
If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands:

```bash
sudo rm -r /mnt/.ix-apps/app_mounts/immich/*
```

:::

</TabItem>
  <TabItem value="migrate-old-dataset" label="Keep the existing datasets">

To migrate from the old storage configuration to the new one without creating new datasets.

1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app.
2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have **_modify_** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with **_modify_** permissions.
3. **Update the Immich app** to use the existing datasets:
   - Go to the **Installed Applications** screen and select Immich from the list of installed applications.
   - Click **Edit** on the **Application Info** widget.
   - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox.
   - For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`.
   - For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`.
   - Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings:
     - **Type**: `Host Path (Path that already exists on the system)`
     - **Mount Path**: `/data/<folder-name>` (e.g. `/data/library`)
     - **Host Path**: `/mnt/<your-pool-name>/<dataset-name>` (e.g. `/mnt/tank/immich/library`)
       :::danger Ensure using the correct paths names
       Make sure to replace `<folder-name>` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `<your-pool-name>` and `<dataset-name>` with the actual names of your pool and dataset.
       :::
     - **Read Only**: Keep it unticked as Immich needs to write to these datasets.
   - Click **Update** at the bottom of the page to save changes.
4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible.

  </TabItem>
</Tabs>

---

### Docs Install Unraid (`docs/docs/install/unraid.md`)

---
sidebar_position: 70
---

# Unraid [ Community ]

:::note
This is a community contribution and not officially supported by the Immich team, but included here for convenience.

Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/).
:::

Immich can easily be installed and updated on Unraid via:

1. Community made template on the Unraid Community Apps
2. [Docker Compose Manager](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/) plugin from the Unraid Community Apps

## Community Applications Template

:::info

- The Unraid template uses a community made image and is not officially supported by Immich

:::

In order to install Immich from the Unraid CA, you will need an existing Redis and PostgreSQL 14 container, If you do not already have Redis or PostgreSQL you can install them from the Unraid CA, just make sure you choose PostgreSQL **14**.

Once you have Redis and PostgreSQL running, search for Immich on the Unraid CA, choose either of the templates listed and fill out the example variables.

For more information about setting up the community image see [here](https://github.com/imagegenius/docker-immich#application-setup)

## Docker-Compose Method

:::info

- Guide was written using Unraid v6.12.10.
- Requires you to have installed the plugin: [Docker Compose Manager](https://forums.unraid.net/topic/114415-plugin-docker-compose-manager/)
- An Unraid share created for your images
- There has been a [report](https://forums.unraid.net/topic/130006-errortraps-traps-node27707-trap-invalid-opcode-ip14fcfc8d03c0-sp7fff32889dd8-more/#comment-1189395) of this not working if your Unraid server doesn't support AVX _(e.g. using a T610)_

:::

## Installation Steps

1. Go to "**Plugins**" and click on "**Compose.Manager**"
2. Click "**Add New Stack**" and when prompted for a label enter "**Immich**"

<img
src={require('./img/unraid01.webp').default}
width="70%"
alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
/>

3.  Select the cogwheel ⚙️ next to Immich and click "**Edit Stack**"
4.  Click "**Compose File**" and then paste the entire contents of the [Immich Docker Compose](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) file into the Unraid editor. Remove any text that may be in the text area by default. Note that Unraid v6.12.10 uses version 24.0.9 of the Docker Engine, which does not support healthcheck `start_interval` as defined in the `database` service of the Docker compose file (version 25 or higher is needed). This parameter defines an initial waiting period before starting health checks, to give the container time to start up. Commenting out the `start_interval` and `start_period` parameters will allow the containers to start up normally. The only downside to this is that the database container will not receive an initial health check until `interval` time has passed.

    <details >
        <summary>Using an existing Postgres container? Click me! Otherwise proceed to step 5.</summary>
        <ul>
            <li>Comment out the database service</li>
            <img
                src={require('./img/unraid02.webp').default}
                width="50%"
                alt="Comment out database service in the compose file"
            />
            <li>Comment out the database dependency for <b>each service</b> <i>(example in screenshot below only shows 2 of the services - ensure you do this for all services)</i></li>
            <img
                src={require('./img/unraid03.webp').default}
                width="50%"
                alt="Comment out every reference to the database service in the compose file"
            />
            <li>Comment out the volumes</li>
            <img
                src={require('./img/unraid04.webp').default}
                width="20%"
                alt="Comment out database volume"
            />
        </ul>
    </details>

5.  Click "**Save Changes**", you will be prompted to edit stack UI labels, just leave this blank and click "**Ok**"
6.  Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
7.  Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
    - `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
    - `DB_DATA_LOCATION`: Change this to use an Unraid share (preferably a cache pool, e.g. `/mnt/user/appdata/postgresql/data`). This uses the `appdata` share. Do also create the `postgresql` folder, by running `mkdir /mnt/user/{share_location}/postgresql/data`. If left at default it will try to use Unraid's `/boot/config/plugins/compose.manager/projects/[stack_name]/postgres` folder which it doesn't have permissions to, resulting in this container continuously restarting.

      <img
      src={require('./img/unraid05.webp').default}
      width="70%"
      alt="Absolute location of where you want immich images stored"
      />

    <details >
        <summary>Using an existing Postgres container? Click me! Otherwise proceed to step 8.</summary>
        <p>Update the following database variables as relevant to your Postgres container:</p>
        <ul>
            <li><code>DB_HOSTNAME</code></li>
            <li><code>DB_USERNAME</code></li>
            <li><code>DB_PASSWORD</code></li>
            <li><code>DB_DATABASE_NAME</code></li>
            <li><code>DB_PORT</code></li>
        </ul>
    </details>

8.  Click "**Save Changes**" followed by "**Compose Up**" and Unraid will begin to create the Immich containers in a popup window. Once complete you will see a message on the popup window stating _"Connection Closed"_. Click "**Done**" and go to the Unraid "**Docker**" page

    > Note: This can take several minutes depending on your Internet speed and Unraid hardware

9.  Once on the Docker page you will see several Immich containers, one of them will be labelled `immich_server` and will have a port mapping. Visit the `IP:PORT` displayed in your web browser and you should see the Immich admin setup page.

<img
src={require('./img/unraid06.webp').default}
width="80%"
alt="Go to Docker Tab and visit the address listed next to immich-web"
/>

<details >
    <summary>Using the FolderView plugin for organizing your Docker containers? Click me! Otherwise you're complete!</summary>
    <p>If you are using the FolderView plugin go the Docker tab and select "<b>New Folder</b>".<br />Label it <i>"Immich"</i> and use this URL as the logo: https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo.png<br/>Then simply select all the Immich related containers before clicking "<b>Submit</b>"</p>
    <img
        src={require('./img/unraid07.webp').default}
        width="80%"
        alt="Go to Docker Tab and visit the address listed next to immich-web"
    />
    <img
        src={require('./img/unraid08.webp').default}
        width="90%"
        alt="Go to Docker Tab and visit the address listed next to immich-web"
    />

</details>

:::tip
For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide.
:::

## Updating Steps

:::danger
Make sure to read the general [upgrade instructions](/install/upgrading.md).
:::

Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager.

<img
src={require('./img/unraid09.webp').default}
width="50%"
alt="Docker Compose containers always say update ready, ignore it"
/>

You should ignore the "_update ready_" on the Unraid WebUI and update when you receive the notification within the Immich WebUI.
<img
src={require('./img/unraid10.webp').default}
width="50%"
alt="Immich update notification"
/>

1. Go to the "**Docker**" tab and scroll to the Compose section
2. Next to Immich click the "**Update Stack**" button and Unraid will begin to update all Immich related containers
   > Note: **Do not** select Compose Down first, it is unnecessary.
3. Once complete you will see a "_Connection Closed_" message, select "**Done**".
   <img
   src={require('./img/unraid11.webp').default}
   width="50%"
   alt="Wait for Connection Closed and click Done"
   />
4. Return back to the Immich WebUI and you will see the version has been updated to the latest
   <img
   src={require('./img/unraid12.webp').default}
   width="70%"
   alt="Wait for Connection Closed and click Done"
   />

---

### Docs Install Upgrading (`docs/docs/install/upgrading.md`)

---
sidebar_position: 95
---

# Upgrading

:::tip Breaking changes
You can see versions that had breaking changes [here][breaking].
:::

When a new version of Immich is [released][releases], you should read the release notes and account for any breaking changes noted (as mentioned above).
If you use `IMMICH_VERSION` in your `.env` file, it will need to be updated to the latest or desired version.
After that, the application can be upgraded and restarted with the following commands, run in the directory with the `docker-compose.yml` file:

```bash title="Upgrade and restart Immich"
docker compose pull && docker compose up -d
```

To clean up disk space, the old version's obsolete container images can be deleted with the following command:

```bash title="Clean up unused Docker images"
docker image prune
```

[watchtower]: https://containrrr.dev/watchtower/
[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created
[releases]: https://github.com/immich-app/immich/releases

## Versioning Policy

Immich follows [semantic versioning][semver], which tags releases in the format `<major>.<minor>.<patch>`.
We intend for breaking changes, including those to the API or deployment, to be limited to major version releases.
You can configure your Docker image to point to the current major version by using a metatag, such as `:v3`. These metatags do not follow release candidates.

The mobile app is typically compatible with the current and prior major version. However, the server is only compatible with the matching major version.
Thus, we recommend upgrading all mobile clients before upgrading the server to ensure compatibility.

We do not backport patches to earlier versions. We encourage all users to run the most recent stable release of Immich.
Downgrading to an earlier version, even within the same minor version, is not supported.

[semver]: https://semver.org/

## Migrating to VectorChord

:::info
If you deploy Immich using Docker Compose, see `ghcr.io/immich-app/postgres` in the `docker-compose.yml` file and have not explicitly set the `DB_VECTOR_EXTENSION` environmental variable, your Immich database is already using VectorChord and this section does not apply to you.
:::

:::important
If you do not deploy Immich using Docker Compose and see a deprecation warning for pgvecto.rs on server startup, you should refer to the maintainers of the Immich distribution for guidance (if using a turnkey solution) or adapt the instructions for your specific setup.
:::

Immich has migrated off of the deprecated pgvecto.rs database extension to its successor, [VectorChord](https://github.com/tensorchord/VectorChord), which comes with performance improvements in almost every aspect. This section will guide you on how to make this change in a Docker Compose setup.

Before making any changes, please [back up your database](/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, there’s always a chance that something can go wrong.

After making a backup, please modify your `docker-compose.yml` file with the following information.

```
/* Detailed source-code truncated for AI context efficiency. */
```

:::important
If you deviated from the defaults of pg14 or pgvectors0.2.0, you must adjust the pg major version and pgvecto.rs version. If you are still using the default `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` image, you can just follow the changes above. For example, if the previous image is `docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0`, the new image should be `ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0` instead of the image specified in the diff.
:::

After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, it’s normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index` for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time.

:::danger
After switching to VectorChord, you should not downgrade Immich below 1.133.0.
:::

Please don’t hesitate to contact us on [GitHub](https://github.com/immich-app/immich/discussions) or [Discord](https://discord.immich.app/) if you encounter migration issues.

### VectorChord FAQ

#### I have a separate PostgreSQL instance shared with multiple services. How can I switch to VectorChord?

Please see the [standalone PostgreSQL documentation](/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether you’re currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions.

#### Why are so many lines removed from the `docker-compose.yml` file? Does this mean the health check is removed?

These lines are now incorporated into the image itself along with some additional tuning.

#### What does this change mean for my existing DB backups?

The new DB image includes pgvector and pgvecto.rs in addition to VectorChord, so you can use this image to restore from existing backups that used either of these extensions. However, backups made after switching to VectorChord require an image containing VectorChord to restore successfully.

#### Do I still need pgvecto.rs installed after migrating to VectorChord?

pgvecto.rs only needs to be available during the migration, or if you need to restore from a backup that used pgvecto.rs. For a leaner DB and a smaller image, you can optionally switch to an image variant that doesn’t have pgvecto.rs installed after you’ve performed the migration and started Immich: `ghcr.io/immich-app/postgres:14-vectorchord0.4.3`, changing the PostgreSQL version as appropriate.

#### Why does it matter whether my database is on an SSD or an HDD?

These storage mediums have different performance characteristics. As a result, the optimal settings for an SSD are not the same as those for an HDD. Either configuration is compatible with SSD and HDD, but using the right configuration will make Immich snappier. As a general tip, we recommend users store the database on an SSD whenever possible.

#### Can I use the new database image as a general PostgreSQL image outside of Immich?

It’s a standard PostgreSQL container image that additionally contains the VectorChord, pgvector, and (optionally) pgvecto.rs extensions. If you were using the previous pgvecto.rs image for other purposes, you can similarly do so with this image.

---

### Docs Overview Comparison (`docs/docs/overview/comparison.md`)

---
sidebar_position: 3
---

# Comparison

If you're new here and came from other asset self-hosting alternatives you might want to look at a comparison between Immich and your current solution.
Here you can see a [comparison between the various OpenSource Photo Libraries](https://meichthys.github.io/foss_photo_libraries/) including Immich.

---

### Docs Overview Help (`docs/docs/overview/help.md`)

---
sidebar_position: 6
---

# Help Me!

Running into an issue or have a question? Try the following:

1. Check the [FAQs](/FAQ.mdx).
2. Read through the [Release Notes][github-releases].
3. Search through existing [GitHub Issues][github-issues].
4. Open a help ticket on [Discord][discord-link].

[github-issues]: https://github.com/immich-app/immich/issues
[github-releases]: https://github.com/immich-app/immich/releases
[discord-link]: https://discord.immich.app

---

### Docs Overview Quick Start (`docs/docs/overview/quick-start.mdx`)

---
sidebar_position: 2
---

# Quick start

Here is a quick, no-choices path to install Immich and take it for a test drive.
Once you've tried it, you might use one of the many other ways
to install and use it.

## Requirements

- A system with at least 6GB of RAM and 2 CPU cores.
- [Docker](https://docs.docker.com/engine/install/)

> For a more detailed list of requirements, see the [requirements page](/install/requirements).

---

## Set up the server

import DockerComposeSteps from '/docs/partials/_docker-compose-install-steps.mdx';

<DockerComposeSteps />

---

## Try the web app

import RegisterAdminUser from '/docs/partials/_register-admin.md';

<RegisterAdminUser />

Try uploading a picture from your browser.

<img src={require('./img/upload-button.webp').default} title="Upload button" />

---

## Try the mobile app

### Download the Mobile App

import MobileAppDownload from '/docs/partials/_mobile-app-download.md';

<MobileAppDownload />

### Login to the Mobile App

import MobileAppLogin from '/docs/partials/_mobile-app-login.md';

<MobileAppLogin />

In the mobile app, you should see the photo you uploaded from the web UI.

### Transfer Photos from Your Mobile Device

import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';

<MobileAppBackup />

The backup time differs depending on how many photos are on your mobile device. Large uploads may
take quite a while.
To quickly get going, you can selectively upload few photos first, by following this [guide](/features/mobile-app#sync-only-selected-photos).

You can select the **Job Queues** tab to see Immich processing your photos.

<img src={require('/docs/guides/img/jobs-tab.webp').default} title="Job Queues tab" width={300} />

---

## Review the database backup and restore process

Immich has built-in database backups. You can refer to the
[database backup](/administration/backup-and-restore) for more information.

:::danger
The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`.
:::

---

## Where to go from here?

You may decide you'd like to install the server a different way; the Install category on the left menu provides many options.

You may decide you'd like to add the _rest_ of your photos from Google Photos, even those not on your mobile device, via Google Takeout. You can use [immich-go](https://github.com/simulot/immich-go) for this.

You may want to [upload photos from your own archive](/features/command-line-interface).

You may want to incorporate a pre-existing archive of photos from an [External Library](/features/libraries); there's a [guide](/guides/external-library) for that.

You may want your mobile device to [back photos up to your server automatically](/features/mobile-backup).

---

### Docs Overview Support The Project (`docs/docs/overview/support-the-project.md`)

---
sidebar_position: 5
---

# Support The Project

## Report issues

By far the easiest way to help make Immich better it to use it and report issues and bugs. Found a bug? [Open an issue on GitHub][github-issue].

## Translations

Support the project by localizing on [Weblate](https://hosted.weblate.org/projects/immich/immich/). For more information, see the [Translations](/developer/translations) section.

## Development

If you are a programmer or developer, take a look at Immich's [technology stack](/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/developer/architecture.mdx) section.

## Purchase Immich

You can also [purchase Immich](https://buy.immich.app), for either one user or your entire server. Building Immich takes a lot of time and effort, and we have full-time engineers working on it to make it as good as we possibly can, so any support is greatly appreciated. Don't worry, all features will be free, forever! Nothing will ever be put behind any paywalls.

[github-issue]: https://github.com/immich-app/immich/issues/new/choose
[github-langs]: https://github.com/immich-app/immich/tree/main/mobile/assets/i18n

---

### Docs Partials  Compose Builder (`docs/docs/partials/_compose-builder.mdx`)

:::tip Docker Compose Builder
You can also use the <a href={`https://immich.app/docker-compose-builder${props.query ? `?${props.query}` : ''}`}>Docker Compose Builder</a> instead.
:::

---

### Docs Partials  Docker Compose Install Steps (`docs/docs/partials/_docker-compose-install-steps.mdx`)

import CodeBlock from '@theme/CodeBlock';
import ExampleEnv from '!!raw-loader!../../../docker/example.env';

### Step 1 - Download the required files

Create a directory of your choice (e.g. `./immich-app`) to hold the `docker-compose.yml` and `.env` files.

```bash title="Move to the directory you created"
mkdir ./immich-app
cd ./immich-app
```

Download [`docker-compose.yml`](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml) and [`example.env`](https://github.com/immich-app/immich/releases/latest/download/example.env) by running the following commands:

```bash title="Get docker-compose.yml file"
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
```

```bash title="Get .env file"
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env
```

You can alternatively download these two files from your browser and move them to the directory that you created, in which case ensure that you rename `example.env` to `.env`.

### Step 2 - Populate the .env file with custom values

<CodeBlock language="bash" title="Default environmental variable content">
  {ExampleEnv}
</CodeBlock>

- Populate `UPLOAD_LOCATION` with your preferred location for storing backup assets. It should be a new directory on the server with enough free space.
- Consider changing `DB_PASSWORD` to a custom value. Postgres is not publicly exposed, so this password is only used for local authentication.
  To avoid issues with Docker parsing this value, it is best to use only the characters `A-Za-z0-9`. `pwgen` is a handy utility for this.
- Set your timezone by uncommenting the `TZ=` line.
- Populate custom database information if necessary.

### Step 3 - Start the containers

From the directory you created in Step 1 (which should now contain your customized `docker-compose.yml` and `.env` files), run the following command to start Immich as a background service:

```bash title="Start the containers"
docker compose up -d
```

---

### Docs Partials  Mobile App Backup (`docs/docs/partials/_mobile-app-backup.md`)

1. Navigate to the backup screen by clicking on the cloud icon in the top right corner of the screen.

<img src={require('./img/backup-header.webp').default} width='50%' title='Backup button' />

2. You can select which album(s) you want to back up to the Immich server from the backup screen.

<img src={require('./img/album-selection.webp').default} width='50%' title='Backup button' />

3. Scroll down to the bottom and press "**Enable Backup**" to start the backup process. This will upload all the assets in the selected albums.

:::info
You can read more about backup options [here](/features/mobile-backup.md).
:::

---

### Docs Partials  Mobile App Download (`docs/docs/partials/_mobile-app-download.md`)

The mobile app can be downloaded from the following places:

- [Apple App Store](https://apps.apple.com/us/app/immich/id1613945652)
- [Google Play Store](https://play.google.com/store/apps/details?id=app.alextran.immich)
- [GitHub Releases (APK)](https://github.com/immich-app/immich/releases)
- Obtainium: You can get your Obtainium config link from the [Utilities page of your Immich server](https://my.immich.app/utilities).
- [FUTO F-Droid](https://get.immich.app/fdroid)

---

### Docs Partials  Mobile App Login (`docs/docs/partials/_mobile-app-login.md`)

Login to the mobile app with the server endpoint URL at `http://<machine-ip-address>:2283`

<img src={require('./img/sign-in-phone.webp').default} width='50%' title='Mobile App Sign In' />

---

### Docs Partials  Register Admin (`docs/docs/partials/_register-admin.md`)

The first user to register will be the admin user. The admin user will be able to add other users to the application.

To register for the admin user, access the web application at `http://<machine-ip-address>:2283` and click on the **Getting Started** button.

<img src={require('./img/admin-registration-form.webp').default} width="500" title="Admin Registration" />

Follow the prompts to register as the admin user and log in to the application.

---

### Docs Partials  Server Backup (`docs/docs/partials/_server-backup.md`)

Now that you have imported some pictures, you should setup server backups to preserve your memories.
You can do so by following our [backup guide](/administration/backup-and-restore.md).

:::info
A 3-2-1 backup strategy is still crucial. The team has the responsibility to ensure that the application doesn’t cause loss of your precious memories; however, we cannot guarantee that hard drives will not fail, or an electrical event causes unexpected shutdown of your server/system, leading to data loss. Therefore, we still encourage users to follow best practices when safeguarding their data. Keep multiple copies of your most precious data: at least two local copies and one copy offsite in cold storage.
:::

---

### Docs Partials  Storage Template (`docs/docs/partials/_storage-template.md`)

Immich allows the admin user to set the uploaded filename pattern at the directory and filename level as well as the [storage label for a user](/administration/user-management/#set-storage-label-for-user).

:::tip
You can read more about the differences between storage template engine on and off [here](/administration/backup-and-restore#asset-types-and-storage-locations)
:::

The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text. If the template produces [multiple files with the same filename, they won't be overwritten](https://github.com/immich-app/immich/discussions/3324) as a sequence number is appended to the filename.

Date and time variables in storage templates are rendered in the server's local timezone.

```bash title="Default template"
Year/Year-Month-Day/Filename.Extension
```

If you want to change the storage template during the initial setup, first enable the feature.

<img src={require('./img/enable-storage-template.webp').default} width="80%" title="Enable Storage Template Setting" />

Then, customize your storage template.

<img src={require('./img/storage-template.webp').default} width="80%" title="Storage Template Setting" />

:::info
The `Storage Template Migration` job can be run after enabling this feature or changing the template, in order to apply the changes to the existing library.

<img src={require('./img/storage-template-migration-job.webp').default} width="80%" title="Storage Template Setting" />

:::

:::tip
If an asset is in multiple albums, `{{album}}` will be set to the name of the album which was most recently created. By default, special characters will be converted to an HTML entity (for example, `&` -> `&amp;`). To prevent this, wrap the variable in an extra set of braces (for example, `{{{album}}}`). You can learn more about this [here](https://handlebarsjs.com/guide/expressions.html#html-escaping) and [here](https://github.com/immich-app/immich/issues/4917).
:::

Immich also provides a mechanism to migrate between templates so that if the template you set now doesn't work in the future, you can always migrate all the existing files to the new template. The mechanism is run as a job on the Job page.

If you want to store assets in album folders, but you also have assets that do not belong to any album, you can use `{{#if album}}`, `{{else}}` and `{{/if}}` to create a conditional statement. For example, the following template will store assets in album folders if they belong to an album, and in a folder named "Other/Month" if they do not belong to an album:

```
{{y}}/{{#if album}}{{album}}{{else}}Other{{/if}}/{{MM}}/{{filename}}
```

---

### Docs Partials  User Create (`docs/docs/partials/_user-create.md`)

If you have friends or family members who want to use the application as well, you can create additional accounts for them.

<img src={require('./img/create-new-user.webp').default} width="90%" title='New User Registration' />

On the **Administration > Users** page, you can click on the **Create user** button, and you'll be presented with the following dialog:

<img src={require('./img/create-new-user-dialog.webp').default} width="40%" title='New User Registration Dialog' />

---

### Src Pages Errors (`docs/src/pages/errors.md`)

# Errors

## TypeORM Upgrade

If you encountered "Migrations failed: Error: Invalid upgrade path" then perform an intermediate upgrade to `v1.132.3` first.

:::tip
We recommend users upgrade to `v1.132.3` since it does not have any breaking changes or bugs on this upgrade path.
:::

In order to update to Immich `v1.137.0` or above, the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to `v1.137.0` (or above).

:::caution
Avoid `v1.136.0` if upgrading from `v1.131.0` (or earlier) due to a bug blocking this upgrade in some installations.
:::

## Inconsistent Media Location

:::caution
This error is related to the location of media files _inside the container_. Never move files on the host system when you run into this error message.
:::

Immich automatically tries to detect where your Immich data is located. On start up, it compares the detected media location with the file paths in the database and throws an Inconsistent Media Location error when they do not match.

To fix this issue, verify that the `IMMICH_MEDIA_LOCATION` environment variable and `UPLOAD_LOCATION` volume mount are in sync with the database paths.

If you would like to migrate from one media location to another, simply successfully start Immich on `v1.136.0` or later, then do the following steps:

1. Stop Immich
2. Update `IMMICH_MEDIA_LOCATION` to the new location
3. Update the right-hand side of the `UPLOAD_LOCATION` volume mount to the new location
4. Start up Immich

After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync.

## Schema drift

Schema drift is when the database schema is out of sync with the code. This could be the result of manual database tinkering, issues during a database restore, or something else. Schema drift can lead to data corruption, application bugs, and other unpredictable behavior. Please reconcile the differences as soon as possible. Specifically, missing `CONSTRAINT`s can result in duplicate assets being uploaded, since the server relies on a checksum `CONSTRAINT` to prevent duplicates.

---

--- METRICS ---
- Files Extracted: 90
- Estimated Token Budget: ~132552 tokens
- Canonical Reference: https://codewiki.google/github.com/immich-app/immich
