Software / Homelab

Immich v3.1.0 self-hosted photo server

Hands-on Immich v3.1.0: 24s cold start, 2.42 GiB idle, 743.7% peak ML CPU and 5 assets per second upload, measured on a laptop-class box.

Acquisition
Free and open source, AGPLv3
Tested for
1 days
Disclosure
Nothing here is sponsored

As tested

Specifications as tested
Version Immich v3.1.0, pinned IMMICH_VERSION=v3
Tested on i5-10210U, 4 cores / 8 threads, 7.62 GiB to Docker
Host Windows 11 Pro, Docker Desktop 29.6.2, WSL2
Library 120 generated JPEGs, 35.0 MB, full EXIF and GPS
Cold start 24.4 s from up -d to API responding
Idle memory 2.42 GiB across four containers
Peak ML CPU 743.7% on eight threads
Disk cost 6.1 GB images + 823.6 MB models + 1.35x library

A full install, 120 uploads and a machine-learning run on a laptop-class box. Immich v3.1.0 never wobbled. It is also not the lightweight photo app the name suggests.

24.4s
Cold start
5.01
Assets per second
2.42GiB
Idle memory
743.7%
Peak ML CPU

How this was tested

Everything below came off one machine: an i5-10210U with four cores and eight threads, 7.62 GiB handed to Docker Desktop 29.6.2 on the WSL2 backend.

The 120-image library was generated locally rather than lifted from a real photo collection. Correct resolutions from 1920×1080 up to 6000×4000, with genuine EXIF: camera make and model, exposure, ISO, focal length, timestamps, and GPS across five cities.

That gives trustworthy figures for upload speed, indexing cost, CPU load and storage overhead. It tells you nothing about face recognition, because synthetic landscapes contain no faces.

Installation

Two downloads and one command. It works, and it is quick.

167.2s
Cold image pull
3.9s
up -d returns
24.4s
API responding
33s
All containers healthy

The first-boot log looks alarming and is not. Warnings that the smart_search and face_search tables do not exist, and that relation "system_metadata" does not exist, are simply migrations that have not run yet.

Two traps on Windows

The stock .env sets DB_DATA_LOCATION=./postgres. On Docker Desktop for Windows that resolves to a Windows path, and Postgres initialises with --data-checksums, which needs Unix ownership semantics that the path cannot provide.

So the database has to move into a named volume. That volume then has to be declared, because the official compose file does not declare it.

docker-compose.yml
# Verified on Windows 11 + Docker Desktop 29.6.2 (WSL2), 9 Aug 2026
# Without this declaration Compose fails with an undefined-volume error
volumes:
  model-cache:
  immich_pgdata:

Machine learning will take every core you own

This is the number that matters.

Bar chart showing immich_machine_learning peaking at 743.7 percent CPU and immich_server at 214.6 percent

Sampled every four seconds across 420 seconds while the backlog processed, the ML container peaked at 743.7% CPU on an eight-thread chip. The stack as a whole hit 827%.

On a machine you also use for other things, you will notice. If Immich shares a box with anything latency-sensitive, cap it with Docker resource limits on day one rather than after the first complaint.

What it costs at rest

Stacked bar showing 2.42 GiB idle memory split across server, postgres, machine learning and redis

The server container alone holds 1.66 GiB with nothing whatsoever in the library.

That one figure explains the documented 6 GB minimum better than the documentation does, and it is why a 4 GB box cannot work. At 4 GB the docs tell you to disable machine learning, which removes face recognition and smart search, which is most of the reason to choose Immich at all.

Storage is the other quiet cost. 35.0 MB of originals became 366 files and 46.8 MB on disk, roughly three files per asset once thumbnails and previews exist. Budget about 1.35x your library size.

Upload and search both hold up

120 assets uploaded sequentially through the REST API in 23.9 seconds. All 120 succeeded. None failed, none duplicated. Median 0.189 seconds each.

All 120 CLIP embeddings generated. I then searched for green hills under a blue sky, a phrase appearing in no filename, tag or metadata anywhere in the library.

The API returned 100 correct matches in 1.49 seconds, and the web interface showed visually correct results. The map view clustered all 120 assets from embedded GPS, and the timeline grouped them properly by capture date.

Stability

docker compose restart brought the API back in 13.9 seconds, faster than the 24.4 second cold start, with no data loss and no intervention. Nothing crashed at any point during testing.

One correction worth recording. My first smart-search attempt hung on a spinner and I nearly wrote it up as a bug. It was not. I had built the URL as ?query=green+hills when Immich expects JSON. My error, not a defect.

Against Google Photos and OneDrive

Immich’s pitch is privacy and control. The price is that you become the operations team: no storage bill, but you own backups, updates, uptime and security.

Google’s search remains the benchmark, and Immich’s semantic search needs indexing time before it starts feeling clever.

For Microsoft-shaped readers, OneDrive is not competing on the same axis. It is excellent sync with a photo view attached, it speaks Entra ID, it is already in your tenant, and nobody maintains it at 2am. What it does not give you is face grouping, semantic search or a map view at this quality.

If you already pay for Microsoft 365 you already have the storage, and that is a genuinely strong argument for doing nothing.

Who should run it

Run it if you have a spare box with 8 GB and four cores, you want off Google Photos, and a Docker stack does not intimidate you.

Skip it if your server has 4 GB, if you already pay for Microsoft 365 or iCloud and are content, or if 3-2-1 backup is not already part of your vocabulary. Self-hosting means nobody is coming to help when the disk dies.

What holds up

  • Cold start in 24.4 seconds and a clean restart in 13.9, with zero failures across 120 uploads.
  • CLIP semantic search returned 100 correct matches for a phrase held in no filename or metadata, in 1.49 seconds.
  • AGPLv3 with every feature free, no paywalled tiers, and a funded team behind it.

What does not

  • The machine-learning container peaked at 743.7% CPU and will starve anything sharing the box.
  • It idles at 2.42 GiB with an empty library, so the documented 4 GB floor is not realistic with ML on.
  • The Windows quick-start is wrong twice: Postgres cannot use a bind mount, and the required volume is undeclared.

Common questions

Six gigabytes minimum and eight recommended, and the measurements back that up. I saw 2.42 GiB at idle with an empty library and 2.97 GB peak during machine-learning work. On 4 GB you have to disable ML, which removes face recognition and smart search.

No, but machine learning is the heaviest part of the stack. On CPU alone mine peaked at 743.7% across eight threads. CUDA, OpenVINO, ROCm, ARM NN and RKNN are all supported and worth using if you have the hardware.

The four images are 6.1 GB before any photo exists, models add 823.6 MB on first job, and the database sat at 311 MB. Thumbnails and previews took 35 MB of originals to 46.8 MB on disk, so budget roughly 1.35x your library size.

Not directly. The documentation says never to forward port 2283 straight to the internet and states plainly that severe vulnerabilities cannot be ruled out. Use WireGuard, a reverse proxy with a real certificate, or Cloudflare Access.

My test library was generated rather than photographed, so it contains no faces. Scoring face recognition off that would be inventing a result. It remains untested here.