Phone uploading photos to a self-hosted Immich server running on a VPS
Self-hosting

Immich Docker Compose: Your Own Google Photos on a VPS

Your phone probably holds the most valuable data you own: years of family photos, receipts you snapped in a hurry, videos of the kids you will never be able to record again. Most of us hand all of that to a big cloud provider, pay a monthly fee once the free storage runs out, and hope the terms of service do not change. Immich offers a different deal: an app that looks and feels like Google Photos, backs up your phone automatically, recognizes faces and lets you search for “dog on the beach”, except it runs on a server that you control. In this guide we install it step by step with Docker Compose on a fresh VPS, put it behind HTTPS, connect the phone app, and plan disk space and backups so your memories are actually safe.

Immich is open source (AGPL-3.0), developed in the open at github.com/immich-app/immich , with documentation on immich.app . After a long beta period, the project reached its stable 2.x line in late 2025, which made it a much more comfortable choice for a family or a small business photo archive.

What Immich is, and why self-host your photos

Immich is a photo and video management platform with three parts: a web interface you open in the browser, mobile apps for Android and iOS that upload your camera roll in the background, and a server that stores the files, generates thumbnails, converts videos to formats every device can play, and runs machine learning models for search and face recognition.

Why would you run this yourself instead of paying for cloud storage? A few honest reasons:

  • Privacy. Your photos stay on a server you rent, in a data center you know, in Romania. Nobody trains models on them or scans them for advertising.
  • Predictable cost. A VPS with a few hundred GB of storage costs the same whether you have 10,000 or 60,000 photos, and the whole family can share it with separate accounts.
  • No lock-in. Immich keeps your original files untouched on disk, in normal folders. If you ever stop using it, your photos are still just files.

The tradeoff is responsibility: you are now the person in charge of updates and backups. That is exactly why half of this article is about planning and protecting the data, not just starting containers.

A few terms before we start

A container is an isolated package that contains an application and everything it needs to run, so it behaves the same on any Linux server. Docker is the engine that runs containers, and Docker Compose is a tool that starts several related containers from one YAML file. Immich is not a single program but four cooperating services, so Compose is the natural way to run it.

A port is a numbered “door” on the server where a program listens for connections. Immich listens on port 2283. A reverse proxy is a web server placed in front of the application: it receives HTTPS traffic on the standard ports 80 and 443, handles the TLS certificate, and forwards requests to Immich internally. A volume is storage that lives outside the container, so your data survives when containers are recreated or updated.

What you need

  • A KVM VPS with Debian 13 or Ubuntu 24.04, at least 2 vCPU and 4 GB RAM (6 to 8 GB if you want machine learning to run comfortably), plus enough disk for your library (see the planning section below).
  • A user with sudo rights and SSH access.
  • A domain or subdomain (for example photos.example.com) with an A record pointing to the VPS IP address.
  • The Immich mobile app from Google Play, the App Store or F-Droid.
  • Ideally, a second location for backups: another VPS, a storage box or a NAS at home.

A note about containers inside containers: Docker can run in an unprivileged LXC VPS only if nesting is enabled for the container, and even then some things behave differently (firewall rules, swap, memory accounting). For a photo library you plan to keep for years, we recommend a KVM VPS, where everything in this guide works exactly as written.

How Immich fits together on a VPS

Before typing commands, it helps to see the moving parts. Your phone and browser talk HTTPS to the server. The firewall only lets through SSH and web traffic. Caddy, our reverse proxy, terminates TLS and forwards requests to the Immich server container, which listens only on 127.0.0.1, meaning it cannot be reached from the internet directly. Behind it sit three helper containers, and all the important data ends up in two folders on the VPS disk.

How Immich runs on your VPSPhone app + browserHTTPS requestsFirewall (ufw)22, 80, 443 openCaddy proxyTLS certificatePort 2283localhost onlyDocker Compose stack in /opt/immichimmich-server127.0.0.1:2283machine-learningsmart search, facesPostgreSQLmetadata, vectorsValkeyjob queueOn the VPS diskmodel-cacheDocker volumeDB_DATA_LOCATIONlocal SSD onlyUPLOAD_LOCATIONphotos and videosOff-site backuprestic, encrypted
Only Caddy faces the internet. Immich listens on localhost, and the library folder is what we back up off-site.

