Immich is the most complete open-source replacement for Google Photos: automatic phone backup, timeline, face recognition, semantic search and shared albums. This guide uses the official Docker Compose stack on a 2 vCPU / 4 GB server and puts it behind a domain with HTTPS.
1. Prerequisites
- A Linux server (Ubuntu 22.04 or Debian 12), 2 vCPU / 4 GB minimum, with photos on a separate data disk.
- Docker and the Compose plugin:
docker --versionanddocker compose versionmust work. - A domain such as
photos.example.compointing at the server. The mobile app needs HTTPS. - Ports
2283for testing and80/443for production.
2. Images and components
| Component | Image | Purpose |
|---|---|---|
| immich-server | ghcr.io/immich-app/immich-server:release | Web and API, port 2283 |
| immich-machine-learning | ghcr.io/immich-app/immich-machine-learning:release | Face, object and CLIP inference |
| database | ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 | PostgreSQL with vector extensions |
| redis | docker.io/valkey/valkey:9 | Job queue cache |
Do not use the Compose file from the main branch; versions must match the release. Each release ships its own docker-compose.yml and example.env.
3. Download the official Compose file and configure it
mkdir -p /opt/immich && cd /opt/immich
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
Edit .env and change at least these values:
# Where photos and videos live — point this at your data disk
UPLOAD_LOCATION=/data/immich/library
# Database files
DB_DATA_LOCATION=/data/immich/postgres
# Database password — generate a strong one
DB_PASSWORD=replace-with-a-random-strong-password
# Timezone
TZ=Asia/Shanghai
Leave docker-compose.yml as shipped. The core services look like this (excerpt):
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'
depends_on: [redis, database]
restart: always
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
volumes:
- model-cache:/cache
env_file: [.env]
restart: always
redis:
image: docker.io/valkey/valkey:9
restart: always
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
restart: always
Start it:
docker compose up -d
docker compose ps
docker compose logs -f immich-server
Once you see Immich Server is listening on ... 2283 it is up. Open http://SERVER_IP:2283.
4. Reverse proxy and HTTPS
Immich needs HTTPS for reliable background upload from the mobile app. Nginx example:
server {
listen 80;
server_name photos.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name photos.example.com;
ssl_certificate /etc/letsencrypt/live/photos.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/photos.example.com/privkey.pem;
# Video uploads need a large limit, otherwise you get 413
client_max_body_size 50000M;
location / {
proxy_pass http://127.0.0.1:2283;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Stream large uploads instead of buffering to disk
proxy_request_buffering off;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
Get a certificate with certbot --nginx -d photos.example.com.
5. First-run setup
- Open
https://photos.example.comand register the first account — it becomes the administrator. - Go to Administration → Settings → User Management, disable public registration and invite family members manually.
- Install the Immich app, enter
https://photos.example.comas the server, then choose albums to back up. - For a large library, let phones upload overnight on Wi-Fi and power; the server will queue thumbnails and AI indexing.

6. Backup, upgrade and migration
- Backup: two things matter — originals in
UPLOAD_LOCATIONand the database. Dump it withdocker compose exec -t database pg_dumpall -U postgres > backup.sqlon a daily schedule. - Upgrade: read the release notes for breaking changes, then
docker compose pull && docker compose up -d. Major versions (1.x → 2.x) must follow the official upgrade guide step by step. - Migration: restore
UPLOAD_LOCATIONand the database on the new host, then start the same Compose version.
7. Troubleshooting
413 when uploading large videos? Raise Nginx client_max_body_size to 50000M and reload.
Machine-learning container using too much RAM? Normal — models idle around 1 GB. Add mem_limit: 2g to immich-machine-learning or disable some smart features.
The app says "server unavailable"? Check that you are using HTTPS, that the certificate is trusted, and that X-Forwarded-Proto reaches the backend.
8. Wrap-up
Immich is the most complete option for a family album, at the cost of some server and ops knowledge. If you would rather skip the setup, we offer a tested deployment service: environment, Compose stack, HTTPS and mobile connectivity delivered together, from $99 within 48 hours.
Related: Which self-hosted photo album should you pick in 2026? · PhotoPrism tutorial · ChronoFrame tutorial


