Immich: Self-Hosted Photo and Video Management
GitHub Repo
AGPL-3.0
November 13, 2026 at 03:38 PM
0 views

Immich: Self-Hosted Photo and Video Management

@immich-appProject Author

What Immich is

Immich is a self-hosted photo and video management application. The README describes it in one line as a "High performance self-hosted photo and video management solution", and the feature table that follows makes the intent clear: a phone app that backs up the camera roll to your own server, a web interface for browsing and administering the library, and a set of machine-learning features (face clustering, natural-language search, text recognition in images) that run on that same server rather than in someone else's cloud. It is aimed at people who want the convenience of a hosted photo service while keeping originals on hardware they control.

The project is written mostly in TypeScript, with a Flutter mobile app and a Python machine-learning service, and it is licensed under the GNU Affero General Public License v3. It is deployed as a set of Docker containers, and the documentation treats Docker Compose as the recommended production setup. The docs also cover Unraid, TrueNAS, Portainer and Kubernetes (via an official Helm chart).

The README no longer carries a general "under active development" disclaimer. What it does carry, at the very top, is a warning to "Always follow 3-2-1 backup plan for your precious photos and videos", and the documentation repeats that message in several places. That framing is worth taking at face value: Immich is designed to be the primary home for a photo library, but it does not take responsibility for backing that library up, which is covered in detail below.

Development is funded through paid product keys. The FAQ says the team works on Immich full-time, linking to an announcement that Immich joined FUTO, and the support page says the key can be bought for one user or a whole server while stating that "all features will be free, forever" and nothing will be put behind a paywall.

How it works

The architecture page describes "a traditional client-server design, with a dedicated database for data persistence". Clients talk to the backend over REST, and the server reaches Redis, Postgres, the machine-learning service and the file system through repository interfaces.

The four containers

The default docker-compose.yml defines four services:

  • immich-server handles REST API requests and executes background jobs such as thumbnail generation, metadata extraction and video transcoding. It publishes port 2283 and mounts UPLOAD_LOCATION at /data. It is a Node.js application built on Nest.js, Express and the Kysely query builder.
  • immich-machine-learning is a Python service using FastAPI. All machine-learning work is externalised here, and models are cached in a model-cache volume. The default port is 3003.
  • database runs Immich's own Postgres image (ghcr.io/immich-app/postgres), which in the current compose file is tagged for Postgres 14 with the VectorChord and pgvecto.rs extensions. It is initialised with data checksums enabled.
  • redis runs a Valkey image and is used through BullMQ for job queues.

The job pipeline

Most of the heavy lifting happens asynchronously. When an asset arrives, the server queues work in Redis and workers pick it up. The architecture page lists jobs for thumbnails, metadata extraction, transcoding, smart search, facial recognition, storage template migration, XMP sidecars and housekeeping. Some jobs trigger others: smart search and facial recognition depend on thumbnails and run automatically once a thumbnail exists. Job progress and concurrency are adjustable from the administration UI.

Machine learning as a separate service

The service downloads and caches the requested model, and all models are in ONNX format. The docs explain the reason for splitting this out: Python suits the ML work and has specific hardware requirements, and a separate container "makes it possible to run the container on a separate machine, or easily disable it entirely". By default an idle model is unloaded after 300 seconds (MACHINE_LEARNING_MODEL_TTL).

Search uses Postgres for both metadata and vector search. The searching page says contextual search is powered by the VectorChord extension, using CLIP-style models that convert images and query text into embeddings and compare them. The FAQ notes that no browsable tags or labels are generated.

Clients

There are three main clients: the mobile app for Android and iOS, written in Dart with Flutter; the web app, built with SvelteKit and Tailwind CSS; and a CLI distributed as an npm package for bulk uploads. All three use REST clients generated from the server's OpenAPI specification.

Key features

  • Mobile backup. The app backs up selected device albums, with foreground backup when the app is open and background backup scheduled by the operating system. Duplicate assets are detected so the same file is not uploaded twice.
  • Free Up Space. The mobile app can remove local copies of media that are already on the server, with a cutoff date, options to keep favourites or specific albums, and a review screen before anything moves to the device trash.
  • Multi-user with sharing. Albums can be shared with other users on the same instance as editors or viewers; partner sharing exposes a whole library to another user; public links can carry an expiry date and a password.
  • Facial recognition and clustering. Faces are grouped into people using an algorithm the docs describe as derived from DBSCAN, adapted for incremental clustering.
  • Smart search and filters. Natural-language search over image content, plus filters for people, file name, folder path, description, OCR text, location from reverse geocoding, tags, camera and lens, date range, media type and star rating. Multilingual CLIP models are available for non-English queries.
  • OCR. The default configuration enables text recognition with a PP-OCRv5_mobile model, so text inside images becomes searchable.
  • External libraries. Existing folders on disk can be mounted into the container and scanned into a user's timeline without copying them into Immich's storage, with glob exclusion patterns, a nightly rescan and an experimental file watcher.
  • Storage templates. By default originals are stored under random UUID file names; an admin can enable a template such as Year/Year-Month-Day/Filename.Extension and run a migration job to reorganise existing files.
  • Broad format support. The README lists raw formats, Live Photo and Motion Photo playback and 360-degree images on the web; the supported formats page lists HEIC, AVIF, JPEG XL, PSD and many others.
  • Authentication options. Password login, API keys and OpenID Connect, with Authentik, Authelia, Okta, Google and Keycloak named as compatible providers.
  • Built-in database dumps and integrity checks. Daily automatic database backups with restore from the UI, and nightly checks for untracked files, missing files and checksum mismatches.
  • Hardware acceleration. Optional GPU or NPU acceleration for transcoding (NVENC, Quick Sync, RKMPP, VAAPI) and for machine learning (ARM NN, CUDA, ROCm, OpenVINO, RKNN), both marked experimental in the docs.

