This guide covers the full process of deploying Immich, an open-source Google Photos alternative on a Contabo Cloud VPS 8 using Docker Compose. It walks through VPS provisioning, Docker installation, the Immich Compose stack, a Caddy reverse proxy with automatic SSL, migration to v3, and mobile client configuration.
Requirements
Immich Docker deployments rely on a small stack of containers working together: the main server, PostgreSQL, Redis, and a machine learning container. The VPS needs enough headroom to run all of them without contention.
- VPS: Contabo Cloud VPS 8 (8 vCores, 24 GB RAM, 300 GB SSD), or an equivalent plan with at least 6 GB of RAM. This covers all containers comfortably, with spare capacity as the photo library grows.
- Operating system: Ubuntu 22.04 LTS or newer
- Docker Engine and the Docker Compose plugin
- A registered domain name pointed at the VPS through an A record
- A reverse proxy for HTTPS termination — Caddy is used throughout this guide for its automatic certificate handling
Step 1: Provision and Secure Your Contabo VPS
Getting the Immich VPS ready involves a handful of standard hardening steps before any containers are touched. Skipping this stage is one of the more common reasons self-hosted services end up compromised, given that a freshly provisioned VPS is reachable from the public internet the moment it boots.
- Order the Contabo Cloud VPS 8 plan and select Ubuntu 22.04 LTS as the base image during provisioning. Contabo typically provisions the server within a few minutes and emails the root credentials once it is ready.
- Generate an SSH key pair locally with ssh-keygen and upload the public key to the VPS during setup, or add it afterward through the Contabo control panel. Once key-based login is confirmed to work, disable password authentication in /etc/ssh/sshd_config by setting PasswordAuthentication no, then restart the SSH service.
- Configure UFW to allow only the ports needed: SSH (22), HTTP (80), and HTTPS (443). Run sudo ufw allow OpenSSH, sudo ufw allow http, sudo ufw allow https, then enable the firewall with sudo ufw enable. Port 2283, where Immich listens, stays closed to the public internet — all external traffic will pass through the reverse proxy.
- Create a non-root user with sudo privileges using adduser and usermod -aG sudo, then switch to that account for the rest of the setup. Running Docker as root increases the blast radius if a container is ever compromised.
Step 2: Install Docker and Docker Compose
Install Docker Engine and the Compose plugin directly from Docker’s official repository rather than the older packages bundled with Ubuntu, which tend to lag several versions behind:
sudo apt update && sudo apt upgrade -y
sudo apt install ca-certificates curl gnupg -y
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
sudo usermod -aG docker $USERLog out and back in afterward so the new group membership takes effect. Confirm the install with docker –version and docker compose version, both of which should report current stable release numbers.
Step 3: Deploy Immich with Docker Compose
With Docker in place, the Immich install follows the official Compose configuration. Create a directory for the deployment, then pull down the required files from the latest GitHub release:
mkdir ~/immich-app && cd ~/immich-app
curl -o docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
curl -o .env https://github.com/immich-app/immich/releases/latest/download/example.envOpen the .env file and set the following values before starting the stack:
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
DB_PASSWORD=a-strong-generated-password
IMMICH_VERSION=release
TZ=Etc/UTCGenerate DB_PASSWORD rather than typing something by hand, and stick to alphanumeric characters to avoid Docker mis-parsing special characters in the environment file. Once the values are set, start the stack:
docker compose up -dRun docker compose ps to confirm each container reaches a healthy state, which typically takes a minute or two on first boot while the database initializes its schema. Immich is then reachable at http://server-ip:2283, though it still needs a domain and SSL certificate before daily use.
Step 4: Set Up a Reverse Proxy with SSL (Caddy)
Caddy provisions and renews Let’s Encrypt certificates automatically, removing a maintenance task that other proxies handle manually through Certbot. Install Caddy from its official APT repository, then edit /etc/caddy/Caddyfile:
photos.example.com {
reverse_proxy localhost:2283 {
header_up X-Forwarded-Proto {scheme}
}
}Replace photos.example.com with the domain pointed at the VPS through its DNS A record, then reload Caddy to apply the change:
sudo systemctl reload caddyCaddy requests a certificate for the domain automatically on the first incoming request. All traffic on port 2283 is proxied through HTTPS on port 443; because UFW only allows ports 80 and 443 from the outside, the underlying Immich port stays inaccessible directly.
Step 5: Migrate from Immich v2 to v3
The v3 release concentrates breaking changes to the API and database schema. Migrations are not designed to be rolled back cleanly once they have run, so it is always worth reading the official v3 release notes before proceeding, particularly for deployments with third-party integrations.
- Back up the PostgreSQL database and the upload directory before touching anything. Database schema migrations cannot be cleanly reversed once they have run. Use docker exec -t immich_postgres pg_dumpall -c -U postgres > immich_db_backup.sql and archive the UPLOAD_LOCATION directory separately.
- Edit .env: set IMMICH_VERSION=v3, or pin to a specific tag such as v3.0.0. It is good practice to read through the remaining .env entries against their comments, since some deployments benefit from moving paths to a dedicated data volume.
- Pull the updated images and restart the stack with docker compose pull && docker compose up -d. This downloads the updated server, database, and machine learning images before recreating the containers.
- Watch the logs with docker compose logs -f immich-server until the health checks pass, then confirm the web interface loads and existing albums and assets are visible. Check any third-party tools, backup scripts, or integrations that call the Immich API directly against the current v3 API documentation for breaking changes.
Step 6: Configure Mobile App and Backup
Once the server is reachable over HTTPS, the Immich mobile app for iOS or Android can connect to it by entering the server URL, for example https://photos.example.com, on the login screen. After signing in with the admin account created during first setup, background backup can be enabled from the app settings so new photos and videos upload automatically without opening the app.
Background backup on both platforms depends on the operating system not aggressively killing the app process while it sits idle. Check the battery optimization settings for the Immich app and exclude it from any automatic app-hibernation feature the phone manufacturer includes. Manufacturers like Samsung and Xiaomi apply additional battery restrictions on top of stock Android that can silently stop background uploads if left at default settings. Enabling unrestricted battery usage for the app, along with allowing background data over both Wi-Fi and mobile networks if syncing away from home is desired, generally resolves it.
FAQ: Installing Immich
Set IMMICH_VERSION=v3 (or a specific tag such as v3.0.0) in the .env file, then run docker compose pull followed by docker compose up -d. Back up the database and upload directory first, and check the official v3 release notes for any breaking changes that third-party integrations may depend on.
The official minimum is 6 GB of RAM, with 8 GB recommended for a smoother experience. This accounts for the combined footprint of the Immich server, PostgreSQL, Redis, and the machine learning container running together.
Immich can run on 4 GB of RAM if the machine learning container is disabled by setting IMMICH_MACHINE_LEARNING_ENABLED=false in the .env file. This removes facial recognition and semantic search but keeps core upload and browsing functionality intact.
The PostgreSQL database used by Immich can be safely backed up using docker exec with pg_dumpall:
docker exec -t immich_postgres pg_dumpall -c -U postgres > immich_db_backup.sql
Alternatively, stop the stack entirely with docker compose down before copying the DB_DATA_LOCATION directory directly to prevent database corruption. Either approach should run on a regular schedule alongside a separate backup of the UPLOAD_LOCATION directory, since the database and the media files both need to be restorable together for a backup to be useful.