Александр Есин / блог

Immich - альтернатива Google Photos

2 октября 2026

Фото на телефоне заканчиваются одинаково: память кончается, облако просит подписку, а «просто посмотреть семейный архив» превращается в передачу личных данных третьей стороне. Immich — self-hosted система хранения и каталогизации фото и видео. Ваше оборудование, ваш сервер, ваши правила. По ощущению — та же облачная галерея, только поиск по лицам и сценам считается дома, а не на чужой машине.

Ветка на момент написания — 3.x. Ставил 3.2.4 поверх 3.1.

Как это устроено

Immich — не один контейнер, а четыре. Лучше знать это до первой поломки:

  • immich-server — веб-интерфейс, API, вся логика;
  • immich-machine-learning — нейросети: распознавание лиц, семантический поиск, текст на снимках;
  • database — PostgreSQL с расширением под векторный поиск;
  • redis — очередь задач. Миниатюры, транскодинг видео и скан библиотек уходят в очередь и не считаются в момент вашего запроса.

Порт по умолчанию — 2283. Состояние живёт в двух местах: каталог хранилища (library, upload, thumbs, encoded-video, backups, profile) и каталог данных базы. Удалить одно без другого — получить базу без файлов или файлы без базы.

Установка

Каталог проекта и два файла. Все пути в примерах условные — подставьте свои.

.env:

UPLOAD_LOCATION=/srv/immich/storage
DB_DATA_LOCATION=/srv/immich/db
DB_HOSTNAME=database
DB_PORT=5432
DB_USERNAME=immich
DB_DATABASE_NAME=immich
DB_PASSWORD=***
REDIS_HOSTNAME=redis
REDIS_PORT=6379
TZ=Europe/Moscow

Внимание: этот файл хранит пароль базы открытым текстом. Доступ к каталогу проекта ограничьте, в систему контроля версий он попадать не должен.

docker-compose.yml — сокращённо, показаны сервер и машинное обучение с монтированиями (полный шаблон берётся из официальной документации):

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    ports:
      - "2283:2283"
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - /srv/photos/existing-archive:/mnt/archive:ro
    env_file: .env
    depends_on:
      database:
        condition: service_healthy
      redis:
        condition: service_healthy

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /srv/photos/existing-archive:/mnt/archive:ro
    env_file: .env

  redis:
    container_name: immich_redis
    image: docker.io/redis:6.2-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10
    restart: always

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
    environment:
      POSTGRES_PASSWORD: ***
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    env_file: .env
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 30
    restart: always

Обратите внимание на :ro во втором монтировании — об этом ниже.

Запуск:

docker compose pull
docker compose up -d

Первый заход на http://<адрес-сервера>:2283 — мастер попросит создать аккаунт администратора.

Форма создания администратора

Дальше в настройках: включить машинное обучение, при необходимости вход через OAuth (OIDC), SMTP для уведомлений, квоты пользователям.

Перенос уже существующего архива

Главный вопрос при переезде: фото уже лежат папками, десятки и сотни гигабайт. «Загрузить через веб» — плохой путь: Immich копирует всё в своё хранилище, и размер удваивается.

Правильный механизм — внешняя библиотека (external library). Указываете серверу на готовый каталог, он сканирует его и строит хронологию, не дублируя файлы. Монтировать такой каталог советую только на чтение (:ro): тогда сервер физически не сможет удалить или испортить ваш исходный архив.

Грабли, на которые сам успел наступить:

1. Скан не мгновенный. Внешние библиотеки опрашиваются по расписанию — по умолчанию раз в сутки ночью. Положили файлы — и до ночи тишина. Ждать незачем: задачу полного скана можно поставить в очередь вручную, сервер подхватит за минуты. Имя задачи — LibraryScanQueueAll, очередь library, лежит в Redis (BullMQ) под префиксом immich_bull. Проще всего выполнить это из контейнера сервера: там уже есть нужная библиотека и параметры подключения в переменных окружения.

2. Права. Файлы, которые вы кладёте в библиотеку, должен читать пользователь процесса внутри контейнера. Если с хоста файл принадлежит вам и закрыт 600/700, сервер пройдёт мимо молча — без ошибки в логах. Проверяется изнутри контейнера одной командой: ls и head -c 3 на любой jpg — должно быть ff d8 ff. Не читается — чините владельца и права, а не Immich.

3. Как понять, что файл принят. Не смотрите только на число записей в базе. Смотрите, что у объекта есть распарсенный EXIF и что в каталоге миниатюр появились реальные файлы. EXIF пустой — значит сервер содержимое не прочитал (права, битый файл, недоступный путь), а не «всё в порядке, просто нет превью».