The four containers are:

  • immich-server: the API, the web interface and the background workers that create thumbnails and transcode videos.
  • immich-machine-learning: a Python service that runs the models for smart search, face detection and face recognition.
  • database: a PostgreSQL image built by the Immich team with vector search extensions, used to store metadata and the “fingerprints” that make search work.
  • redis: in current releases this is actually Valkey, an open source Redis-compatible store used as a job queue.

Planning RAM and disk before you start

Immich is not a tiny app. The official recommendation is 6 GB of RAM (8 GB is better) and 4 CPU cores, with 4 GB and 2 cores as a realistic minimum. Most of the memory is used by machine learning while it indexes your library, so the first days after a large import are the most demanding. After that, the server mostly sits idle until new photos arrive.

Approximate RAM use per Immich componentimmich-server0.8 GBmachine-learning1.5 GBPostgreSQL0.5 GBValkey0.05 GBTotal with ML2.85 GBTotal without ML1.35 GB00.51.01.52.02.53.0RAM in GB (approximate, while indexing)Component
Approximate values for a small family library during indexing. Your numbers will vary with library size and models.

For disk space, the originals are only part of the story. Immich also stores thumbnails, transcoded copies of videos, and the database. These rules of thumb are approximate but good enough for planning:

ItemApproximate size
One phone photo (HEIC or JPEG)2 to 6 MB
One minute of 1080p phone video100 to 130 MB
One minute of 4K phone video170 to 400 MB
Thumbnails and previews5 to 10% of the photo library
Transcoded videos10 to 30% of the video library
Databasea few GB for a large library

A worked example: a family of four with 40,000 photos and about 1,000 short videos needs roughly 160 GB for photos, 100 GB for videos and another 30 GB for thumbnails, transcodes and the database, so about 290 GB today. Add 30 to 50% headroom for the next couple of years and you are looking at 400 GB or more. One important rule from the Immich documentation: the database must live on a local SSD, never on a network share. The photo library itself can live on a bigger, slower disk if you have one attached.

Installing Immich with Docker Compose, step by step

Here is the full procedure. Each step below matches one box in the diagram, so you always know where you are.

Immich installation in 8 steps1Prepare the VPSupdates, firewall2Install Dockerofficial repository3Get Compose filescompose + .env4Configure .envpaths, password5Start the stackdocker compose up6Add HTTPSCaddy proxy7Admin + phoneautomatic backup8Back up the datarestic off-siteSteps 1 to 5: install and start. Steps 6 to 8: publish, connect, protect.
Steps 1 to 8 match the numbered headings below. Do not skip step 8: an unbacked photo library is a single point of failure.

Step 1: Prepare the VPS

Start with a fully updated system. Log in over SSH with your sudo user and run:

sudo apt update && sudo apt full-upgrade -y

This refreshes the package lists and installs all pending updates. If a new kernel was installed, reboot. On Ubuntu, the system tells you by creating a flag file, which you can check like this:

[ -f /var/run/reboot-required ] && echo "Reboot needed" || echo "No reboot flag"

On Debian this file is normally not created, so if you saw a linux-image package in the upgrade output, simply run sudo reboot and log in again.

Next, the firewall. Ubuntu ships with ufw installed, while Debian 13 does not, so install it first if needed. Always allow SSH before enabling the firewall, otherwise you lock yourself out:

sudo apt install -y ufw
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

The output should list ports 22, 80 and 443 as ALLOW. If your SSH daemon listens on a different port, use that port instead of 22. Notice that we do not open port 2283. As we will see, Docker can bypass ufw for published ports, so we will protect Immich in a better way in step 4. In an unprivileged LXC container ufw may fail to load its rules; in that case rely on the firewall in your provider panel.

Step 2: Install Docker

The Docker packages in the Debian and Ubuntu archives are often older, so we use Docker’s official repository. The commands below work on both distributions, because they read the distribution name (debian or ubuntu) and the codename (trixie or noble) from /etc/os-release:

sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
sudo curl -fsSL "https://download.docker.com/linux/${ID}/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

The first lines download Docker’s signing key, so apt can verify the packages are genuine. Then we add the repository and install the engine plus the Compose v2 plugin. If you previously installed docker.io from the distribution, remove it first with sudo apt remove docker.io docker-compose to avoid conflicts. Check that everything works:

sudo docker run --rm hello-world
sudo docker compose version

You should see a “Hello from Docker!” message and a Compose version starting with v2 or newer. Throughout this guide we use sudo docker explicitly. You could add your user to the docker group instead, but keep in mind that membership in that group is effectively root access.

Step 3: Get the Immich Compose files

The Immich team publishes a ready-made docker-compose.yml and an example environment file with every release. Using their files, instead of writing our own, means our setup matches what the developers test. We keep them in /opt/immich:

sudo mkdir -p /opt/immich
cd /opt/immich
sudo curl -fsSL -o docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
sudo curl -fsSL -o .env https://github.com/immich-app/immich/releases/latest/download/example.env
ls -la

ls -la should show two files, docker-compose.yml and .env (the dot makes it hidden, which is why we use -a). If you open the Compose file, you will see roughly this structure. This is a shortened excerpt for understanding, not something to copy:

name: immich

services:
  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'

  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache

  redis:
    # Valkey image, pinned by digest

  database:
    # Immich PostgreSQL image with vector extensions, pinned by digest
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data

volumes:
  model-cache:

The values in ${...} come from the .env file, which is what we edit next. The database and Valkey images are pinned to exact versions by the Immich team, while the two Immich images follow the IMMICH_VERSION variable.

Step 4: Configure .env, storage paths and the port

First, create the folders for photos and for the database, and generate a strong database password. The password must contain only letters and numbers, because some special characters break the database connection string:

sudo mkdir -p /srv/immich/library /srv/immich/postgres
openssl rand -hex 24

Copy the random string printed by openssl. Now edit the environment file:

sudo nano /opt/immich/.env

Make it look like this (keep any other lines that are already there):

UPLOAD_LOCATION=/srv/immich/library
DB_DATA_LOCATION=/srv/immich/postgres
TZ=Europe/Bucharest
IMMICH_VERSION=v2
DB_PASSWORD=paste-the-generated-value-here
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

What each line means:

  • UPLOAD_LOCATION is where every photo, video, thumbnail and automatic database dump will be stored. If you attach a separate large disk, point this to it.
  • DB_DATA_LOCATION holds the PostgreSQL files. Keep it on the local SSD.
  • TZ sets the time zone, so dates and scheduled jobs make sense.
  • IMMICH_VERSION=v2 pins the image tag to the 2.x line. The default release tag always follows the newest release, which could one day be a new major version with breaking changes. Pinning to v2 gives you bug fixes and features without surprise major upgrades. For maximum control, you can pin an exact release such as v2.x.y from the releases page and bump it deliberately.
  • DB_PASSWORD is the password you just generated. It is set only when the database is created for the first time, so choose it now.

Save with Ctrl+O, Enter, then exit with Ctrl+X, and restrict the file permissions so other users cannot read the password:

sudo chmod 600 /opt/immich/.env

Now the port. By default the Compose file publishes port 2283 on all network interfaces. Docker writes its own firewall rules, which take effect before ufw, so a published port is reachable from the internet even if ufw says otherwise. The clean fix is to publish it only on localhost, where only Caddy can reach it:

cd /opt/immich
sudo sed -i "s/- '2283:2283'/- '127.0.0.1:2283:2283'/" docker-compose.yml
grep -n "2283" docker-compose.yml

The grep output must show - '127.0.0.1:2283:2283'. If it still shows the old line (for example because quoting changed in a newer release), open the file with sudo nano docker-compose.yml and edit that line by hand.

Step 5: Start the stack

Time to start everything:

cd /opt/immich
sudo docker compose up -d

The first run downloads a few GB of images, so give it a few minutes. The -d flag runs the containers in the background. Then check their state:

sudo docker compose ps

Trimmed to the relevant columns, a healthy result looks like this:

