Fully verified113,011

Editorial pickPhotos and video

Immich

Immich keeps your photos and videos on your own server, uploads them automatically from your phone, and shows a familiar timeline with albums, a map, and content-based search. This recipe brings up the server, machine learning, PostgreSQL with vector search, and Valkey from one Compose file.

Overview

Immich is a photo archive for your own server that reproduces the familiar cloud gallery workflow: the Android and iOS apps upload photos and videos automatically, and the web interface shows a timeline by date, albums, a map, memories, and link sharing. It fits a family leaving Google Photos or iCloud, and a photographer who needs an archive without storage tiers.

A separate machine learning container adds content-based search without manual tags, face recognition, duplicate detection, and text recognition. All of it runs on your server: the photos never leave it.

The recipe brings up four containers — the server, machine learning, PostgreSQL 14 with vector extensions, and Valkey. Originals, thumbnails, and transcodes live in a host directory, the database in a Docker volume. The reliable level is declared partial: the database is a separate service and every container has a healthcheck, but the recipe sets no resource limits and adds no external monitoring.

Immich is under active development and still ships breaking changes regularly. Read the release notes and take a backup before every update.

Security assessment

The containers run without privileged, host networking, or Docker socket access, and use no-new-privileges; only the web port is published, and only on 127.0.0.1. PostgreSQL and Valkey are reachable only inside the Compose network. Immich has no public sign-up: the administrator creates accounts.

A photo archive is sensitive data: geotags, faces, the shape of your daily life. Expose Immich over HTTPS only, enable two-factor authentication, update the images promptly, and encrypt copies that leave the server. The PostgreSQL password sits in .env next to the recipe — keep the file at mode 600 and out of Git. The recipe sets no CPU or memory limits, so on a small server machine learning jobs can starve the other services; add deploy.resources or disable machine learning if that matters.

The immich-server and immich-machine-learning images are scanned with Trivy; the result and the CVE list are shown on the card. Valkey and PostgreSQL are pinned by digest and their tags do not follow the application version, so they are outside the automated check — update them together with the upstream Compose file.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version v3.1.0Aug 30, 2026, 10:10 PM UTC

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

No changesAug 30, 2026, 10:10 PM UTC

Outdated Docker images

Pinned image tags checked for newer versions.

Not checkedNot checked

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

v3.1.0v3.1.0
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Migration, majors cannot be skipped. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-08-30.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Ready

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

The recipe covers every requirement at this level.

Ready

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

The recipe covers every requirement at this level.

Partial

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Immich bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/immich.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

Screenshots

Screenshots have not been added yet.

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

1. Check your Ubuntu or Debian server

Upstream requires at least 2 CPU cores and 6 GB of RAM and recommends 4 cores and 8 GB. Budget disk space for the library plus 10–20 % for thumbnails and transcodes: this recipe stores both originals and generated files. You need Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+.

docker --version
docker compose version
nproc && free -m && df -h /srv

Since v3 the machine learning container on amd64 requires the x86-64-v2 microarchitecture level. Check it before installing:

/usr/bin/ld.so --help | grep -m1 x86-64-v2

If the line is missing, the ML container will not start: on such a server your only option is to disable machine learning and lose smart search and face recognition.

2. Prepare the files and variables

Put the recipe files in their own directory and create a private .env:

mkdir -p ~/services/immich
cd ~/services/immich
cp .env.example .env
chmod 600 .env

IMMICH_VERSION pins the version of every Immich image; IMMICH_PORT sets the local port; IMMICH_UPLOAD_LOCATION is the host directory holding originals, thumbnails, transcodes, and automatic database dumps; IMMICH_DB_VOLUME and IMMICH_MODEL_CACHE_VOLUME name the Docker volumes for PostgreSQL and the model cache; IMMICH_DB_USERNAME, IMMICH_DB_DATABASE_NAME, and IMMICH_DB_PASSWORD configure database access; TZ sets the time zone.

Always replace the database password and point the library at a disk with free space:

sed -i "s|^IMMICH_DB_PASSWORD=.*|IMMICH_DB_PASSWORD=$(openssl rand -hex 24)|" .env
sudo mkdir -p /srv/immich/library
sed -i "s|^IMMICH_UPLOAD_LOCATION=.*|IMMICH_UPLOAD_LOCATION=/srv/immich/library|" .env

The password may contain only A-Za-z0-9. It cannot be changed after the first start without recreating the database, so set it now.

3. Create the administrator

Keep the port on localhost only and start the stack:

docker compose pull
docker compose up -d
docker compose ps
ssh -L 2283:127.0.0.1:2283 user@server.example

The first start takes longer than usual because the server applies database migrations. Open http://localhost:2283, create an account — the first one becomes the administrator — and complete the setup wizard. Immich has no public sign-up: the administrator creates other users under Administration → Users.

The Android and iOS apps connect to the same address and add background upload of new photos.

Running on a VPS

On a VPS keep the 127.0.0.1 bind, block port 2283 from outside, and publish the service only through an HTTPS reverse proxy. Check the stack and the API:

docker compose up -d
docker compose ps
curl --fail http://127.0.0.1:2283/api/server/ping

All four containers must report healthy. Machine learning downloads its models on the first job, so CPU usage is high for the first minutes after an upload — that is expected.

Access from a local network

