← Back to Blog

Immich on a VPS: Leave Google Photos and iCloud (Tested Setup and Storage Sizing)

Published · by RS Computers

Immich Self-hosting Docker

Immich is a free, open-source photo and video server that does the job of Google Photos and iCloud Photos: the phone app backs up every new picture, and you get a timeline, albums, a map, face grouping and search by description. Running Immich on a VPS keeps all of that but puts the files on a server you rent, in a city you pick. As a self-hosted Google Photos alternative it needs Docker, at least 2 CPU cores, 6 GB of RAM (4 GB works with machine learning switched off) and disk for your library plus about 12% for thumbnails. In our lab, Immich 3.3.1 on 2 vCPUs imported and thumbnailed 1,000 twelve-megapixel photos in 11 minutes, then needed 15 more minutes to index them for search, faces and text.

Key facts, checked on 12 October 2026:

Google Photos, iCloud or your own server?

Google PhotosiCloud PhotosImmich on a VPS
Free storage15 GB, shared with Gmail and Drive5 GBNone: you pay for the server and its disk
Room to growGoogle One plansiCloud+ from 50 GB to 12 TBA bigger plan, or storage you mount
Faces and smart searchYes, run by GoogleYes, run by AppleYes, run by a container on your server
Your jobPayingPayingUpdates, HTTPS and backups

The last row is the real trade: family accounts on one server and original files in a folder you can copy anywhere, in return for looking after that server. Immich handles photos and videos only. For documents, see our Nextcloud guide; for films and music, Jellyfin.

How we tested

Everything below ran on our Amsterdam test server on 12 October 2026, in a Debian 13 container limited to 2 vCPUs and 4 GB of RAM, while other tests shared the host, so treat our times as conservative. Immich's docs call Docker inside LXC containers "not recommended" and prefer "a full virtual machine". Our lab container was the LXC kind and showed no side effects; every RS Computers VPS and VDS is a full KVM virtual machine anyway.

The test data: 1,000 photos made with ImageMagick at 4000 x 3000 pixels (12 megapixels, like most phone cameras), 3.56 MB each and 3.5 GB in total, dated 2019 to 2026, plus four HEVC clips, the format iPhones record by default. The pictures contain no faces, so face detection ran on every photo but found nobody.

Install Immich on Debian 13, step by step

Step 1: Docker and a user for Immich

# as root
apt update
apt install -y docker.io docker-compose curl wget openssl rsync
useradd -m -s /bin/bash -G docker immich
docker compose version
su - immich

Our install, which also pulled in a few test tools, took 40 seconds. It gave us Docker 26.1.5, and the version check printed Docker Compose version 2.26.1-4. Immich needs this docker compose command (with a space). The docker group is as powerful as root, so keep this account for Immich only; the last line switches to it. Since v3, machine learning on amd64 needs the x86-64-v2 CPU level, so check it:

# as the immich user
/lib64/ld-linux-x86-64.so.2 --help | grep x86-64-v2

Ours printed x86-64-v2 (supported, searched). Most CPUs made since about 2012 pass.

Step 2: the official compose file and a database password

# as the immich user
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
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$(openssl rand -hex 16)/" .env
sed -i 's|^# TZ=Etc/UTC|TZ=Europe/Amsterdam|' .env
sed -i "s/- '2283:2283'/- '127.0.0.1:2283:2283'/" docker-compose.yml
grep -v '^#' .env | grep .

The last command lists your settings: UPLOAD_LOCATION=./library, DB_DATA_LOCATION=./postgres, TZ=Europe/Amsterdam, IMMICH_VERSION=v3, a DB_PASSWORD of 32 random characters, and the database user and name. In plain words:

Step 3: start it

# as the immich user, in ~/immich-app
docker compose up -d
docker compose ps
curl -s http://127.0.0.1:2283/api/server/version

