Immich self-hosting guide: RAM and upgrade tips
See why 6 GB RAM na minimum for Immich. We talk about port 2283, memory kill 137, and how to fix pgvecto.rs database wahala for Immich v3 upgrades.
Wetin you dey build
Immich na self-hosted photo and video backup service — e be real replacement for Google Photos. E get phone app wey dey upload your camera roll for background, timeline, albums, face recognition, and machine-learning search wey fit find "beach" or person without you need to tag anything. You go run am for VPS wey you own, original files go stay for your disk, and nobody go scan dem to sell you things.
The install na four containers from the project's own Docker Compose file. That part go take ten minutes. The rest of this guide na where the wahala dey: the machine-learning container dey chop memory plenty for small box, original files dey finish disk fast, the mobile app no go gree for plain-HTTP server, and Immich dey release breaking changes often enough wey careless docker compose pull fit make your database no gree start. If you treat those four things with respect, Immich go dey rock-solid. If you ignore dem, you go lose weekend.
Prerequisites, and the honest gotchas
- RAM: official docs talk say 6 GB minimum and 8 GB recommended — treat 4 GB plus swap as the absolute floor. The
immich-serverand Postgres containers no dey chop much RAM. Theimmich-machine-learningcontainer na him dey chop RAM plenty — e dey load CLIP and face-recognition models inside RAM to build search indexes, and if you dey use 2 GB box, the kernel go kill am. Add swap even if you get 4 GB. - Disk: calculate size for your whole library, plus extra. E dey copy your original files fully, plus Immich go generate thumbnails and preview images (roughly 10–20% extra). If you get 200 GB photo collection, you need 300 GB volume. Postgres small when you compare am.
- CPU: any modern KVM VPS go work, but ML on CPU dey slow. Smart-search indexing for big import fit run for hours for background. Na normal matter; e no need GPU.
- A domain name wey point to the VPS. The mobile app prefer HTTPS endpoint well well, and you go need reverse proxy for front. This setup na same style as self-hosted Nextcloud instance with Docker, TLS and backups — Immich na the photo version of that files server.
- Docker and the Compose plugin must install — Docker Engine plus the Compose v2 plugin from Docker's own apt repository, exactly as we explain for our Docker Compose basics guide.
Step 1: Add swap before anything else
The main reason Immich dey fail for small VPS na because ML container dey get OOM-killed. Give the kernel space to breathe first.
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h for now go show Swap: line of 4.0Gi. This no go make ML fast, but e go stop container from dying mid-index for 4 GB machine.
Step 2: Grab the official compose and env — use theirs, not a copy
Immich fix its service versions and, most important part, its database image inside the files wey dem ship. No copy paste compose file from blog (including this one) as your source of truth. Download the release assets:
sudo mkdir -p /opt/immich && cd /opt/immich
sudo wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
sudo wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.envDem come from the tagged release, so the image references go match. The compose file define four services, and e go help if you know wetin each one dey do before you touch anything:
immich-server(ghcr.io/immich-app/immich-server, containerimmich_server) — the API and web UI, wey dey listen for port2283. E dey mount your uploads for/data.immich-machine-learning(ghcr.io/immich-app/immich-machine-learning, containerimmich_machine_learning) — CLIP search and face recognition. E dey cache downloaded models formodel-cachevolume. This one dey chop plenty RAM.database(containerimmich_postgres) — Postgres with the VectorChord vector extension, wey dey run similarity search. The image tag dey pinned by digest inside the compose file, for exampleghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:.... Old setups dey usepgvecto.rs; dem remove support for am for Immich v3.0, so anything wey you install today na VectorChord. No ever try edit this tag manually.redis(containerimmich_redis) — a Valkey/Redis instance for job queues.
Step 3: Configure .env — where your photos and database live
Open .env and set four things. Everything below the marked line must stay as-is.
# Where original uploads are stored on the host
UPLOAD_LOCATION=/opt/immich/library
# Where the Postgres data lives. NEVER put this on an NFS/network share.
DB_DATA_LOCATION=/opt/immich/postgres
# "v3" is a floating tag that tracks the latest v3.x. Pin a full tag like
# v3.0.2 instead — then you upgrade on purpose, not by surprise.
IMMICH_VERSION=v3.0.2
# Change this to a long random string. Letters and digits only.
DB_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_STRING
# Set your timezone so timestamps and "on this day" line up
TZ=Europe/London
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immichTwo rules wey go save you wahala. UPLOAD_LOCATION must point to your big disk — if you plan to attach a data volume later, set this to its mount path from the beginning, because if you move am later, you go need to move all thumbnails and update all asset paths. Also, DB_DATA_LOCATION must dey on local disk: Postgres go corrupt if you put am on an NFS or SMB share, and the docs say so clearly. If you use only letters and digits for DB_PASSWORD, you go avoid connection-string escaping bugs.
Step 4: First run and creating the admin user
cd /opt/immich
sudo docker compose up -d
sudo docker compose psIf everything correct, you go see four containers, all running and eventually healthy:
NAME STATUS
immich_machine_learning Up (healthy)
immich_postgres Up (healthy)
immich_redis Up (healthy)
immich_server Up (healthy)The first up go pull many gigabytes of images, so make you give am time. You fit use sudo docker compose logs -f immich-server watch how e dey go; the server go log say e dey listen on port 2283 once e ready. Now open http://YOUR_SERVER_IP:2283 for browser. The first time you enter, you go see Getting Started wizard — the first account you create na the admin. Pick strong password; this account dey control server settings, user management and the ML configuration wey you go need later.
Step 5: Mobile app and background backup
Install "Immich" from App Store or Play Store. When you reach the login screen, e go ask you for Server Endpoint URL. Enter the full URL with the scheme, for example https://photos.example.com (the app go add /api itself). Log in with the account wey you just create, then open the app Backup screen, pick the albums wey you want protect (usually Camera and Screenshots), and turn on Background backup. iOS background backup dey slow down because of how the OS dey work — foreground uploads dey always run, but background ones go happen only when the OS allow am.
Na for here many people dey get wahala, so read Step 6 before you start to fight the app.
Step 6: HTTPS via a reverse proxy — and the full-URL rule
Mobile app really need HTTPS. Put one reverse proxy for front of port 2283 and terminate TLS for there. If you don't already run many containers, Traefik with automatic TLS for multiple Docker apps na the cleanest option — one label block go route photos.example.com to the immich-server container and fetch certificate for you. If you prefer nginx, the Let's Encrypt with Certbot and nginx guide go help you get certificate and proxy_pass http://127.0.0.1:2283; block. One proxy setting important for Immich: increase the upload size limit, because phone videos dey big. For nginx, na client_max_body_size 50000M; inside the server block — the default 1 MB go reject video uploads with 413 Request Entity Too Large.
The rule wey app dey enforce: the endpoint must dey reachable and, for real life, e must be HTTPS. http:// endpoints, or direct IP without port, na where "the app cannot reach the server" error dey come from — we go explain this one for the failure list below.
Step 7: External libraries vs uploads — importing an existing photo tree
Two ways dey exist for how photos dey enter Immich, and dem no be the same thing.
- Uploads na assets wey Immich own. App or web uploader dey copy file inside
UPLOAD_LOCATION. Immich fit rename, move, and delete dem. - External libraries na read-only imports for files wey don dey one folder for your server — like old
Picturestree or NAS export. Immich dey index dem for where dem dey and dey show dem for timeline, but e no go ever change or delete original files.
To import existing tree, mount am as read-only inside server container. Edit docker-compose.yml under immich-server: and add volume:
immich-server:
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
- /srv/photos:/mnt/media/photos:ro:ro dey guarantee say Immich no fit touch original files. Recreate container with sudo docker compose up -d, then for web UI go your avatar → Administration → External Libraries → Create Library, pick the user wey go own am, click Add under Folders, and type container path — /mnt/media/photos, no be host path /srv/photos. Click Scan. To use host path instead of container path na number-one mistake for external-library; scan no go find anything and e go show zero assets.
Step 8: The upgrade discipline Immich demands
This is the part that separates a happy Immich from a broken one. Immich ships fast and does not backport fixes or support downgrades. Blindly tracking the floating v3 tag will eventually break your database. The discipline:
- Pin a version. Keep
IMMICH_VERSIONset to a concrete tag likev3.0.2, not the floatingv3that always pulls the newest v3.x. - Read the release notes every single time before upgrading. Breaking changes — especially database or vector-extension changes — are called out there. The v3.0 release is the obvious example: it removed pgvecto.rs outright, so anyone still on the old extension had to finish the VectorChord migration (introduced back in v1.133) before they could move up.
- Back up the database first (Step 9). Always, but doubly so when the notes mention the database.
- Take the new compose file too.
IMMICH_VERSIONonly pins the server and ML images. The Postgres image is pinned by digest insidedocker-compose.yml, so a version that needs a newer database extension ships a new compose file. Re-download both release assets, re-apply your.envvalues, then upgrade. - Update your mobile clients around the same time. The server only speaks its matching major version, and the app supports the current and previous major. A server that has jumped ahead of the app shows
Your app major version is not compatible with the server!on the phone until you update it, so it is safest to update the app first.
The actual commands, once you have the new files in place:
cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image pruneStep 9: Backups — database dump PLUS the originals, and test am
Immich backup na two things, and if you no get both, the one wey you get no go work. The database dey hold album structure, faces, search indexes, and the map wey connect asset to file. The originals directory dey hold the actual photos. If you restore one without the other, you go get photos wey no get organization, or you go get empty shell wey dey point to files wey no dey.
Dump the database with pg_dump from inside the Postgres container — specifically the immich database, no be the whole cluster:
sudo docker exec -t immich_postgres pg_dump --clean --if-exists \
--dbname=immich --username=postgres | gzip > /opt/immich/immich-db-$(date +%F).sql.gzThen back up UPLOAD_LOCATION — the whole /opt/immich/library tree, especially the library/, upload/ and profile/ subfolders — use restic, rsync or borg send am to another machine or object storage. Do the database first and the files second, so the dump no go reference photo wey the file backup never copy yet. For external libraries, back them up separately for their real source; Immich no own them.
Now the part wey everybody dey skip: test the restore. You must run restore for one fresh stack wey server never start before, on a Postgres image wey vector extension compatible with the dump — na why you must no improvise the DB image tag. For one scratch box wey get the same compose and .env, wipe any old state, bring up only the database, then load the dump:
cd /opt/immich
sudo docker compose down -v
sudo docker compose pull
sudo docker compose create
sudo docker start immich_postgres
sleep 10
gunzip --stdout immich-db-2026-07-15.sql.gz |
sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" |
sudo docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
sudo docker compose up -dThe sed rewrite of search_path no be optional for VectorChord database — if you leave am out, the restore go fail halfway. When the stack come up again with your originals inside, open the web UI: if your photos and albums dey there, your backup work. If you never run this, you no get backup — you just dey hope.
Failure modes, with the strings you will see
The ML container dey OOM-killed. sudo docker compose logs immich-machine-learning go stop suddenly, docker compose ps go show say Restarting, and the exit code na 137. sudo dmesg | grep -i oom go confirm am: Out of memory: Killed process ... (python3). Search and face jobs go just stall. The problem na say RAM too small for the models. Fixes, for order: add swap (Step 1); give the VPS more RAM; or, if you no fit do that one, disable ML for Administration → Settings → Machine Learning Settings by turn off Smart Search and Facial Recognition — you go still get backups and albums, but you go lose search-by-content. If you remove the immich-machine-learning service from the compose file, na same result you go get.
Postgres refuse to start after upgrade. The server log go dey loop with line like The database currently has VectorChord 0.5.3 activated, but the Postgres instance only has 0.4.2 available. This most likely means the extension was downgraded. — or, for older stacks, The pgvecto.rs extension is not available in this Postgres instance.. The problem na say database image extension version old pass the one wey your data don upgrade to. This one dey happen mostly when person edit image tag with hand or when person restore newer dump for old image. The fix na to use the Postgres image wey match — take the compose file from the release wey match your database, no downgrade, and restore only for compatible image.
The mobile app no fit reach the server. The login screen go show connection error / Server is not reachable after you type the URL. Three things fit cause am: you type http:// when proxy only dey serve https://; you connect straight to backend but you no put port, so e try example.com (port 443) instead of example.com:2283; or the reverse proxy no dey forward /api. Fix am by type full https://photos.example.com URL and make sure say e load for phone browser first. If browser work but app no work, the proxy dey strip the path or the certificate na self-signed — the app no dey accept untrusted certs.
Disk full mid-import. Uploads go start to fail, thumbnails go blank, and logs go show ENOSPC: no space left on device or, from Postgres, could not extend file ... No space left on device. df -h go show say the UPLOAD_LOCATION volume don reach 100%. Na why you must check disk size before you start to import big library. To fix am, attach larger volume, stop the stack, move UPLOAD_LOCATION go there, update .env, and start again — or expand the existing disk if your provider allow am. Postgres fit jam if disk full, so clear space and restart the database container before you think say data don corrupt.
FAQ
How much RAM and disk Immich need?
Immich official requirements na 6 GB RAM minimum and 8 GB recommended — for small library, 4 GB with swap na the real floor, and make sure say you configure swap because machine-learning container dey spike RAM. For disk, calculate your full library size plus roughly 10–20% for thumbnails and previews for local storage — no ever put Postgres data directory for network share. If you still dey decide wetin else to run, see guide to what to self-host in 2026 to see how Immich footprint be compared to other services.
I fit run Immich without GPU?
Yes. Machine-learning container dey run well for CPU — GPU only dey speed up smart-search indexing and video transcoding if you use the right image variant. For CPU, the initial index for big library fit take hours for background, but e no go block your backups or browsing. If your box too small for ML, you fit disable Smart Search and Facial Recognition for admin settings and keep everything else.
How I fit upgrade Immich safely?
Pin IMMICH_VERSION to a concrete tag like v3.0.2, read the release notes before every upgrade, and back up the database first. Because Postgres image dey pinned inside docker-compose.yml instead of IMMICH_VERSION, re-download both the compose file and example.env from your target release and re-apply your values, then run docker compose pull && docker compose up -d. No ever let the version float unattended — Immich dey carry breaking changes and e no support downgrades.
Wetin exactly I need to back up?
Two things, together: a pg_dump of the immich database and the whole UPLOAD_LOCATION originals directory. The database dey hold albums, faces and the asset-to-file mapping; the directory dey hold the actual photos, and to restore, you need both plus a database image with compatible vector extension. Do the database dump first and the file copy second, and test the restore for one scratch box at least once — backup wey you no test, no be backup.
How I fit import my existing photo folder?
Mount the folder as read-only into the immich-server container as extra volume (for example - /srv/photos:/mnt/media/photos:ro), recreate the container, then go Administration → External Libraries, create a library and add the container path /mnt/media/photos. Immich dey index the files for where dem dey and e no dey modify or delete dem. The mistake wey many people dey do na to put host path instead of container path, which go make the scan find nothing.