Without TLS use an SSH tunnel or a VPN. If the reverse proxy runs on another host in a trusted LAN, replace 127.0.0.1 in compose.yaml with the server’s LAN address and restrict the port to the proxy address in the firewall. Do not publish Immich on 0.0.0.0 without network restrictions: access to the timeline means access to the whole photo archive and its geotags.

Domain and HTTPS

Replace photos.example.com with your single domain in the file you pick from proxy/. proxy/Caddyfile obtains a certificate automatically; proxy/nginx.conf expects a Certbot certificate; proxy/traefik.yaml uses the letsencrypt resolver. For Traefik in a container, replace 127.0.0.1 with a host gateway it can reach.

All three samples lift the request body size limit and raise timeouts: without that, video uploads from a phone break. Verify the external endpoint:

curl --fail https://photos.example.com/api/server/ping

Backup

chmod +x backup.sh restore.sh
./backup.sh

The script stops immich-server and immich-machine-learning, takes a PostgreSQL dump with pg_dump, archives the library directory, and starts the containers again. Upstream considers copying the PostgreSQL data directory instead of a dump unsafe, so this recipe always uses a dump.

The archive holds originals, thumbnails, and the whole database with metadata and faces: encrypt it and keep at least one copy off the server. Immich also writes automatic database dumps to UPLOAD_LOCATION/backups — daily at 02:00 keeping the last 14 by default; they do not replace a copy of the files.

Restore

Restoring replaces both the database and the library directory:

./restore.sh ./backups/immich-YYYYMMDDTHHMMSSZ.tar
docker compose ps
curl --fail http://127.0.0.1:2283/api/server/ping

Before replacing anything the script takes a safety copy of the current data, then removes the database volume and loads the dump into a clean database. That is an upstream requirement: a dump must not be restored over a schema the server has already used. The database and the files must come from the same snapshot, otherwise the timeline will show entries without files.

Update

Take a backup and read the release notes: Immich regularly ships changes that require manual steps. Change only the pinned IMMICH_VERSION, then run:

./backup.sh
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 immich-server

Upgrade one version at a time and do not skip major releases: database migrations assume an upgrade from the previous version.

Rollback

Immich database migrations are irreversible. Starting an older image against a database already migrated by a newer version makes the server fail to start. Restore the pinned IMMICH_VERSION in .env and restore the archive you created before the update:

docker compose pull
./restore.sh ./backups/immich-before-update.tar
docker compose ps

Photos uploaded after the update are not in that archive. Copy them out of UPLOAD_LOCATION separately before rolling back if you need them.

Stopping and complete removal

docker compose down removes the containers but keeps the database and the library. Complete, irreversible removal after verifying a backup:

docker compose down
docker volume rm immich-database immich-model-cache
sudo rm -rf /srv/immich/library
rm -rf ~/services/immich

Sources: requirements, Docker Compose install, environment variables, backup and restore, and reverse proxy.

Troubleshooting

One of the containers never becomes healthy

docker compose ps
docker compose logs --tail=200 immich-server
docker compose logs --tail=100 database
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q immich-server)"

immich-server will not start until the database passes its healthcheck. If the database dies right after startup, the usual cause is volume permissions or a changed IMMICH_DB_PASSWORD against an existing database: the password is set at initialization and is not changed by the variable afterwards.

The machine learning container restarts or crashes

On amd64, since v3 the image needs the x86-64-v2 microarchitecture level:

/usr/bin/ld.so --help | grep -m1 x86-64-v2
docker compose logs --tail=100 immich-machine-learning

If it is unsupported, turn machine learning off under Administration → Settings → Machine Learning and remove the service from compose.yaml. Smart search, face recognition, duplicate detection, and OCR will not work in that case.

Uploads from the phone break on large videos

The limit comes from the reverse proxy, not from Immich. Nginx needs client_max_body_size 0 and raised proxy_read_timeout and proxy_send_timeout, Caddy needs request_body max_size, and Traefik needs the buffering middleware with maxRequestBodyBytes: 0. All three samples in proxy/ already carry these settings.

The disk fills up although there are few photos

Besides originals, Immich stores thumbnails and transcoded videos. Check what takes the space:

sudo du -sh /srv/immich/library/*
docker system df -v | grep immich

The backups directory holds automatic database dumps; their retention is set under Administration → Settings → Backup. The model cache in the immich-model-cache volume can be deleted — it will be downloaded again.

Smart search returns nothing

Check that the jobs ran: Administration → Job Queues → Smart Search. The default CLIP model understands English queries only. Searching in another language requires a multilingual model, selected under Administration → Settings → Machine Learning → Smart Search; after changing the model, re-run the Smart Search job for the whole library.

The server does not start after an update

Look at the migration logs:

docker compose logs --tail=200 immich-server | grep -i migration

Do not start the previous image against a migrated database — the only rollback is restoring the archive taken before the update. Skipped major versions also cause migration errors: upgrade one version at a time.

Empty cards in the timeline after a restore

The database and the files came from different snapshots. Restore a consistent pair from an archive created by backup.sh, and never mix database.sql.gz from one archive with library.tar.gz from another.

restore.sh fails with a psql error

A dump must not be loaded into a database the server has already used. The script removes the database volume before restoring; if you restore by hand, make sure the database is empty and that immich-server has never started against it.

Official sources

Latest release: 2026-07-29 · GitHub Stars: 113,011 · metadata checked Aug 30, 2026, 10:10 PM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.