My Immich server sat on 2.7.5 for six months while 3.x came and went. One line in .env was the reason.
The short answer: if your .env says IMMICH_VERSION=v2, docker compose pull will only ever fetch version 2 images. The tag pins the major version. Change it to v3, pull, and start. On my server the whole upgrade from 2.7.5 to 3.3.1 took under three minutes including a database dump, and the photo, face and people counts were identical afterwards. The one thing to check first is which vector extension your database uses, because v3 drops the old one.
What you’ll learn
- Why a pinned tag keeps you on an old major version without any error
- Three checks to run before moving to v3
- The exact steps, with the timings I measured
- What the “schema drift” warning on first start meant in my case
Evidence note: this is based on the upgrade of my own Immich server on 10 October 2026. The commands, timings and log lines are from that run. Addresses and paths are replaced with placeholders. I did not test the mobile app afterwards and I did not test a rollback; both are flagged below.
The symptom
Immich had been showing me a notice that a new version was available. I ignored it. I use Immich as an archive and rarely log in, so the notice was easy to leave for another day.
When I finally looked, the server was on 2.7.5, a build from April 2026. Version 3.0.0 had been released in July and 3.3.1 in October. And the usual update command would not have helped: as the server was configured, docker compose pull could never have fetched version 3.
My setup
| Part | What it is |
|---|---|
| Host | Intel NUC8i7BEH (i7-8559U, 16 GB RAM), Ubuntu 24.04, Docker Compose 5.5.1 |
| Photo library | On a Synology DS723+ over NFS |
| Database | Postgres 14 on the NUC’s local disk |
| Library size | 54,413 items, 71,022 detected faces, 6,281 people |
One detail matters for this story. In September I moved this server from an old Xeon workstation to the NUC. I copied the Immich folder across, restored the database, and everything came back with the same photo count. The .env file came across too, unchanged since April. So the version pin survived a complete hardware change without anyone looking at it.
Why nothing looked wrong
This is the part worth knowing. A pinned server does not fail. A pull succeeds, reports that the images are up to date, and it is telling the truth: you have the newest image for the tag you asked for. There is no error to search for, only a version number that stops moving.
The cause: the tag pins the major version
The compose file builds the image name from a variable:
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
and my .env had:
IMMICH_VERSION=v2
v2 is a moving tag that follows the newest 2.x release. The last 2.x release was 2.7.5, so that is where it stops, permanently. Nothing is broken and nothing warns you. The v3 release notes say the same thing in one line: set IMMICH_VERSION to v3, then run the usual update commands.
Three checks before you change the tag
The v3 migration guide lists a lot of breaking changes, but most of them are API changes that only affect third-party tools. These are the three that could affect a normal self-hosted install, and how I checked each one.
1. Which vector extension does your database use? Version 3 removes support for pgvecto.rs. If you have been running Immich since before 1.133 and never did the VectorChord migration, do that first.
docker exec immich_postgres psql -U postgres -d immich -At \
-c "select extname, extversion from pg_extension"
Mine listed vchord 0.4.3 and vector 0.8.1, and both search indexes were built with vchordrq. That is VectorChord, so this change did not apply to me. If you see vectors in that list instead of vchord, stop and follow Immich’s VectorChord migration guide.
2. Do you set any of the removed environment variables? Three were removed in v3: IMMICH_MACHINE_LEARNING_PING_TIMEOUT, MACHINE_LEARNING_PRELOAD__CLIP and MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION.
grep -cE "MACHINE_LEARNING_PRELOAD|PING_TIMEOUT|DB_VECTOR_EXTENSION" .env
Mine returned 0.
3. Is your CPU new enough? The machine-learning container now needs an x86-64-v2 processor. Almost anything from the last fifteen years qualifies. To confirm:
/lib64/ld-linux-x86-64.so.2 --help | grep "x86-64-v"
My 2018 NUC reported v2 and v3 as supported.
I also compared my compose file with the one attached to the 3.3.1 release. The Postgres image was identical. The only differences were the pinned digest of the cache container and its health check, so I kept my file as it was.
The upgrade
Back up the database first. Immich already writes a nightly dump into the library’s backups folder, and mine had one from 02:00 that morning. I took a fresh one anyway:
docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=postgres \
| gzip > /path/to/backups/immich-db.sql.gz
gzip -t /path/to/backups/immich-db.sql.gz && echo ok
Then the change itself:
cp -p .env docker-compose.yml /path/to/backups/
sed -i 's/^IMMICH_VERSION=v2$/IMMICH_VERSION=v3/' .env
docker compose pull
docker compose up -d
What it took on my server:
| Step | Time | Notes |
|---|---|---|
| Database dump | 62 s | 333 MB compressed |
| Image pull | 39 s | Server image 2.46 GB, machine-learning image 1.11 GB |
| Start to “healthy” | 32 s | Database migrations ran during this window |
The server log showed a run of migrations, each ending in succeeded, then Finished running migrations, then:
Immich Server is listening on http://[::1]:2283 [v3.3.1] [production]
Verification
I counted rows before and after:
| Before (2.7.5) | After (3.3.1) | |
|---|---|---|
| Assets | 54,413 | 54,413 |
| Faces | 71,022 | 71,022 |
| People | 6,281 | 6,281 |
| Search embeddings | 51,094 | 51,094 |
curl -s http://localhost:2283/api/server/version
# {"major":3,"minor":3,"patch":1,"prerelease":null}
The web interface loaded, all four containers reported healthy, and the server log had no error lines.
The “schema drift” warning
On first start the log printed this, which looks alarming:
WARN Detected schema drift.
- The index "geodata_places"."IDX_geodata_gist_earthcoord" is missing and needs to be created
- The constraint "geodata_places"."geodata_places_pkey" (primary-key) is missing and needs to be created
In my case it was a timing artefact. Immich was re-importing its place-name table at that moment (228,594 records in under six seconds, according to the log), and the check ran while that table was being rebuilt. A minute later:
docker exec immich_server immich-admin schema-check
# Migrations are up to date
# No schema drift detected
If the warning names only geodata_places and the schema check is clean afterwards, I would not worry about it. If it names other tables, or it is still there after the import finishes, that is a different situation and worth raising with the Immich project.
The mistake in my own script
I ran the upgrade through a small wrapper script that stopped if anything looked wrong. It stopped, wrongly. After the pull it checked that the v3 image existed by searching the text output of docker image ls, the search did not match, and the script put IMMICH_VERSION back to v2 and exited. Nothing had been started yet, so no harm was done, but the upgrade had not happened either.
The image was there. The reliable check is to ask Docker directly:
docker image inspect ghcr.io/immich-app/immich-server:v3 --format '{{.Created}}'
I reran the second half by hand. The lesson is small but general: do not parse human-readable command output in a safety check.
What I did not test
- The mobile app. Version 3 removes the legacy timeline in the app and changes the editor. I use Immich as an archive and do not back up my phone to it, so I have not checked the app against the new server.
- A rollback. The migrations change the database, so going back means restoring the dump and setting the tag to
v2. I have the dump. I have not tried restoring it.
Checklist
- Look at
IMMICH_VERSIONin.env. If it saysv2, that is why you are not getting v3. - Check
pg_extensionforvchord. If you seevectors, migrate to VectorChord first. - Check
.envfor the three removed variables. - Dump the database and copy
.envanddocker-compose.ymlsomewhere safe. - Set
IMMICH_VERSION=v3, thendocker compose pull && docker compose up -d. - Confirm the version, compare your counts, and run
immich-admin schema-check. - Update the mobile app.
Takeaway
A version pin is a decision you make once and then forget. Mine outlived the hardware it was made on. If a self-hosted app tells you an update exists and pulling does nothing, read the tag before you suspect the network.
AI assisted with running the upgrade and drafting this article. The server, the commands and every figure here are from my own Immich installation.
