Гайды

Запуск Persona в контейнере: возможности и честные ограничения

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

Начнём с ограничения, а не с инструкции, потому что для части читателей на нём всё и закончится — и лучше узнать об этом сейчас, а не после часа настройки.

В контейнере не работает захват экрана. У контейнера нет графической сессии: нет подключённого дисплея, нет сеанса пользователя, нет окон. Библиотека снятия экрана обращается к оконной системе, которой там просто нет. Это не настройка и не подбор флагов — это структурное свойство контейнеров, разобранное в статье про Docker для ИИ-приложений.

Поэтому контейнерный режим Persona — это веб-интерфейс и приём данных извне. Захват при этом остаётся на вашем компьютере и отправляет данные на контейнер по сети.

#Что понадобится

  • Docker и плагин Compose на целевой машине.
  • Каталог на хосте под данные — база, миниатюры, входящие, копии.
  • Ключ облачного провайдера модели либо доступный по сети Ollama. Без подключённой модели чат не отвечает вовсе: Persona не проксирует запросы через себя, языковую модель подключает сам пользователь.
  • Понимание, что подписанного установщика у Persona нет ни в каком виде — на Windows установка идёт из командной строки, и это отдельный пошаговый гайд.

#Что работает и что нет

Функция В контейнере
Веб-интерфейс, чат, память ✓
Приём данных от внешнего агента ✓
Поиск, заметки, граф памяти ✓ по тому, что пришло извне
Распознавание текста присланных картинок ✓ если в образе есть движок
Захват экрана ✗ нет графической сессии
Запись звука и микрофон ✗ нет звуковых устройств
Активное окно, горячие клавиши ✗
Фоновые воркеры захвата и очистки выключены намеренно
Инференс на видеокарте в том же контейнере ✗ отдельный контейнер и проброс

Строку про фоновые воркеры стоит пояснить. Образ запускается в облегчённом режиме (PERSONA_LEAN_MODE=1): около сорока фоновых воркеров отключены. Это не урезание ради простоты, а следствие первой строки — в headless-режиме им нечего обрабатывать, а работающие вхолостую воркеры молотили бы журнал базы без пользы.

#Шаг 1. Файл сборки

Образ собирается из репозитория. Ключевые решения в нём стоит понимать, а не копировать вслепую:

FROM python:3.12-slim

# Движок распознавания текста — ОПЦИОНАЛЕН. Нужен только если вы хотите
# распознавать присланные извне картинки. Без него распознавание тихо отключается.
RUN apt-get update \
    && apt-get install -y --no-install-recommends tesseract-ocr tesseract-ocr-rus \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Сначала метаданные пакета — слой с зависимостями кешируется
# и не пересобирается при каждой правке исходников.
COPY pyproject.toml README.md ./
COPY app ./app
RUN pip install --no-cache-dir -e .

# /data — единый корень БД, миниатюр, входящих и копий.
ENV PERSONA_DATA_DIR=/data \
    PERSONA_LEAN_MODE=1 \
    PERSONA_HOST=0.0.0.0 \
    PERSONA_PORT=8000 \
    PYTHONUNBUFFERED=1

EXPOSE 8000
VOLUME ["/data"]

CMD ["uvicorn", "app.web.main:create_app", "--factory", "--host", "0.0.0.0", "--port", "8000"]

PYTHONUNBUFFERED=1 — не косметика: без него вывод приложения копится в буфере, и docker logs показывает пустоту ровно в тот момент, когда логи нужнее всего.

#Шаг 2. Compose

services:
  persona:
    build: .
    image: persona:latest
    container_name: persona
    ports:
      # ВАЖНО: 8000:8000 публикует порт на ВСЕ интерфейсы хоста.
      # Для машины в интернете правильнее 127.0.0.1:8000:8000 и прокси сверху.
      - "127.0.0.1:8000:8000"
    volumes:
      # Все данные — на хосте. Без этого тома база живёт внутри
      # контейнера и исчезает при первом же пересоздании.
      - ./data:/data
    environment:
      PERSONA_DATA_DIR: /data
      PERSONA_LEAN_MODE: "1"
      PERSONA_HOST: 0.0.0.0
      PERSONA_PORT: "8000"
    restart: unless-stopped
    networks:
      - persona-net

  # Опционально: локальная модель рядом, в своём контейнере.
  # Тогда в окружении persona укажите:
  #   PERSONA_BYO_API_PROVIDER=ollama
  #   PERSONA_BYO_API_BASE=http://ollama:11434
  # ollama:
  #   image: ollama/ollama:latest
  #   ports: ["11434:11434"]
  #   volumes: [ollama-data:/root/.ollama]
  #   networks: [persona-net]

networks:
  persona-net:
    driver: bridge

Две строки, на которых ошибаются чаще всего.

