How to install Immich with Docker and leave Google Photos behind
Table of contents
- Key takeaways
- What Immich is, and whether it is still an unstable project
- What you need before you start
- Installing from the official compose file
- What is actually inside the .env file
- The machine learning container and its hardware options
- Where the library lives
- The mobile app and camera-roll backup
- The backup almost everybody gets wrong
- How upgrades work
- Immich against Nextcloud Memories and PhotoPrism
- Frequently asked questions
- Can I run Immich without the machine learning container?
- Do I need a domain and HTTPS to use the mobile app?
- Does Immich delete photos from my phone once they are uploaded?
- Conclusion
- Sources
Immich installs from two files you download from its latest release, docker-compose.yml and .env, plus one docker compose up -d. The server listens on port 2283, the mobile app uploads your camera roll on its own, and your backup has to include the database, not just the photos.
You have twenty years of photos on your phone and a monthly fee for keeping them somewhere you do not control. Immich is the self-hosted photo and video manager that turns that fee into one more container on your server: it uploads your camera roll on its own, recognises faces and searches by what is actually visible in the picture. Here is the real install, checked against v3.1.0, with the official compose file, the machine learning container and the backup almost everybody gets wrong.
Key takeaways
- Immich stopped being an experiment in October 2025: v2.0.0 was its first stable release and the old "expect bugs and data loss" banner left the README.
- Installing Immich is two files downloaded from the latest release,
docker-compose.ymland.env, plus onedocker compose up -d. The server listens on port 2283. - The machine learning container is optional, but it is what gives you faces, tagging and freeform search. On 4 GB of RAM you have to drop it.
- The database stores every file path and is never rebuilt by scanning the library folder. Copying only the photos leaves you with a library Immich cannot read.
- Pinning
IMMICH_VERSIONmatters: downgrading is not supported, not even within the same minor version.
What Immich is, and whether it is still an unstable project
Immich is a photo and video manager you install on your own machine, licensed AGPL-3.0, with 112,964 stars on its GitHub repository[1] as of late August 2026. The project started in February 2022 and for almost four years its README carried a disclaimer section that read, among other things, "Do not use the app as the only way to store your photos and videos". That section was still there in v1.135.0.
That warning is gone, and it is worth saying loudly because half the internet keeps copying it. The v2.0.0 release notes[2], published on 1 October 2025, put it in writing: "This release marks the first stable version of Immich". What remains in the README today is a different and rather more sensible warning, the one about always following a 3-2-1 backup strategy for your photos.
The version everything below was checked against is v3.1.0, published on 29 July 2026. The v3 branch landed on 2 July 2026 with breaking changes, though most of them touch the API and therefore third-party tools: for anyone who only uses the web and the mobile app, updating worked exactly as it always had.
What you need before you start
The official requirements are specific, and worth reading before buying hardware:
- A 64-bit Linux or *nix operating system. Windows and macOS work worse with Docker and the documentation discourages them.
- 6 GB of RAM minimum, 8 GB recommended. On 4 GB you can run Immich with machine learning disabled.
- Two cores minimum, four recommended. There are
amd64andarm64images. - Since v3,
amd64needs anx86-64-v2class processor, roughly 2012 onwards. - A filesystem with real user and group permissions, such as EXT4, ZFS or APFS. The database cannot live on a network share, nor on NTFS or FAT.
One sizing detail that catches people out: thumbnails and transcoded videos add 10 to 20 % on top of your original library size. Upload 500 GB of camera roll and budget 600.
Installing from the official compose file
The documentation insists on a point everyone skips: use the docker-compose.yml from the current release, not the one on the main branch, because main may not be compatible with the latest release. The official steps are these:
- Create the project directory with
mkdir ./immich-appand enter it. - Download
docker-compose.ymlandexample.envfrom the latest release. - Rename the second one to
.envand editUPLOAD_LOCATION,DB_PASSWORDand, if you want,TZ. - Bring the stack up with
docker compose up -d. - Open
http://<server-ip>:2283and create the admin account.
In commands, without decoration:
mkdir ./immich-app && cd ./immich-app
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
## edit .env before starting
docker compose up -d
The file you download declares four services. This is the real skeleton, trimmed to what matters:
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
restart: always
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:8e8d64b405ce18f41b8e5ee20aa4687a8ed0022d1298f2ce31cdcf3a76e09411
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
volumes:
model-cache:
There are two design decisions in there that explain why upgrading Immich is so undramatic. First: Redis is not Redis, it is Valkey 9, and both that image and the PostgreSQL one are pinned by digest rather than by a moving tag. A docker compose pull updates the server and the model runner, but will not swap your database engine on you. Second: there is exactly one named volume, model-cache. Everything else is a mount against paths you chose, which you will appreciate the day you move the machine. If you are unclear on when a volume beats a bind mount, we cover it in Docker volumes and bind mounts.
What is actually inside the .env file
The official example.env is short and has no filler in it:
# where your uploaded files are stored
UPLOAD_LOCATION=./library
## where the database lives (network shares are not supported)
DB_DATA_LOCATION=./postgres
## TZ=Etc/UTC
## the Immich version; you can pin it to something like "v3.1.0"
IMMICH_VERSION=v3
## postgres connection secret: change it to a random string
## use only A-Za-z0-9 characters, no spaces or symbols
DB_PASSWORD=postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
The character restriction on DB_PASSWORD is not arbitrary: the string travels inside a connection URL and symbols break the parse. And IMMICH_VERSION=v3 is a major-version tag, so it keeps you on the 3 branch without jumping to 4 by itself. If you would rather that secret did not sit in plain text inside the project directory, environment variables and secrets in Docker Compose covers alternatives that fit here without touching the official compose file.
The machine learning container and its hardware options
immich-machine-learning does three things people take for granted: it detects and groups faces, tags image content, and feeds smart search. That search uses CLIP models and the VectorChord extension for PostgreSQL, so you can type "dog on the beach at sunset" without any of those words appearing in the photo metadata. There are multilingual model families (nllb, xlm, siglip2) and Spanish is among the supported languages, so nobody has to search in English.
By default all of that runs on the CPU, which is slow but works. The hardware acceleration documentation[3] describes five alternatives: CUDA for NVIDIA, ROCm for AMD, OpenVINO for Intel integrated graphics, ARM NN for Mali and RKNN for Rockchip SoCs. Enabling one is a three-line change: download hwaccel.ml.yml, add the suffix to the image tag and uncomment the extends block.
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda
extends:
file: hwaccel.ml.yml
service: cuda
The small print matters and the documentation states it plainly. CUDA requires compute capability 5.2 or higher and driver 545 or newer. ROCm needs 35 GiB of free disk just for the image. ARM NN accelerates tagging but not search latency, because of model incompatibilities. And OpenVINO on integrated graphics is more prone to problems and uses more memory than the CPU path, so it is not an automatic win.
If your machine has 4 GB of RAM the decision is a different one: drop the service from the compose file and keep a photo manager that syncs, sorts by date and shows the map. Still useful. It simply will not search by content.
Where the library lives
Everything a user uploads hangs off UPLOAD_LOCATION, in this structure:
| Folder | What it holds | Regenerable |
|---|---|---|
upload/ |
originals uploaded from web, mobile or command line | no |
library/ |
originals once you enable the storage template | no |
profile/ |
each user’s avatar | no |
thumbs/ |
thumbnails and previews, including face crops | yes |
encoded-video/ |
videos re-encoded for compatibility | yes |
backups/ |
automatic database dumps | yes |
The first three are the ones that cannot be lost. The other three rebuild by re-running the thumbnail and transcoding jobs, at the cost of a few CPU hours. The database itself lives separately under DB_DATA_LOCATION, and that separation is deliberate.
On the UPLOAD_LOCATION folders the documentation is blunt: do not touch the files inside them for any reason other than taking a backup. Renaming or deleting something by hand leaves orphaned rows in the database and files nothing references any more.
The mobile app and camera-roll backup
The app is on Google Play and the App Store, and signed APKs ship with every GitHub release. Configuration is a single value: the server address, in the form http://<machine-ip-address>:2283. From there, on the cloud screen you pick which phone albums get backed up and press "Enable Backup".
Two behaviours are worth separating. Foreground backup starts when you open the app and is the reliable one. Background backup depends on your phone’s operating system and its battery policies, so on Android it pays to exclude Immich from battery optimisation if you want it uploading without being opened.
One specific warning for iCloud users: if you free up space from Immich, the photos removed from the camera roll then live only on your server and disappear from iCloud across every device signed into that Apple account. It is exactly what you asked for, but understand it before tapping.
The backup almost everybody gets wrong
This is where libraries get lost. Plenty of people copy the photos folder, see the files sitting there and relax. The backup documentation explains why that is not enough: "Immich stores file paths and user metadata in the database. It does not scan the library folder, so database backups are essential". Without the database you have a pile of files with opaque names and no way for Immich to recognise them.
Immich does help a little: it writes automatic dumps into UPLOAD_LOCATION/backups, by default the last 14, one daily at 2:00 AM, adjustable from the admin settings. But those dumps hold metadata, not photos, and they live on the same disk as everything else. They are insurance against a botched migration, not against a dead drive.
The manual dump, exactly as it appears in the backup and restore documentation[4]:
docker exec -t immich_postgres pg_dump --clean --if-exists
--dbname=immich --username=postgres
| gzip > /backups/immich/dump-$(date +%F).sql.gz

