Independent hosting operator · New York · Dallas · Amsterdam Status Looking Glass Contact
D4 NetworksD4 Networks
Client area ↗ Deploy a server
Home / Resources / Self-host Immich, a Google Photos alternative, on your VPS

Tutorials

Self-host Immich, a Google Photos alternative, on your VPS

Oct 26, 2026 · 13 min read

Immich is an open-source photo and video library that you run yourself. Its phone apps back up your camera roll, and it has albums, sharing, face recognition and search by description, such as "dog on a beach". We tested this guide with Immich v3.3.5, the latest release at the time, on Debian 13, Ubuntu 24.04 and AlmaLinux 10.

1. Check that your server is big enough

Immich needs at least 6GB of RAM and 2 CPU cores; 8GB and 4 cores are recommended. On our 6GB test server it used about 2.5GB at rest. With 4GB it runs only with machine learning turned off, which removes face recognition and search by description.

For disk, plan for your photos plus 10 to 20 percent for thumbnails, and about 10GB for the software, database and its backups.

Machine learning also needs a CPU that supports x86-64-v2. Every D4 Networks location does; to confirm:

ssh session
$ /lib64/ld-linux-x86-64.so.2 --help | grep x86-64-v2

The x86-64-v2 line should include supported. To pick a plan, see how much RAM and CPU your VPS needs.

2. Install Docker

On AlmaLinux, update the server and reboot before you install Docker:

RHEL-based
$ sudo dnf upgrade -y
$ sudo reboot

On our fresh AlmaLinux 10 test server, Docker would not start until it ran the newest kernel.

Then install Docker from Docker's own repository, as in our Docker guide; Immich warns that some distributions' own Docker packages can fail. Check the Compose plugin:

ssh session
$ docker compose version

Immich needs docker compose, with a space; the older docker-compose is not supported.

3. Download Immich

Download the setup files from the latest release into their own folder:

ssh session
$ mkdir ~/immich-app
$ cd ~/immich-app
$ curl -fsSL -o docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
$ curl -fsSL -o .env https://github.com/immich-app/immich/releases/latest/download/example.env

4. Set a password and keep Immich private

Before the first start, give the database a random password (Immich allows only letters and digits):

ssh session
$ sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)/" .env
$ chmod 600 .env

Then make Immich listen only on the server itself:

ssh session
$ sed -i "s/- '2283:2283'/- '127.0.0.1:2283:2283'/" docker-compose.yml
$ grep 2283 docker-compose.yml

The line should now read - '127.0.0.1:2283:2283'. Without this, anyone could reach Immich on port 2283, because ports opened by Docker skip ufw and firewalld rules. Caddy will be the only way in. Your photos go in ~/immich-app/library; the other .env settings can stay as they are.

5. Start Immich

ssh session
$ docker compose up -d
$ docker compose ps

The first start downloads about 4.6GB of images. When the four containers show Up, check that Immich answers:

ssh session
$ curl http://127.0.0.1:2283/api/server/ping

It should print {"res":"pong"}. Then confirm it is not reachable from outside:

ssh session
$ docker compose port immich-server 2283

This must print 127.0.0.1:2283. If it shows 0.0.0.0:2283, step 4 did not apply: run docker compose down, fix the ports line in docker-compose.yml, and start again.

Create your account now, while Immich is still private, because the first account becomes the admin. On your own computer, open an SSH tunnel:

your computer
$ ssh -L 12283:127.0.0.1:2283 user@203.0.113.10

With that session open, browse to http://127.0.0.1:12283, click Getting Started and create your account. Sign-up then closes, and you can close the tunnel; add other people yourself under Administration.

6. Add HTTPS with Caddy

Point a domain at your server, for example an A record for photos.example.com. If your DNS is at Cloudflare, set that record to DNS only (grey cloud); Cloudflare's proxy rejects uploads over 100MB on its Free and Pro plans. Then install Caddy from its official repository:

Ubuntu and Debian
$ sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gpg
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
$ sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list
$ sudo apt update
$ sudo apt install -y caddy
RHEL-based
$ sudo dnf install -y dnf-plugins-core
$ sudo dnf copr enable -y @caddy/caddy
$ sudo dnf install -y caddy
$ sudo systemctl enable --now caddy

Replace everything in /etc/caddy/Caddyfile with these lines, using your domain:

/etc/caddy/Caddyfile
photos.example.com {
	reverse_proxy 127.0.0.1:2283
}

Check the file, then open the web ports and load the new config:

Ubuntu and Debian
$ sudo caddy validate --config /etc/caddy/Caddyfile
$ sudo ufw allow 80,443/tcp
$ sudo systemctl reload caddy
RHEL-based
$ sudo caddy validate --config /etc/caddy/Caddyfile
$ sudo firewall-cmd --get-active-zones
$ zone=public
$ sudo firewall-cmd --permanent --zone="$zone" --add-service=http --add-service=https
$ sudo firewall-cmd --reload
$ sudo systemctl reload caddy

The check must print Valid configuration. For firewalld, replace public with the zone that --get-active-zones lists for your network interface.

Once the domain points to your server, Caddy gets a free HTTPS certificate by itself. In our tests it handled a 224MB video upload with no extra settings.

7. Connect your phone

Install the Immich app from the App Store, Google Play or the FUTO F-Droid repository. Enter https://photos.example.com as the server address, log in, and turn on backup. If uploads stall on Android, let Immich run without battery limits; on iPhone, turn on Background App Refresh for it.

8. Back up the right things

By default, Immich saves its database every night in ~/immich-app/library/backups, keeping the last 14. Those copies hold no photos and sit on the same server, so they are lost with it.

For a backup where the database matches the files, stop the Immich server and take a fresh database dump:

ssh session
$ cd ~/immich-app
$ docker compose stop immich-server
$ (umask 077; set -o pipefail; docker compose exec -T database pg_dump --clean --if-exists --username=postgres --dbname=immich | gzip > db-dump.sql.gz) && gzip -t db-dump.sql.gz && zgrep -q 'PostgreSQL database dump complete' db-dump.sql.gz && echo 'Backup verified'

The parentheses keep the dump private from the moment it is created, and the checks catch a dump that failed or was cut short. Use it only if Backup verified appears.

Copy db-dump.sql.gz, the library folder, .env and docker-compose.yml to another server, for example with restic, then run docker compose start immich-server. Check now and then that the backups still run, and test a restore before you need one.

9. Upgrade Immich

Read the release notes on Immich's GitHub page and update the phone apps first. Take a snapshot, because Immich cannot go back to an older version. Then:

ssh session
$ cd ~/immich-app
$ docker compose pull && docker compose up -d

This keeps you on the main version in .env, version 3 at the time of writing. Moving to the next one means changing IMMICH_VERSION yourself, after reading its release notes, which may also ask for changes to the setup files.

10. When something goes wrong

  • Read the server log with docker compose logs immich-server in ~/immich-app.
  • A container that keeps restarting, or exit code 137, usually means memory ran out. Add swap, or turn off machine learning under Administration, Settings, Machine Learning Settings.
  • If HTTPS does not load, check the domain, that ports 80 and 443 are open, and Caddy's log with sudo journalctl -u caddy -n 50.

Where to go next

Keep the server patched and protected as in the first ten minutes on a new VPS. Immich's docs note that a public site always carries some risk from undiscovered bugs; if only your household uses it, you can keep it off the internet and reach it through a WireGuard VPN.

immich photos docker caddy self-hosted ubuntu debian almalinux

Related articles