Начнём с ограничения, а не с инструкции, потому что для части читателей на нём всё и закончится — и лучше узнать об этом сейчас, а не после часа настройки.
В контейнере не работает захват экрана. У контейнера нет графической сессии: нет подключённого дисплея, нет сеанса пользователя, нет окон. Библиотека снятия экрана обращается к оконной системе, которой там просто нет. Это не настройка и не подбор флагов — это структурное свойство контейнеров, разобранное в статье про 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 и нужна. Контейнер уместен на второй машине — той, которая должна быть доступна всегда.