Публикация порта. "8000:8000" открывает приложение на всех интерфейсах хоста. На домашней машине это допустимо, на арендованной — это публикация в интернет без прокси, без ограничения частоты запросов и без HTTPS. Привяжите порт к локальному адресу и поставьте перед ним обратный прокси.

Том с данными. Каталог ./data на хосте — это вся ваша база. Забыли том — данные живут внутри контейнера и умирают вместе с ним при первом docker compose down --volumes или пересоздании образа.

#Шаг 3. Запуск и подключение модели

docker compose up -d --build
docker compose logs -f persona     # смотреть первые 30 секунд, там миграции

При первом старте применяется схема базы. Это единственный запуск, который заметно дольше остальных; последующие должны выполнять ноль тел миграций.

Дальше — подключение модели, без которой чат молчит. Два варианта, и различие между ними принципиально:

  • Своя Ollama (в соседнем контейнере или на хосте) — переписка не уходит за пределы вашей сети;
  • Ключ облачного провайдера — текст ваших запросов уходит именно этому провайдеру, напрямую. Persona не проксирует их через себя.

И факт, который нужно знать заранее: ключ участника хранится в базе в открытом виде. Ключ владельца может лечь в зашифрованное хранилище, но при его недоступности — тоже в открытое. Файл базы из каталога ./data следует считать хранилищем ключей со всеми вытекающими: не класть в общее облако, не прикладывать к обращениям, шифровать при передаче.

#Проверка результата

# 1. контейнер жив и слушает
docker compose ps
curl -s http://127.0.0.1:8000/healthz          # {"status":"ok","version":"..."}

# 2. данные легли на ХОСТ, а не внутрь контейнера
ls -la ./data/                                  # persona.db должен быть здесь

# 3. данные переживают пересоздание — главная проверка
docker compose down && docker compose up -d
curl -s http://127.0.0.1:8000/healthz
ls -la ./data/persona.db

# 4. версия совпадает с той, что вы собрали
curl -s http://127.0.0.1:8000/healthz | grep -o '"version":"[^"]*"'

Критерий приёмки — пункт 3. Контейнер, из которого данные исчезают при пересоздании, не годится ни для чего, и узнавать об этом лучше на второй минуте, а не через месяц. Как встроить проверку версии в постоянное наблюдение — в статье про мониторинг личного сервиса.

#Что может пойти не так

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

Чат не отвечает. Модель не подключена. Участник без ключа и без доступной Ollama ответов не получает — это не ошибка, а устройство: модель приносит пользователь.

Данные исчезли после обновления образа. Не был примонтирован том. Восстанавливать неоткуда, если не было копии.

Не работает распознавание присланных картинок. В образе нет движка или он выключен переключателем. Проверьте наличие языковых пакетов: без нужного языка распознавание формально работает и выдаёт мусор.

Ошибки блокировки базы. Каталог с данными смонтирован с сетевого диска или из папки синхронизации. Механизм блокировок SQLite опирается на корректное поведение файловой системы, и сетевые протоколы эмулируют его по-разному. Том должен быть на локальном диске.

Занят порт 8000. Проверьте, не остался ли предыдущий контейнер или нативный запуск. Два процесса на одной базе — источник самых непонятных симптомов.

#Частые вопросы

#Можно ли включить захват экрана в контейнере хоть как-то?

На Linux — при пробросе сокета оконной системы и запуске от того же пользователя в той же графической сессии, то есть только на настольной машине и только для собственных задач. На сервере, на Windows и на macOS — нет. Если захват ваш основной сценарий, ставьте Persona нативно на свой компьютер.

#Можно ли держать захват на компьютере, а всё остальное в контейнере?

Да, и это основной рабочий сценарий контейнерного режима. Ваш компьютер снимает и отправляет, контейнер принимает, хранит, индексирует и отдаёт интерфейс. Требования к серверной части — в статье про выбор VPS под личный ИИ-сервис.

#Что происходит при нескольких пользователях на одном инстансе?

Участники получают чат, память, граф, голос, навыки и свой характер. Захват, таймлайн, поиск, заметки, напоминания, дашборды и брифинг доступны только владельцу инстанса — мульти-тенантный захват не сделан. В контейнере это ограничение не ощущается: захвата там нет ни у кого.

#Как обновлять контейнер, не теряя данные?

Данные на томе, обновляется образ: docker compose pull либо пересборка, затем up -d. Обязательное условие — копия базы перед обновлением, которое меняет схему. Обратных миграций нет, вернуться можно только из копии.

#Как перенести контейнерную установку на другую машину?

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

#Нужен ли контейнер, если я запускаю Persona только у себя?

Нет, и скорее вредно: нативный запуск на вашей машине даёт захват экрана, звук и активное окно, то есть ровно то, ради чего Persona и нужна. Контейнер уместен на второй машине — той, которая должна быть доступна всегда.

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт