🛠️ Tutorials · ⏱ 10 min read

Deploy Immich with Docker in 2026: Complete Guide (HTTPS, Reverse Proxy, Backups)

Step-by-step 2026 tutorial to self-host Immich, the Google Photos alternative, using Docker Compose. Features include photo/video gallery, facial recognition, automatic HTTPS via Caddy and Let's Encrypt, backups, and hardening. Ready-to-use configs included.

S By Selfhostr Team · independent tests
Deploy Immich with Docker in 2026: Complete Guide (HTTPS, Reverse Proxy, Backups)
ⓘ This article may contain affiliate links (no extra cost to you, it supports our tests). See the disclosure.
💾
4 GB
Min RAM
📦
4 services
Containers
🐧
Ubuntu 24.04
OS Support
💿
High capacity
Storage

👍 What we like

  • Open-source self-hosted alternative to Google Photos
  • Automatic mobile backup for iOS and Android
  • Facial recognition and smart search capabilities
  • Free with no subscription fees
  • Handles HTTPS via Caddy reverse proxy

👎 What to watch

  • Resource-intensive machine learning module
  • Requires complex multi-container Docker setup
  • Needs significant disk space for photo libraries
  • Requires manual DNS and firewall configuration
▶ Video tutorial: deploy Immich
📑 Contents

You want to leave Google Photos without giving up automatic phone backup, facial recognition, and a smooth gallery experience? Immich is currently the most mature self-hosted alternative: an open-source application that automatically backs up your photos and videos from your iOS/Android mobile apps, organizes them by date and location, detects faces, and offers smart search — all on your own server, with no subscription.

The catch: Immich is a multi-container service (server, machine learning, PostgreSQL, Redis) hosting irreplaceable data. It must therefore be deployed cleanly, with HTTPS and reliable backups. In this tutorial, we deploy Immich via Docker Compose, behind a Caddy reverse proxy that handles HTTPS and automatic Let’s Encrypt certificates.

Prerequisites

  • A server or VPS running Ubuntu 24.04 (or Debian 12) with at least 4 GB of RAM: Immich’s machine learning module is resource-intensive. For large volumes, aim for 8 GB. An entry-level VPS from Hetzner or Scaleway works perfectly, but many users host Immich on a NAS or mini-PC at home for massive storage.
  • Lots of disk space. Your photos quickly add up to several hundred gigabytes. Plan accordingly.
  • Docker and Docker Compose installed (command in Step 1).
  • A domain name for which you control the DNS. We will use photos.example.com.
  • Ports 80 and 443 open on your firewall for Let’s Encrypt validation and HTTPS traffic.

Step 1: Install Docker and Docker Compose

If Docker is not present, install it from the official repository (Ubuntu repository versions are often outdated):

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/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/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 -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Add your user to the docker group (log out and back in afterwards):

sudo usermod -aG docker $USER

Verify:

docker --version && docker compose version

Step 2: Configure DNS

Caddy needs your domain to point to the server’s IP address to issue the certificate. At your DNS provider, create an A record (and AAAA if IPv6):

TypeNameValue
Aphotos.example.com203.0.113.10

Check propagation from your machine:

dig +short photos.example.com

The command should return your server’s IP. Until it does, Let’s Encrypt will fail to issue the certificate.

Step 3: Create the Shared Docker Network

For Caddy to reach the Immich container by its name, both must share a dedicated Docker network. Create it once:

docker network create web

If you are already following our Caddy reverse proxy tutorial, this network might already exist: the command will return an error, which is harmless; ignore it.

Step 4: Prepare the Folder and .env File

Create your working directory and the environment file. Immich uses a .env file to centralize versions, passwords, and paths:

mkdir -p ~/immich && cd ~/immich
nano .env
# Immich version (pin a stable version rather than latest in production)
IMMICH_VERSION=release

# Library location (uploaded photos/videos). Use a path with plenty of space.
UPLOAD_LOCATION=./library

# PostgreSQL data location (DO NOT put this in a network-mounted folder)
DB_DATA_LOCATION=./postgres

# Database credentials — use a long, unique password
DB_PASSWORD=replace_with_a_long_random_password
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

# Timezone (fixes displayed EXIF dates)
TZ=Europe/Paris

Generate a strong password for the database:

openssl rand -base64 32

Paste the result into DB_PASSWORD.

Step 5: The Immich docker-compose.yml

This is the core of the deployment. Immich orchestrates four containers: the server, the machine learning module, PostgreSQL (with the vector extension for similarity search), and Redis.

nano docker-compose.yml
name: immich

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    depends_on:
      - redis
      - database
    restart: unless-stopped
    healthcheck:
      disable: false
    networks:
      - web
      - immich-internal

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      disable: false
    networks:
      - immich-internal

  redis:
    container_name: immich_redis
    image: docker.io/valkey/valkey:8-bookworm
    healthcheck:
      test: redis-cli ping || exit 1
    restart: unless-stopped
    networks:
      - immich-internal

  database:
    container_name: immich_postgres
    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}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    shm_size: 128mb
    restart: unless-stopped
    networks:
      - immich-internal

volumes:
  model-cache:

networks:
  web:
    external: true
  immich-internal:

The Immich server is on two networks: immich-internal to talk to its database and Redis, and web to be reachable by Caddy. PostgreSQL, Redis, and the ML module are only on the internal network, never accessible from the outside. Note the absence of a ports: section: Immich is not exposed directly; all traffic goes through the reverse proxy.

Step 6: The Caddyfile for Automatic HTTPS

If you already have Caddy in place, simply add the site block. Otherwise, create a Caddy directory:

mkdir -p ~/caddy && cd ~/caddy
nano Caddyfile
{
    email admin@example.com
}

photos.example.com {
    encode gzip zstd

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server
    }

    # Immich sends large files: increase body size limits
    request_body {
        max_size 50000MB
    }

    reverse_proxy immich_server:2283
}

The request_body max_size directive is crucial: without it, uploading large video files would fail. Immich’s internal port is 2283.

Then, the Caddy docker-compose.yml:

nano docker-compose.yml
services:
  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - web

volumes:
  caddy_data:
  caddy_config:

networks:
  web:
    external: true

The caddy_data volume stores your certificates: without it, you would request a new certificate on every restart and quickly hit Let’s Encrypt rate limits.

Step 7: Start the Services

Start Immich first (the first launch downloads several images, so be patient), then Caddy:

cd ~/immich && docker compose up -d
cd ~/caddy && docker compose up -d

Follow Immich’s logs to see the server start up:

cd ~/immich && docker compose logs -f immich-server

When you see Immich Server is listening on ..., visit https://photos.example.com. The administrator account creation page will appear.

Step 8: Create the Admin Account and Configure Apps

The first account created automatically becomes the administrator. Choose a strong email and password. Then, in the admin panel:

  1. Disable public registration if you don’t need it, and manually create accounts for your family (Administration → Users).
  2. Install the Immich app on iOS or Android, enter the URL https://photos.example.com, and log in.
  3. In the mobile app, enable automatic backup: select the albums to upload. Your photos now upload automatically, just like with Google Photos.

Facial recognition and smart search run in the background via the machine learning container. On a large initial import, let it work for several hours; you can track progress in Administration → Tasks.

Step 9: Reliable Backups

Immich hosts irreplaceable memories. Two things must be backed up: the PostgreSQL database (metadata, albums, faces) and the file library (UPLOAD_LOCATION). A simple cp of the live database would corrupt it: use a consistent dump.

nano ~/immich/backup.sh
#!/bin/bash
set -euo pipefail
cd "$HOME/immich"
BACKUP_DIR="$HOME/immich/backups"
STAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"

# Consistent database dump (the official method recommended by Immich)
docker compose exec -T database \
  pg_dumpall --clean --if-exists --username=postgres \
  | gzip > "$BACKUP_DIR/immich-db-$STAMP.sql.gz"

# Keep the last 14 database dumps
find "$BACKUP_DIR" -name 'immich-db-*.sql.gz' -mtime +14 -delete
chmod +x ~/immich/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * $HOME/immich/backup.sh") | crontab -

The file library (often hundreds of gigabytes) should be backed up separately, ideally incrementally and off-site. Our guide automatic backup with Restic and Backblaze shows how to replicate this folder to an encrypted remote storage. A backup on the same server protects against neither fire nor ransomware.

Step 10: Final Verification

Check that everything is in place:

# Containers are running
docker compose -f ~/immich/docker-compose.yml ps

# HTTP redirects to HTTPS (code 308)
curl -sI http://photos.example.com | head -1

# HTTPS responds and the certificate is valid
curl -sI https://photos.example.com | head -1

Check the internal health status from the Administration → Status panel: all services (database, ML, Redis) should be green.

Security and Hardening

  • Strict UFW firewall. Open only 80, 443, and SSH. Internal ports (PostgreSQL 5432, Redis, ML) are never exposed — the network configuration handles this already.
  • Strong passwords for the admin account and database, public registration disabled, and 2FA enabled from user settings.
  • Controlled updates. Immich evolves quickly, and some versions require database migrations. Back up before every update, read the release notes, then run docker compose pull && docker compose up -d. Pin a specific version in .env rather than release in production.

To learn more about securing an exposed server, check out install and secure an Ubuntu VPS.

Common Pitfalls and Troubleshooting

  • Blocked uploads / 413 error. Reverse proxy size limit: check request_body max_size in the Caddyfile (Step 6). With Nginx Proxy Manager, increase client_max_body_size.
  • ML container fails to start or runs out of RAM. The machine learning container can be killed (OOM) on a 2 GB server. Add RAM or swap.
  • Database fails to start after an update. Almost always a failed migration on a skipped version. Restore the last dump and proceed version by version.
  • DB_DATA_LOCATION on a network share. PostgreSQL requires a local POSIX filesystem: an NFS/SMB mount will corrupt the database. Keep it on a local disk.

FAQ

Can Immich really replace Google Photos?

For daily use, yes: automatic phone backup, chronological gallery, shared albums, location map, facial recognition, and keyword search (“beach”, “dog”). The mobile experience is very similar. The major difference: you are responsible for storage and backups, but you retain full control over your data.

How much RAM do I need?

Count on a minimum of 4 GB, mainly due to the machine learning module that indexes faces and objects. For very large libraries or multiple active users, 8 GB is preferable.

What is the difference between an “uploaded” library and an “external” library?

The uploaded library corresponds to files sent from your apps (Immich manages and organizes them). An external library points to an existing folder mounted read-only: Immich indexes it without moving it. Useful for scanning a collection already organized on a NAS.

Is HTTPS really mandatory?

Yes. Mobile apps send your credentials and photos over the network; with HTTP, everything travels in cleartext, and some mobile features refuse insecure connections. The Caddy reverse proxy in this tutorial handles this automatically.

Can I host Immich on a NAS?

Yes, this is a very common use case to take advantage of large storage. Synology (Container Manager), QNAP, and especially TrueNAS support Docker, with the same logic: containers behind an HTTPS reverse proxy. See our comparison Synology vs TrueNAS vs QNAP.

Immich or PhotoPrism: which one to choose?

Immich excels in automatic mobile backup and the Google Photos-like experience; PhotoPrism is renowned for its organization and indexing. Our comparison Immich vs PhotoPrism details the strengths of each.

You now have your own Google Photos, in HTTPS, with automatic mobile backup, facial recognition, and 100% sovereign data. It is one of the services that changes the homelab experience the most. To follow Immich new versions, risky database migrations, and best practices for self-hosting, subscribe to our Telegram watch bot.

Tags: ImmichDockerDocker ComposeCaddyLet's EncryptSelf-hostingGoogle Photos alternativeHTTPSReverse ProxyBackups

Related

🛠️ Tutorials

Deploy Jellyfin with Docker in 2026: Complete Guide (Hardware Transcoding, HTTPS, Reverse Proxy)

Step-by-step 2026 tutorial to self-host Jellyfin, the open-source media server, using Docker Compose. Covers film/series libraries, Intel/NVIDIA hardware transcoding, automatic HTTPS via Caddy and Let's Encrypt, remote access, and hardening. Ready-to-use configs included.

Read
🛠️ Tutorials

Automated HTTPS Reverse Proxy with Caddy and Docker in 2026 (Effortless Let's Encrypt Certs)

2026 tutorial on deploying a Caddy reverse proxy with Docker: automatic HTTPS and Let's Encrypt certificates, multi-service routing, security headers, and seamless renewal. Ready-to-use Caddyfile and docker-compose configs.

Read
🛠️ Tutorials

Deploy n8n with Docker and HTTPS in 2026: Complete Tutorial (Postgres, Reverse Proxy, Persistence)

Step-by-step 2026 guide to self-host n8n, the no-code automation platform, using Docker Compose. Features PostgreSQL, workflow persistence, automatic HTTPS via Caddy and Let's Encrypt, credential encryption, backups, and hardening. Ready-to-use configs.

Read