Pulling the four images took 62 seconds; they use 3.3 GB of disk. Fifteen seconds after up -d the server answered {"major":3,"minor":3,"patch":1,"prerelease":null}, and docker compose ps listed immich_server, immich_machine_learning, immich_redis (Valkey 9) and immich_postgres, Immich's own Postgres 14 image with the VectorChord search extension. Immich works with Postgres only.

Step 4: create the admin account through an SSH tunnel

Because Immich listens on 127.0.0.1 only, reach it through SSH from your own computer, with the account you normally log in with:

# on your own computer
ssh -N -L 2283:127.0.0.1:2283 YOUR_USER@YOUR_SERVER_IP

Leave that window open, browse to http://localhost:2283, click Getting Started and create the first account, which becomes the administrator. Through our tunnel the API answered {"res":"pong"} and the web page loaded. We created the admin with the API call the page itself uses, /api/auth/admin-sign-up.

What 1,000 photos cost in RAM, CPU time and disk

We uploaded the photos with Immich's official CLI (command below) with machine learning off, switched it on and let it catch up, then uploaded 300 new photos with everything running at once, as a phone backup would. "RAM in use" means memory taken by programs, without the file cache Linux frees on demand; docker stats showed up to 3.4 GB for the server container, mostly cached photo files.

Measurement (2 vCPUs, 4 GB)Result
RAM in use, idle after a fresh start1.2 GB with or without the ML container; it holds about 200 MB until it has work
Upload of 1,000 photos (3.5 GB) with the CLI2 min 28 s
Upload, thumbnails and metadata, ML off11 min 19 s (0.68 s per photo), RAM peak 2.2 GB
Smart search, face detection and OCR for the same 1,00014 min 52 s (0.89 s per photo), RAM peak 3.6 GB while the models downloaded and loaded, 2.0 GB typical
300 new photos, ML on, all jobs at once7 min 37 s (1.52 s per photo), RAM peak 2.8 GB
Thumbnail plus preview per photo8 KB + 412 KB, about 12% of a 3.56 MB original
Database for 1,304 photos and videos158 MB
ML models, downloaded on first use2.1 GB of disk

The CPU is the bottleneck: during the import, the server used 168% of the 200% two cores can give. At 1.5 seconds per photo with ML on, a first import of 10,000 photos keeps two vCPUs busy for about four hours; Immich works through it in the background. And 4 GB with ML on did work, but the peak left only 450 MB free, which is why Immich asks for 6 GB.

Running on 4 GB: switch machine learning off

Machine learning (ML) recognises what is in a picture: smart search ("dog on a beach"), face grouping, text recognition (OCR) and duplicate detection. Without it, Immich still backs up, shows the timeline and map, and searches by date, place, camera and file name. Turn it off in Administration > Settings > Machine Learning Settings, then comment out its block in the compose file, as Immich's FAQ suggests:

# as the immich user, in ~/immich-app
cp docker-compose.yml docker-compose.yml.orig
sed -i '/^  immich-machine-learning:/,/^  redis:/{/^  redis:/!s/^/#/}' docker-compose.yml
docker compose up -d --remove-orphans

The sed line puts a # before every line of the ML block, and docker compose ps then shows three containers. To bring ML back, run cp docker-compose.yml.orig docker-compose.yml and docker compose up -d, switch the setting on, and press Missing for Smart Search, Face Detection and OCR under Administration > Job Queues, as we did for the table above.

Videos fill the disk and keep the CPU busy

By default Immich converts every video that isn't already H.264 into a 720p H.264 copy that any browser can play, and that includes all HEVC video from iPhones. Our four clips (1.5 minutes of footage, 153 MB) were uploaded and processed in 59 seconds. Each 30-second 1080p clip (30.8 MB, 8 Mb/s) got a 25.1 MB copy, and each 15-second 4K clip (45.5 MB, 24 Mb/s) a 7.3 MB copy: 65 MB extra, or 42%. Our footage was full of synthetic noise, which compresses badly, so take that as a high case. The rules live in Administration > Settings > Video Transcoding Settings.