Что умеет

  • Хронология по дате съёмки, а не по дате загрузки. Избранное, архив, корзина с отложенным удалением.
  • Люди — распознавание лиц и группировка в персон. Ошибки правятся вручную: подтвердить, разделить, «не этот человек». Первый прогон на большом архиве считается долго, дальше не пересчитывается каждый раз.
  • Умный поиск — семантический, по описанию сцены: «кошка на подоконнике», «ночной город», «документ на столе». Отдельно полнотекстовый поиск по распознанному тексту: находятся скриншоты, документы, чеки.

Фильтры поиска

  • Карта — по координатам из EXIF. Геокодирование названий работает офлайн-базой, наружу не стучится.
  • Альбомы с совместным доступом, ссылки на просмотр без аккаунта — с сроком жизни и паролем, воспоминания «три года назад».
  • Теги — иерархия через слэш (Работа/Проекты), поиск и фильтрация по ним.

Теги

  • Несколько пользователей и квоты. У каждого своя хронология. Режим «партнёр» открывает другому человеку просмотр вашей ленты — не весь аккаунт, а именно просмотр. Ровно тот сценарий, ради которого делают семейный архив: два аккаунта, две отдельные библиотеки, ничего общего насильно.
  • Дубликаты по хэшу содержимого; stack для серий и пар raw+jpg; видео — транскодинг по требованию и стриминг; конвертация RAW в JPEG на сервере.
  • API, ключи API, CLI — автоматизация загрузки и выгрузки.

Полезная мелочь: в базе Immich хранит собственный контрольный хэш файла, и это не md5 и не sha1 содержимого. Сверять копию с оригиналом надо через md5sum по файлам, а не сравнением с полем checksum.

Мобильные приложения

Официальные клиенты — Android и iOS. Подключаются адресом сервера: значит наружу нужен доступ (публичный адрес, туннель или VPN), и HTTPS желателен — иначе приложение гоняет фото открытым текстом.

Экран подключения к серверу

Главная функция — фоновое резервное копирование. Настраивается: какие папки, по какой сети (Wi-Fi или мобильные данные), только на зарядке или всегда, включать ли видео.

Выбор альбомов для резервного копирования

Есть опция «удалить с устройства после загрузки» — включайте её, только когда точно уверены в бэкапе: приложение честно удалит исходники с телефона.

Приятное: дубли не грузятся дважды, а на втором телефоне достаточно указать тот же сервер и аккаунт — вся хронология уже на месте. Совместный доступ в приложении выглядит так же, как в вебе.

Совместный альбом в мобильном приложении

После обновления сервера проверяйте клиент: если приложение ругается на версию, обновляйте сервер и телефон синхронно.

Эксплуатация

Обновление. Проверено на переходе 3.1 → 3.2: миграции базы отработали на старте контейнера, руками чинить не пришлось. Порядок, который считаю правильным:

  1. Прочитать release notes — искать Breaking Changes и требование к минимальной версии клиента.
  2. Сделать дамп базы и обязательно проверить, что он читается, а не просто весит больше нуля:
docker exec -t immich_postgres pg_dump -U immich -d immich --format=custom -f /tmp/immich.dump
pg_restore --list /tmp/immich.dump | tail -5
  1. Обновить образы и пересоздать контейнеры: docker compose pull && docker compose up -d.
  2. Проверить: контейнеры healthy, веб отвечает, число объектов в базе совпадает с ожиданием, ошибок в docker logs нет.

Даже если база страхуется регулярным бэкапом, дамп перед миграцией делаю всегда: миграция необратима, а дамп стоит секунды.

Очередь задач в админ-панели

Резервные копии. Нужны оба куска: файлы хранилища и данные базы. Только файлы — потеряете хронологию, лица, альбомы. Только база — потеряете сами фотографии.

Свободное место. Если файловая система с копированием при записи и снимками (btrfs, ZFS), удаление не вернёт место мгновенно: снимки держат блоки до своей ротации. Картина «удалил 20 ГБ, а df не изменился» — чаще всего именно это, а не «место не освободилось».

Итог

Self-hosted фотоархив с лицами, умным поиском, картой, семейными аккаунтами и мобильным автосканом — без подписки и без чужого сервера, где лежат ваши фотографии. Цена: четыре контейнера, обновления синхронно с мобильным клиентом и одна привычка — дамп базы перед каждым апдейтом.

Иллюстрации — скриншоты интерфейса из официальной документации Immich (docs.immich.app). На одном скриншоте адрес сервера закрыт заглушкой.