NAME                      STATUS
immich_machine_learning   Up 2 minutes (healthy)
immich_postgres           Up 2 minutes (healthy)
immich_redis              Up 2 minutes (healthy)
immich_server             Up 2 minutes (healthy)

Right after start, the status can show (health: starting) for a minute. Confirm that the server answers locally:

curl -s http://127.0.0.1:2283/api/server/ping

The expected answer is {"res":"pong"}. If something is wrong, read the logs with sudo docker compose logs -f immich-server (press Ctrl+C to stop following them). Because of restart: always in the Compose file, the containers will start again automatically after a reboot.

Step 6: Add HTTPS with Caddy

Make sure your DNS record (for example photos.example.com) already points to the VPS, because Caddy needs it to obtain a free Let’s Encrypt certificate. Caddy is packaged in both Debian 13 and Ubuntu 24.04 (in Ubuntu it comes from the universe component, enabled by default on cloud images):

sudo apt install -y caddy
sudo nano /etc/caddy/Caddyfile

Replace the content of the file with these three lines, using your own domain:

photos.example.com {
    reverse_proxy 127.0.0.1:2283
}

That is the whole configuration: Caddy listens on 80 and 443, obtains and renews the certificate on its own, redirects HTTP to HTTPS, and forwards everything to Immich. It has no upload size limit by default, which matters because phone videos can be several GB. Validate and reload:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo journalctl -u caddy --no-pager -n 30

In the logs you should see a line saying the certificate was obtained successfully. If you prefer Nginx, it works too, but you must set client_max_body_size 50000M;, enable WebSocket upgrade headers and raise the proxy timeouts, otherwise large uploads fail.

Step 7: Create the admin account and connect your phone

Do this immediately after step 6. The first account registered on a new Immich instance becomes the administrator, so you do not want to leave a fresh, public instance unclaimed. If you want zero risk, you can claim it before step 6 through an SSH tunnel: run ssh -L 2283:127.0.0.1:2283 youruser@your-vps-ip from your computer and open http://localhost:2283 in the browser.

Open https://photos.example.com, click “Getting Started” and create the admin user with a long, unique password. The onboarding wizard asks a few questions (theme, storage template, privacy settings); you can change all of them later.

For the rest of the family, go to Administration > Users and create one account per person. Here you can also set a storage quota per user, which is a good way to stop one 4K video enthusiast from filling the disk. Each person sees only their own photos unless they share albums or set up partner sharing.

On the phone:

  1. Install the Immich app and enter the server URL https://photos.example.com.
  2. Log in with your account.
  3. Open the backup screen (the cloud icon at the top), select the albums to back up, usually “Camera” or “Recents”.
  4. Enable automatic backup, and decide whether it should run only on Wi-Fi and only while charging.

For the first upload, which can mean tens of GB, leave the phone on Wi-Fi and plugged in overnight with the app open. On Android, exclude Immich from battery optimization, otherwise the system may stop background uploads. On iOS, background work is limited by the operating system, so enable Background App Refresh for Immich and open the app from time to time to let it catch up.

Step 8: Back up the photo library and the database

Immich on a single VPS is a copy of your photos, not yet a backup. Follow the classic 3-2-1 idea: three copies of the data, on two different media, one of them in another location. Your phone (until you delete photos from it), the VPS and an off-site repository give you exactly that.

Immich already helps with the database part. It creates automatic database dumps every night (by default at 02:00, keeping the last 14) in UPLOAD_LOCATION/backups. Check or adjust this under Administration > Settings > Backup Settings. This means one folder, /srv/immich/library, contains both your original files and a consistent copy of the database. Never rely on copying the raw /srv/immich/postgres folder while the database runs: that copy can be corrupted.

We use restic, which encrypts, deduplicates and versions backups, and stores them on another server over SFTP. First install it, create a password file and an SSH key for root, and copy the key to the backup host:

sudo apt install -y restic
sudo sh -c 'openssl rand -base64 32 > /root/.restic-pass && chmod 600 /root/.restic-pass'
sudo mkdir -p -m 700 /root/.ssh
sudo ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""
sudo ssh-copy-id -i /root/.ssh/id_ed25519.pub backup@backup.example.com
sudo restic -r sftp:backup@backup.example.com:/srv/restic/immich --password-file /root/.restic-pass init