And now the part nobody tells you: ordering. If you copy the database and the files while Immich is running, the two can drift apart. The clean answer is to stop the immich-server container for the duration of the backup. Where that is not viable, the documentation recommends backing up the database first and the filesystem second. Worst case you end up with files the database knows nothing about, which can be re-uploaded by hand. The other way round leaves you with a database pointing at photos that are not in the backup, and those are broken assets.
To get the result off the machine, encrypted and with retention, the natural move is to hang this off an encrypted Restic backup and schedule the dump just before it.
Two more restore caveats: it only works on a completely fresh installation where the server has never started, and the procedure changed in v2.5.0, so an older dump needs the instructions for its own version.
How upgrades work
The routine upgrade is two commands:
docker compose pull && docker compose up -d
docker image prune
With three rules around them. If you pinned IMMICH_VERSION to a major version, you have to raise it by hand before a branch jump, like the v2 to v3 move of July 2026. Downgrading is not supported, not even between minor versions, so the prior backup is not optional. And the server only keeps compatibility with its own major version while the mobile apps handle the current one and the previous one: which is why you upgrade the phones before the server, not the other way round.
Immich against Nextcloud Memories and PhotoPrism
The three options solve the same problem by different routes:
| Immich | Memories (Nextcloud) | PhotoPrism | |
|---|---|---|---|
| GitHub stars | 112,964 | 3,830 | 40,124 |
| Own mobile app | yes | uses the Nextcloud one | no |
| Automatic camera-roll backup | built in | via Nextcloud | via PhotoSync or WebDAV |
| Content search | CLIP with VectorChord | limited | own tagging |
| Licence | AGPL-3.0 | AGPL-3.0 | mixed |
The deciding difference for the "I want off Google Photos" case is the first column of that table. The PhotoPrism documentation[5] admits it has no app of its own and recommends PhotoSync or any WebDAV client. It works, but it is one more piece to maintain and a worse experience for someone who just wants the phone to upload photos without thinking.
Memories[6] is the logical pick if Nextcloud is already running, because it inherits users, sharing and encryption without standing anything new up. If you do not have it yet, installing all of Nextcloud to manage photos means carrying far more than you asked for; how to install Nextcloud with Docker shows the real size of that stack.
PhotoPrism still has the better RAW handling and a very polished interface. If your priority is cataloguing a photographic archive rather than syncing a phone, the comparison gets much closer.
Frequently asked questions
Can I run Immich without the machine learning container?
Yes. Remove the immich-machine-learning service from the compose file and Immich keeps uploading, sorting and showing the map and the dates. You lose face recognition, tagging and freeform search. It is the configuration the documentation itself recommends for machines with 4 GB of RAM.
Do I need a domain and HTTPS to use the mobile app?
Not to try it at home: http://<ip>:2283 works on the local network. To upload your camera roll from outside you do want a reverse proxy with a certificate, because you will be sending your photos and your password across the internet.
Does Immich delete photos from my phone once they are uploaded?
Not on its own. There is an explicit free-up-space feature that deletes from the phone what is already on the server, and you have to turn it on. If you use iCloud, note that this deletion also removes the photos from iCloud on every device you own.
Conclusion
Installing Immich is downloading two files, changing three values and starting four containers. The part that really decides whether this goes well is not the install: it is understanding that the database holds the file paths and that a backup without it is worth nothing, and deciding honestly whether your machine has the RAM for the machine learning container or whether you would rather have a photo manager with no faces and no semantic search. The unstable-project warning has not applied since v2.0.0, but the 3-2-1 rule still does, and with twenty years of photos at stake that is the one non-negotiable part. The Spanish version of this article is at Cómo instalar Immich con Docker.
Sources
Source code
Access all the source code for this post on GitHub.
View on GitHub