Getting started

The quick start asks for "a system with at least 6GB of RAM and 2 CPU cores" and Docker. The full requirements page recommends a 64-bit Linux host with 8GB of RAM and 4 cores, Docker Engine with the Compose plugin, and a Unix-compatible filesystem. Create a directory for the deployment:

mkdir ./immich-app
cd ./immich-app

Download the compose file and the example environment file from the latest release:

wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env

The installation guide pulls these from the release rather than from the repository on purpose: the compose file on main carries a comment saying it "may not be compatible with the latest release". The default .env looks like this:

# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables

# The location where your uploaded files are stored
UPLOAD_LOCATION=./library

# The location where your database files are stored. Network shares are not supported for the database
DB_DATA_LOCATION=./postgres

# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
# TZ=Etc/UTC

# The Immich version to use. You can pin this to a specific version like "v2.1.0"
IMMICH_VERSION=v3

# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
DB_PASSWORD=postgres

# The values below this line do not need to be changed
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

Set UPLOAD_LOCATION to a new directory with enough free space, change DB_PASSWORD (the docs recommend sticking to A-Za-z0-9 to avoid Docker parsing issues), and uncomment TZ if you want a specific timezone. DB_DATA_LOCATION must be on local storage, not a network share. Then start the stack:

docker compose up -d

Note that the command is docker compose; the requirements page says the older docker-compose binary is deprecated and not supported. Once the containers are up, open http://<machine-ip-address>:2283 and click Getting Started. The first user to register becomes the admin and can create other users. The post-install checklist then covers the storage template, installing the mobile app (App Store, Google Play, GitHub releases, Obtainium or FUTO F-Droid), choosing albums to back up and setting up server backups.

Importing existing photos

For bulk uploads from a computer, the CLI requires Node.js 22 or later and installs from npm:

npm i -g @immich/cli

A Docker image is available if Node.js is not, and the docs give this example of a recursive upload that creates albums from folder names with a concurrency of five:

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/

For a Google Photos export from Takeout, the docs recommend the community-maintained immich-go instead. For a library you want to keep in place, mount it into immich-server (read-only with :ro if you do not want Immich to delete or write sidecars there) and add it as an external library from the administration panel.

Upgrading

After reading the release notes for breaking changes, the upgrade is:

docker compose pull && docker compose up -d

The project follows semantic versioning and says it intends to limit breaking changes, including API and deployment changes, to major releases. Downgrades are not supported and patches are not backported.

Running it well: storage, machine learning and access

Where your files live

Everything Immich writes goes under UPLOAD_LOCATION: originals in upload/<userID> (or library/<userID> once the storage template is enabled), avatars in profile, generated previews in thumbs, transcoded videos in encoded-video, and database dumps in backups. The requirements page estimates that thumbnails and transcodes add 10 to 20 percent to the size of a library on average. The docs say these folders must not be edited by hand; assets are changed only through the apps.

Tuning machine learning

The default smart search model in the config file is ViT-B-32__openai and the default face model is buffalo_l. The searching page publishes the project's own tables comparing alternative CLIP and SigLIP models by memory, speed and recall. On constrained hardware the FAQ suggests lowering job concurrency, reducing transcoding threads, switching to the smaller buffalo_s face model, or moving the machine-learning container to a more powerful machine. Machine learning can also be disabled per feature or entirely, at the cost of search quality.

For GPU acceleration, the compose file includes commented extends blocks that pull a backend from hwaccel.ml.yml or hwaccel.transcoding.yml, and the ML image gets a suffix such as -cuda. Only Linux and Windows through WSL2 are supported for acceleration, and the transcoding page notes that hardware encoding typically produces larger files at lower quality than software encoding with similar settings.

Reaching it from outside the house

The remote access guide opens with a warning: never forward port 2283 directly to the internet, since that exposes the web interface over plain HTTP. It lays out three options: a VPN such as WireGuard or OpenVPN, Tailscale, or a reverse proxy with a proper certificate. For the proxy route the docs give an nginx example and list requirements: forward all headers and set Host, X-Real-IP, X-Forwarded-Proto and X-Forwarded-For, allow very large request bodies, and enable WebSockets (without them the UI shows the server as offline). Immich cannot be served on a sub-path; it needs the root of a domain or subdomain.