Store a copy of /root/.restic-pass in your password manager. Without it, the backup cannot be decrypted, by you or by anyone else. Now create the backup script:

sudo nano /usr/local/sbin/immich-backup.sh
#!/bin/sh
set -eu
export RESTIC_REPOSITORY="sftp:backup@backup.example.com:/srv/restic/immich"
export RESTIC_PASSWORD_FILE="/root/.restic-pass"

restic backup /srv/immich/library /opt/immich/docker-compose.yml /opt/immich/.env \
  --exclude /srv/immich/library/thumbs \
  --exclude /srv/immich/library/encoded-video

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

We exclude thumbnails and transcoded videos because Immich can regenerate them, which saves a lot of backup space. Make the script executable and schedule it at 03:30, after the nightly database dump:

sudo chmod 700 /usr/local/sbin/immich-backup.sh
echo '30 3 * * * root /usr/local/sbin/immich-backup.sh >> /var/log/immich-backup.log 2>&1' | sudo tee /etc/cron.d/immich-backup
sudo /usr/local/sbin/immich-backup.sh

Ubuntu has cron installed by default; on minimal Debian 13 images it may be missing, so run sudo apt install -y cron if /etc/cron.d does not exist. The last command runs the first backup by hand, which can take a long time for a big library. Afterwards, list the snapshots with sudo restic -r sftp:backup@backup.example.com:/srv/restic/immich --password-file /root/.restic-pass snapshots, and at least once, practice a restore to a test folder with restic restore latest --target /tmp/restore-test. A backup you never tested is only a hope.

Storage paths and the storage template

Inside UPLOAD_LOCATION you will find several folders: upload (fresh uploads), library (files organized by the storage template, if enabled), thumbs, encoded-video, profile and backups. Do not move or rename files there by hand; Immich tracks every path in its database.

By default, files are stored by internal IDs, which is efficient but not readable. If you enable the storage template in Administration > Settings > Storage Template, Immich arranges originals in human-friendly folders, for example by year and date. That makes your library easy to browse with any file manager, and easy to recover even without Immich. After enabling it, run the “Storage Template Migration” job to move existing files.

Already have years of photos in folders? Use an external library instead of uploading them again. Add a read-only mount to the immich-server service in docker-compose.yml, under its volumes: list:

      - /srv/old-photos:/mnt/old-photos:ro

Run sudo docker compose up -d to apply it, then in Administration > External Libraries create a library with the import path /mnt/old-photos. Immich indexes the files where they are and never modifies them, thanks to :ro.

Machine learning: smart search, faces and when to turn it off

The machine learning container powers the features that make Immich feel like a modern photo app:

  • Smart search: type “red car at night” or “birthday cake” and Immich finds matching photos, using CLIP models that link images and text.
  • Facial recognition: faces are detected and grouped into people, which you can name.
  • Duplicate detection: visually similar photos are flagged so you can clean them up.

Settings live in Administration > Settings > Machine Learning Settings. The default smart search model works best with English queries. If you want to search in Romanian or another language, pick one of the multilingual models offered in the list, then run the “Smart Search” job again for all assets in Administration > Jobs. Larger models give better results but need more RAM and CPU time.

On a VPS without a GPU, everything runs on the CPU, which is fine once the initial indexing is done. To keep the server responsive during a big import, lower the concurrency for “Smart Search”, “Face Detection” and “Thumbnail Generation” in the job settings. The ML container also unloads models after a few minutes of inactivity (controlled by MACHINE_LEARNING_MODEL_TTL, 300 seconds by default), so idle memory use drops back down.

On a 4 GB VPS, you have two options. Add swap, so peaks do not kill containers:

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

Or turn machine learning off completely: comment out the entire immich-machine-learning block in docker-compose.yml with #, run sudo docker compose up -d --remove-orphans, and disable machine learning in the admin settings so jobs do not keep failing. Backup, timeline, albums and sharing all keep working. Note that swap usually cannot be configured from inside an LXC container.