How much storage do 10,000, 50,000 or 200,000 photos and videos need?

From our measurements, plan on 4 MB per photo (original plus thumbnail and preview) and about 60 MB per video (a 30-second 1080p clip plus its 720p copy came to 56 MB). Add about 10 GB of fixed overhead: Debian, 3.3 GB of Docker images, 2.1 GB of ML models and a database of up to 3 GB. The table is our arithmetic from those figures, for photos only and for a mix where one item in ten is a video. Your phone's storage screen gives your real total.

LibraryPhotos onlyWith 10% videosPlan with room to spare
10,000 items50 GB106 GBPhotos only: VPS Mini (80 GB) with ML off. With videos: VDS Small (240 GB)
50,000 items210 GB490 GBPhotos only: VDS Medium (480 GB). With videos: VDS Large (960 GB)
200,000 items810 GB1,930 GBPhotos only: VDS Large (960 GB). With videos: VDS Large plus mounted storage for the originals

When a VDS or remote storage makes sense

VDS Small is our first plan that meets Immich's recommended 4 cores and 8 GB of RAM, and VDS plans have dedicated vCPUs for the indexing and video work. VPS plans share their vCPUs, which suits a family adding a few dozen photos a day once the first import is done. Every plan has NVMe disks and unmetered traffic, so a first upload of hundreds of gigabytes is no problem, and you can move to a bigger plan later from the client area with a short reboot.

Beyond 960 GB, keep the database and the thumbs folder on the server's NVMe and put only the originals on storage you mount, such as a NAS reached over WireGuard. Immich's custom locations guide gives each folder its own path, and the external library below suits an archive of older years. We didn't test a network mount.

Phone backup, HTTPS and remote access

The Immich app is in the App Store, Google Play and F-Droid. Enter the server address, log in, tap the cloud icon, choose albums and press Enable Backup (mobile app docs). Immich's FAQ notes that background backup "is controlled by your device's operating system". The v3.0 release notes say Android background backup became "significantly more reliable" and that uploads on iOS now start sooner. The phone still has to reach a server that listens on 127.0.0.1. Two clean ways:

Backup and restore: the drill we ran

Immich keeps file paths and metadata in its database and doesn't rescan the folder, so a backup needs both the database and the upload folder (backup docs). Immich already dumps the database every night at 2:00 and keeps 14 dumps in library/backups; our manual run produced a 22 MB file. The manual commands from the docs:

# as the immich user, in ~/immich-app
mkdir -p ~/backup
docker exec -t immich_postgres pg_dump --clean --if-exists --dbname=immich --username=postgres | gzip > ~/backup/immich-db.sql.gz
rsync -a --delete library/ ~/backup/library/

For 1,304 items the dump took 6 seconds and the file copy 27 seconds (5.3 GB). One quirk: because of -t, every line of the dump ends with an extra carriage return. The restore below handled it, and no stray characters showed up in the data. A copy on the same disk only protects against mistakes, so send ~/backup to another machine, for example with restic as in our off-site backups guide.

Then we played disaster and rebuilt Immich in an empty folder from the backup alone, with the official sequence:

# as the immich user
cd ~/immich-app && docker compose down
mkdir ~/immich-restore && cd ~/immich-restore
cp ~/immich-app/docker-compose.yml ~/immich-app/.env .
rsync -a ~/backup/library/ ./library/
docker compose pull
docker compose create
docker start immich_postgres
sleep 10
gunzip --stdout ~/backup/immich-db.sql.gz \
| sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
| docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
docker compose up -d

With the images already downloaded, it took 78 seconds from an empty folder to a working server. All 1,300 photos and 4 videos came back, previews loaded, a downloaded original had the same SHA-256 checksum as the file we uploaded, and smart search still worked. The import only works into a database the Immich server has never started, which is why the sequence uses create before up.

Before every update

Read the release notes, run the backup, then docker compose pull, docker compose up -d and docker image prune -f in the Immich folder. With nothing newer than 3.3.1 out, our containers kept running unchanged.

Bringing your old photos in

From Google Photos or an iPhone

Export with Google Takeout and pick the 50 GB archive size, which Google suggests so the export is split into fewer files. Takeout puts dates and descriptions in separate JSON files; the community tool immich-go (v0.32.0, June 2026) reads them while uploading a Takeout archive. We didn't test it here. On an iPhone, the Immich app uploads what is in the phone's photo library.

Photos already on the server

Copy them in with the official CLI. Create a key under Account Settings > API Keys, then:

# as the immich user
docker run --rm --network host -v ~/sample-photos:/import:ro \
  -e IMMICH_INSTANCE_URL=http://127.0.0.1:2283/api -e IMMICH_API_KEY=YOUR_API_KEY \
  ghcr.io/immich-app/immich-cli:latest upload --recursive /import

Replace ~/sample-photos with your folder. Ours ended with Successfully uploaded 1000 new assets (3.7 GB); the CLI counts decimal gigabytes. Files it has seen before count as duplicates and are skipped.

Folders that should stay where they are

Use an external library. Immich indexes the files in place and keeps edits in its database, not in the files. Mount the folder read-only into the server container:

# as the immich user, in the Immich folder
sed -i 's|^      - /etc/localtime:/etc/localtime:ro|&\n      - /home/immich/old-photos:/mnt/old-photos:ro|' docker-compose.yml
docker compose up -d
docker exec immich_server ls /mnt/old-photos/2018 | wc -l

Put your own folder in place of /home/immich/old-photos. The last line printed 100, our test photos in a 2018 folder. Then, in Administration > External Libraries, create a library, add /mnt/old-photos and press Scan. Our 100 photos (357 MB) were indexed in 144 seconds with ML on, and Immich's own folder grew by just 42 MB of thumbnails.

Frequently asked questions

Is Immich a good replacement for Google Photos?

For backup, timeline, albums, sharing, maps, face grouping and search by description, yes. Immich 3.3.1 does all of these, with apps for iPhone and Android. What you take on is running the server: updates, HTTPS and backups.

How much RAM does Immich need?

Immich asks for at least 6 GB and recommends 8 GB, or 4 GB with machine learning off. In our test with 2 vCPUs and 4 GB, it used 1.2 GB idle and peaked at 2.2 GB during an import with ML off and at 3.6 GB when the ML models loaded.

How much disk space does Immich need?

Your library plus about 12% for thumbnails and previews, plus video copies, plus about 10 GB for Docker images, ML models and the database. We measured 0.42 MB of thumbnail and preview per 12-megapixel photo, so 10,000 phone photos need about 50 GB of disk in total.

Can Immich run without machine learning?

Yes. Turn it off in Machine Learning Settings and comment out the immich-machine-learning service in docker-compose.yml. You keep backup, timeline, map and search by date, place and file name, but lose smart search, faces, OCR and duplicate detection.

Do I need a domain name for Immich?

No. You can reach it through an SSH tunnel or a private Tailscale network. For the phone app on the open internet, a domain with HTTPS through a reverse proxy is the usual way, and Immich must then use a whole domain or subdomain.

How do I back up Immich?

Back up the database and the upload folder together. Immich dumps the database every night into library/backups; copy the whole upload location to another machine. In our drill, a full restore of 1,304 items took 78 seconds.

Start with one phone

Install Immich with the official compose file, bind it to 127.0.0.1, back up one phone and check the timeline. Run the restore drill once before you delete anything from Google or iCloud. Choose the server size from the storage table on our plans page; our servers are in Amsterdam, Dublin and Prishtina, and availability by city is shown there. If you'd rather not set it up yourself, message us on Telegram and we'll quote the setup.

← All articles

Chat on Telegram