Use cases

  • Replacing a phone's cloud photo backup. Install the app on each family member's phone, enable backup for the camera album, and use Free Up Space once the server holds the originals.
  • Indexing an existing archive in place. A NAS full of folders from years of cameras can be mounted read-only as an external library, so Immich builds thumbnails, faces and search embeddings without touching or duplicating the originals.
  • Searching a large personal library. Queries such as a dog breed or a scene description work through CLIP search, combined with filters for a person, place, camera or date range, and OCR makes text in screenshots and documents findable.
  • Sharing events with people outside the server. Public links with expiry dates and passwords cover a wedding album or a trip without creating accounts, while shared albums with editor rights let several users contribute photos to one collection.

How it compares

The obvious comparison is with hosted services such as Google Photos and iCloud Photos, both of which the docs reference in the context of migrating or coexisting. Immich offers a similar experience while keeping originals and derived data on your own server. The trade is operational: you supply the hardware, storage, upgrades, remote access and backups. The FAQ addresses cost directly, answering "Will Immich save me money?" with "Probably not", and pointing to hardware, backup storage and your own time as the real costs of self-hosting.

The mobile docs include a specific caution for iCloud users. iCloud Photos syncs both ways, so using Free Up Space on an iPhone also deletes those photos from iCloud and other signed-in devices. If iCloud is meant to remain a second copy, the docs recommend iCloud's own Optimize iPhone Storage option instead.

For other self-hosted photo libraries, the project does not publish its own comparison. The documentation's comparison page instead links to a community-maintained comparison of open-source photo libraries, which is the better place to weigh specific alternatives.

Things to know before adopting

  • Backups are your job. The backup page says Immich "does not handle filesystem backups for you". The built-in database dumps (daily at 2:00 AM, keeping the last 14 by default) contain metadata only. You need to back up the database and at least library, upload and profile under UPLOAD_LOCATION, ideally with immich-server stopped. If not, back up the database first and the files second, so a restore never references files that are missing.
  • The database is the source of truth. Immich stores file paths and metadata in Postgres and does not rescan its own upload folders, so losing the database without a dump means losing albums, people and other metadata. Postgres should be on local SSD storage, never a network share, and the docs list unexpected power-offs, network shares and SD cards among the causes of corruption.
  • Hardware requirements are real. 6GB of RAM is the minimum and 8GB recommended; with 4GB the docs suggest disabling machine learning. Since v3 the ML container on amd64 needs a CPU with the x86-64-v2 microarchitecture level, and the last version supporting older CPUs, v2.7.5, is no longer supported.
  • Linux is the supported host. Non-Linux hosts are "strongly discouraged" and get reduced support. Docker inside LXC containers is not recommended.
  • Mobile and server versions must line up. The server is only compatible with the matching major version, while the app typically supports the current and previous major, so upgrade the apps before the server.
  • iOS background backup is limited by the OS. The FAQ explains that iOS decides when background tasks run; enabling Background App Refresh and avoiding Low Power Mode improve the odds but cannot guarantee it.
  • Network edge cases. Self-signed certificates, Basic Auth, custom headers and mutual TLS in the mobile app are described as experimental and unsupported. Cloudflare Tunnel's 100 MB upload limit can stop video uploads, and reverse proxies need large body limits.
  • External library caveats. An external library belongs to a single user, chosen at creation and not changeable later. Metadata added in Immich is not written back to external files and is lost if a file is moved. In a read-write mount, emptying the trash deletes the original file.
  • Outbound connections and telemetry. The default config enables a new-version check and reverse geocoding, and map styles point at tiles.immich.cloud; each can be configured. Prometheus metrics are off by default and only exposed when IMMICH_TELEMETRY_INCLUDE is set.
  • AGPL-3.0. The licence's network clause matters if you modify Immich and let other people use it over a network: you must offer those users the corresponding source of your modified version. Running an unmodified instance for yourself or your family raises no such obligation. Separately, the FAQ asks resellers not to use the Immich trademarks or imply official endorsement.

Project activity

As of November 2026 the repository has about 119,000 stars and 7,600 forks. It was created on 3 February 2022, is written primarily in TypeScript, and its most recent push was on 13 November 2026. The code is licensed under AGPL-3.0. The .env shipped on main pins the v3 major version, and the requirements page refers to changes made "since v3". Translations are managed on Weblate, and a public demo is available.

Enjoying this project?

Discover more amazing open-source projects on TechLogHub. We curate the best developer tools and projects.

Project
immich
Created
November 13
Last Updated
November 13, 2026 at 03:38 PM

Maintain Immich: Self-Hosted Photo and Video Management? Add the badge to your README

Show that the project is featured on TechLogHub and send readers to this write-up.

Featured on TechLogHub
[![Featured on TechLogHub](https://techloghub.com/badge-neutral.svg)](https://techloghub.com/open-source/immich?utm_source=techloghub_badge&utm_medium=referral&utm_campaign=readme)

Find more projects like this

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.