Keeping Immich up to date

Immich moves fast, so read the release notes on GitHub before each update, especially the “breaking changes” section. With IMMICH_VERSION=v2, an update is:

cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune -f

pull downloads newer images for the pinned tag, up -d recreates only the containers that changed, and prune removes old images to free disk space. When a new major version arrives, the release notes will tell you whether to download a new docker-compose.yml too. Make sure a fresh restic snapshot exists before any major upgrade.

Troubleshooting and common mistakes

  • The postgres container keeps restarting. Usually the password contains special characters, or DB_DATA_LOCATION points to a network share. Use letters and numbers only and a local disk. Remember that changing DB_PASSWORD after the first start does not change the password inside an existing database.
  • 502 Bad Gateway from Caddy. Immich is not up yet or crashed. Check sudo docker compose ps and the server logs. During the first start, wait until all containers are healthy.
  • Large videos fail to upload. If you use Nginx or another proxy in front, raise the body size limit and timeouts. Caddy needs no change.
  • Containers die with exit code 137. That means the kernel killed them for lack of memory. Add swap, reduce job concurrency or disable machine learning.
  • The phone stops uploading in the background. Exclude the app from battery optimization on Android, enable Background App Refresh on iOS, and open the app periodically.
  • Port 2283 is reachable from outside. You skipped the 127.0.0.1 binding in step 4. Test from another machine with curl http://your-vps-ip:2283 and fix the Compose file.
  • The disk is full. Check with df -h and sudo du -sh /srv/immich/library/*. Transcoded videos and thumbnails grow over time; plan disk upgrades before you reach 85% usage.
  • Search finds nothing. The smart search job may still be running. Watch progress in Administration > Jobs.

Security notes

  • Keep port 2283 private. Docker bypasses ufw for published ports, so the localhost binding is your real protection. Only Caddy should be public.
  • Claim the admin account right away, as described in step 7, and use a long unique password for every user.
  • Be careful with shared links. Public album links are convenient, but set an expiry date and a password, and remove links you no longer need.
  • Consider single sign-on. Immich supports OAuth, so you can connect it to an identity provider you already use and enforce two-factor authentication there.
  • Update regularly, both the operating system (apt) and Immich, after reading the release notes.
  • Encrypt backups and protect the key. restic encrypts everything, but the password file is the key to your entire photo history: store it outside the VPS as well.
  • Use SSH keys for the VPS and disable password logins once your keys work.

So, is Immich worth running yourself?

If you are tired of paying more every year for cloud photo storage, or you simply want your family memories on a server you control, Immich is the most complete self-hosted alternative to Google Photos today. With Docker Compose, the installation itself takes less than half an hour; the real work is choosing a server with enough RAM and disk, and setting up backups from day one. Do that, and you get automatic phone backup, smart search, face recognition and shared albums, without anyone else looking at your photos.

You can start on a VPS with us, in our own data center in Romania, and grow the disk as your library grows. If you would rather not deal with Docker, certificates and backup scripts, our server administration team can install, secure, monitor and back up Immich for you.

FAQ

Can I run Immich on a 2 GB VPS?

Not comfortably. The containers alone need over 1 GB without machine learning, and imports cause spikes. Treat 4 GB with swap and machine learning disabled as the minimum, and 6 to 8 GB as the comfortable size.

Will my photos stay in their original quality?

Yes. Immich stores the original files byte for byte. Thumbnails and transcoded videos are extra copies used for fast browsing; the originals are never compressed.

Can I store the library on object storage like S3?

Immich expects a normal filesystem. The database must stay on a local SSD; the photo library can sit on a separate, larger disk attached to the VPS. Back up to object storage with restic if you want, but do not mount it as the live library.

What happens to the photos if Immich stops working?

They are still regular files in UPLOAD_LOCATION. With the storage template enabled, they are even sorted into readable year and date folders, so you can copy them anywhere.

Does Docker inside an LXC VPS work for Immich?

It can, if nesting is enabled for the container, but firewall rules, swap and memory limits behave differently. For a long-lived photo archive, a KVM VPS is the simpler and more predictable choice.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX