English
Get started

← All templates · Media

Deploy Immich in one click

Your photos and videos, backed up from your phone, with smart search, faces and albums

A self-hosted alternative to Google Photos and iCloud Photos.

What is Immich?

Immich is a self-hosted photo and video library that people run in place of Google Photos or iCloud Photos. Its apps for iPhone and Android back up the camera roll in the background, and the web page and the apps show everything on one timeline, with albums, favourites, an archive, a map of where each photo was taken and memories of the same day in earlier years.

Machine learning that runs within Immich, with no cloud service, finds the people in your photos and groups their faces, reads text in images, and powers smart search, which finds a photo from words such as “dog on a beach”. Immich keeps RAW files, Live Photos and motion photos, shares albums or links with anyone, and lets a partner see your library. Each person on the server has an account of their own.

This template runs Immich’s server, its machine learning, Valkey and its PostgreSQL with the vector extensions it needs, its library in a volume. The administrator is made from the email and password you give before Immich answers anyone, and shared links get the project’s address. Immich asks for 6 GB of memory and 2 cores at least, so it fits Grow; your photos take the disk, so watch its size as the library grows.

Hosting Immich on Pethost

  1. You sign in to Pethost with a passkey, GitHub or Google. No machine yet? You pick a plan, and the link waits until it is ready.
  2. New project opens at Immich. You fill in what it asks for and click Create project: Email, Admin password.
  3. In a minute or two Immich is live at its own address with HTTPS. Its secrets are made at random, and its data is in the nightly backup.
  4. It is a project like any other: you or your agent can change it, roll it back or restore it.

Questions about Immich

Is Immich free?

Yes. Immich is open source under the AGPL-3.0 license, and its makers say all its features are free forever, with nothing behind a paywall. You can buy it for yourself or for a server to support the project, which unlocks nothing more.

How do I back up my phone’s photos to Immich?

Install the Immich app from the App Store or Google Play, give your Immich’s address as its Server Endpoint URL, sign in, and turn on Backup for the albums you choose. It uploads new photos and videos in the background, and skips what is already there.

Does Immich recognise faces?

Yes. Its machine learning, which runs within Immich and sends your photos nowhere, detects faces in each photo and groups them into People, which you name. It also reads text in images, and its smart search finds photos by what is in them.

How much memory and disk does Immich need?

Immich asks for 6 GB of memory and 2 cores at least, and here takes about 1.5 GB at rest, about 2.5 GB while machine learning works through new photos, and more at the first upload, while it downloads its models. The disk holds your originals plus thumbnails and transcoded videos, so plan for more than the library’s own size.

Is Immich safe for my only copy of my photos?

Immich itself says to keep a 3-2-1 backup of photos you care about: three copies, on two kinds of storage, one of them elsewhere. Pethost backs up the project’s volumes every night, and Immich also dumps its database by itself; keep the originals somewhere else too.

How much memory and disk does Immich need?

Immich takes about 1.5 GB of memory at rest with its database and machine learning, about 2.5 GB while it works through new photos, and more at the first upload, while it downloads its models; the disk is your library plus its thumbnails and videos. On Pethost the Grow plan is enough for it, with room for other apps beside it.

Immich Docker Compose file

The template is this compose.yaml and the variables written into the project’s .env as it is created. Pethost runs the file as it is written.

Containers: immich-server (ghcr.io/immich-app/immich-server:v3), immich-machine-learning (ghcr.io/immich-app/immich-machine-learning:v3), redis (valkey/valkey:9), database (ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0).

Volumes, in the nightly backup: library, db, model-cache.

services:
  immich-server:
    image: ghcr.io/immich-app/immich-server:v3
    restart: unless-stopped
    environment:
      DB_HOSTNAME: database
      DB_USERNAME: postgres
      DB_PASSWORD: ${DB_PASSWORD}
      DB_DATABASE_NAME: immich
      REDIS_HOSTNAME: redis
      TZ: ${TZ:-Etc/UTC}
      URL: ${URL}
      ADMIN_EMAIL: ${ADMIN_EMAIL}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
    # A new Immich makes whoever opens it first its administrator. So its first start runs it on
    # port 2284, which nobody reaches, signs the administrator up there with the email and password
    # given, and sets the address shared links use; only then does Immich answer on 2283.
    command:
      - |
        set -e
        if [ ! -f /data/.pethost-admin ]; then
          # In a process group of its own, so that its workers stop with it.
          IMMICH_PORT=2284 setsid start.sh &
          setup=$$!
          SETUP_PID=$$setup node -e '
            const base = "http://127.0.0.1:2284/api";
            const { URL: url, ADMIN_EMAIL: email, ADMIN_PASSWORD: password } = process.env;
            const json = { "content-type": "application/json" };
            const call = async (path, options) => {
              const response = await fetch(base + path, options);
              if (!response.ok) throw new Error(path + ": " + response.status + " " + (await response.text()));
              return response.json();
            };
            const setUp = async () => {
              const { isInitialized } = await call("/server/config");
              if (isInitialized) return;
              const name = email.split("@")[0];
              await call("/auth/admin-sign-up", { method: "POST", headers: json, body: JSON.stringify({ email, password, name }) });
              const { accessToken } = await call("/auth/login", { method: "POST", headers: json, body: JSON.stringify({ email, password }) });
              const headers = { ...json, authorization: "Bearer " + accessToken };
              const config = await call("/system-config", { headers });
              config.server.externalDomain = url;
              await call("/system-config", { method: "PUT", headers, body: JSON.stringify(config) });
            };
            (async () => {
              let failure;
              for (let i = 0; i < 300; i++) {
                // Immich stops if it cannot start: the container starts again and so does this.
                try {
                  process.kill(Number(process.env.SETUP_PID), 0);
                } catch {
                  process.exit(1);
                }
                try {
                  return await setUp();
                } catch (error) {
                  failure = error;
                  await new Promise((done) => setTimeout(done, 1000));
                }
              }
              console.error(failure);
              process.exit(1);
            })();
          '
          kill -- -$$setup
          wait $$setup || true
          while kill -0 -- -$$setup 2>/dev/null; do sleep 1; done
          touch /data/.pethost-admin
        fi
        unset ADMIN_PASSWORD
        exec start.sh
    volumes:
      - library:/data
    depends_on:
      redis:
        condition: service_started
      database:
        condition: service_healthy
    # Its first start makes the database and the administrator before it answers on 2283.
    healthcheck:
      test: ["CMD-SHELL", "immich-healthcheck"]
      interval: 30s
      start_period: 10m
      start_interval: 5s

  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:v3
    restart: unless-stopped
    volumes:
      - model-cache:/cache

  redis:
    image: valkey/valkey:9
    restart: unless-stopped
    healthcheck:
      test: redis-cli ping | grep -q PONG || exit 1

  database:
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: immich
      POSTGRES_INITDB_ARGS: --data-checksums
    shm_size: 128mb
    volumes:
      - db:/var/lib/postgresql/data

volumes:
  library:
  db:
  model-cache:

More in Media