MAATRIX / Блог / AFFiNE в Docker Compose: готовый файл

AFFiNE в Docker Compose: готовый файл

MAATRIX

Notion удобен, пока вы не упираетесь в облако: данные лежат на чужих серверах, платная подписка растёт с числом участников, а если сервис недоступен из России без VPN — рабочий процесс просто встаёт. AFFiNE закрывает тот же набор задач (документы, канбан-доски, простая база данных, whiteboard) как self-hosted альтернатива, которую вы поднимаете на своём сервере одной командой. Ниже — рабочий docker-compose.yml, разбор первого запуска с созданием администратора и настройка домена с HTTPS.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

AFFiNE: что это и чем отличается от Notion и Obsidian

AFFiNE — открытый проект (лицензия MIT), который с самого начала задумывался как «Notion + Miro в одном приложении»: один и тот же документ можно листать как обычный текстовый редактор с блоками (заголовки, списки, таблицы, код), а можно переключить в режим Edgeless — бесконечный холст, где те же блоки становятся карточками, которые двигают, соединяют стрелками и группируют, как в Miro или FigJam. Плюс к этому — встроенные базы данных с несколькими представлениями (таблица, канбан, галерея), близкие по духу к базам Notion.

Технически сервер AFFiNE — это Node.js-бэкенд с двумя внешними зависимостями: PostgreSQL (с расширением pgvector для векторного поиска, если включён AI-ассистент) и Redis для очередей и кэша. Клиенты — веб, десктоп (Windows/macOS/Linux) и мобильные приложения — синхронизируются с сервером через realtime-протокол на базе CRDT (Yjs), тот же класс технологий, что использует Google Docs для совместного редактирования без конфликтов.

AFFiNENotionObsidian (self-hosted sync)
Модельдокумент + бесконечный холст в одном блокедокумент + база данныхmarkdown-файлы + плагины
Совместная работаrealtime, CRDTrealtime, облакочерез сторонний sync-сервер
Хостингself-hosted или облако AFFiNEтолько облаколокально или self-hosted sync
AI-ассистентопционально, свой ключ (BYOK)встроен, платночерез плагины
Whiteboardвстроен (Edgeless)нетчерез плагины

Если нужна именно иерархическая база знаний с деревом заметок и клонированием, Trilium Notes организован строже. Если основная задача — канбан-доски и управление задачами команды без документов вокруг, проще смотреть в сторону Wekan или Focalboard. AFFiNE выигрывает там, где нужен именно гибрид: заметки, доски и произвольные визуальные схемы в одном рабочем пространстве, без переключения между разными сервисами.

Требования к серверу

Стек лёгкий по одиночным меркам, но постоянно работающий Node.js-процесс плюс PostgreSQL и Redis в сумме требуют больше памяти, чем однофайловые SQLite-приложения вроде Trilium.

СценарийvCPURAMДиск
личное использование, 1 пользователь22 ГБ20 ГБ
небольшая команда, 3-10 человек2-44 ГБ40 ГБ
команда с активным AI-ассистентом и большим объёмом вложений46-8 ГБот 80 ГБ

Цифры ориентировочные: сильнее всего на потребление влияет, включены ли AI-функции (векторный поиск в pgvector нагружает PostgreSQL) и объём файлов, которые команда вкладывает в документы — они растут в отдельном volume независимо от самой базы. На 2 ГБ RAM инстанс запускается и работает, но под несколькими одновременными редакторами лучше закладывать 4 ГБ с запасом.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Готовый docker-compose.yml

Официальный образ AFFiNE тянет актуальный docker-compose.yml прямо с GitHub Releases проекта — файл ниже собран на его основе с портом только на loopback (для работы через реверс-прокси, раздел ниже).

Структура каталогов на сервере:

/opt/affine/
├── data/
│   ├── postgres/     # данные PostgreSQL
│   └── storage/      # вложения, blob-хранилище документов
├── config/            # config.json сервера
└── docker-compose.yml

Создаём каталоги:

mkdir -p /opt/affine/data/postgres /opt/affine/data/storage /opt/affine/config
cd /opt/affine
nano docker-compose.yml

Содержимое файла:

name: affine
services:
  affine:
    image: ghcr.io/toeverything/affine:stable
    container_name: affine_server
    restart: unless-stopped
    ports:
      - "127.0.0.1:3010:3010"
    depends_on:
      redis:
        condition: service_healthy
      postgres:
        condition: service_healthy
      affine_migration:
        condition: service_completed_successfully
    volumes:
      - ./data/storage:/root/.affine/storage
      - ./config:/root/.affine/config
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://affine@postgres:5432/affine
      - AFFINE_INDEXER_ENABLED=false

  affine_migration:
    image: ghcr.io/toeverything/affine:stable
    container_name: affine_migration_job
    volumes:
      - ./data/storage:/root/.affine/storage
      - ./config:/root/.affine/config
    command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]
    environment:
      - REDIS_SERVER_HOST=redis
      - DATABASE_URL=postgresql://affine@postgres:5432/affine
      - AFFINE_INDEXER_ENABLED=false
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  redis:
    image: redis
    container_name: affine_redis
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  postgres:
    image: pgvector/pgvector:pg16
    container_name: affine_postgres
    restart: unless-stopped
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: affine
      POSTGRES_DB: affine
      POSTGRES_INITDB_ARGS: "--data-checksums"
      POSTGRES_HOST_AUTH_METHOD: trust
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "affine", "-d", "affine"]
      interval: 10s
      timeout: 5s
      retries: 5

Обратите внимание на POSTGRES_HOST_AUTH_METHOD: trust — PostgreSQL здесь работает без пароля. Это безопасно ровно потому, что порт базы не публикуется наружу и доступен только внутри приватной сети Compose. Если планируете подключать к этому PostgreSQL что-то ещё, задайте пароль явно через POSTGRES_PASSWORD и пропишите его в DATABASE_URL обоих сервисов — affine и affine_migration.

Образ pgvector/pgvector:pg16 — PostgreSQL 16 с расширением pgvector, которое AFFiNE использует для векторного поиска при включённом AI-ассистенте. Если AI не нужен, AFFINE_INDEXER_ENABLED=false отключает полнотекстовую индексацию и снижает нагрузку — включается обратно сменой значения на true и перезапуском сервиса.

Поднимаем стек:

docker compose up -d
docker compose logs -f affine

Контейнер affine_migration отрабатывает один раз при каждом запуске (применяет миграции схемы БД) и завершается со статусом «успешно» — это штатное поведение service_completed_successfully, а не сбой. Сам сервер affine стартует только после того, как миграция закончена и PostgreSQL с Redis прошли healthcheck.

Первый запуск: администратор и рабочее пространство

Открываем http://<IP-сервера>:3010/admin (именно /admin, а не корень — это важно при первом заходе). AFFiNE предложит создать первую учётную запись — она автоматически становится администратором всего инстанса, отдельного флага «сделать админом» нет, это привязано к самому факту первой регистрации через /admin.

После входа в панель администратора доступны:

  • Add User — добавление пользователей вручную по email. Публичной саморегистрации по умолчанию нет: доступ в инстанс получают только те, кого добавил администратор.
  • Settings — имя сервера, лимиты на размер файлов, включение AI-функций.
  • Workspaces — рабочие пространства на инстансе; по умолчанию одно, дополнительные заводятся под разные команды с изолированными данными.

Дальше заходим на http://<IP-сервера>:3010 под тем же логином — открывается обычный интерфейс. Переключение между режимом документа и Edgeless (холст) — кнопка в правом верхнем углу или Ctrl+Alt+E, весь контент общий для обоих режимов, просто отображается по-разному.

Desktop-клиент для Windows/macOS/Linux — с GitHub Releases проекта; при первом запуске в настройках аккаунта выбираем self-hosted и вводим адрес своего сервера вместо облака AFFiNE.

Домен, HTTPS и config.json

Для продакшена сервер должен знать свой внешний адрес — иначе ссылки на документы, приглашения и вебхуки AI-ассистента будут генерироваться с localhost. Настройка — через config/config.json:

{
  "server": {
    "name": "AFFiNE",
    "externalUrl": "https://affine.your-domain.example"
  }
}

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

docker compose restart affine

Закрываем сервер HTTPS через Caddy — порт 3010 в файле выше уже отдаётся только на 127.0.0.1, наружу пускаем через реверс-прокси:

# /etc/caddy/Caddyfile
affine.your-domain.example {
    reverse_proxy 127.0.0.1:3010
}

Если Caddy на сервере ещё не настроен, разбор установки с автоматическим Let's Encrypt есть в отдельной статье про Caddy с авто-SSL на VPS.

AI-ассистент (чат, помощь с текстом, генерация изображений в документе) в self-hosted версии работает по модели BYOK — «принеси свой ключ»: включается в config.json или через панель администратора, но фактические запросы к модели идут с вашим собственным API-ключом (OpenAI, Anthropic и другие поддерживаемые провайдеры). Без ключа фичи AI просто скрыты из интерфейса — базовый функционал документов и досок от этого никак не зависит и работает полностью офлайн от внешних AI-сервисов.

Бэкап и обновление

Состояние AFFiNE разложено по двум местам: содержимое документов и структура — в PostgreSQL, вложения (картинки, файлы) — в каталоге data/storage. Бэкапить нужно оба, и желательно консистентно друг с другом.

Дамп базы данных без остановки сервиса:

docker compose exec postgres pg_dump -U affine affine | gzip > /opt/affine/backups/affine-db-$(date +%F).sql.gz

Архив вложений:

tar czf /opt/affine/backups/affine-storage-$(date +%F).tar.gz -C /opt/affine data/storage

Оба шага стоит выполнять подряд, одной точкой во времени: если между дампом БД и архивом вложений пройдёт заметное время активной работы, теоретически возможна рассинхронизация — документ уже ссылается на вложение, которого ещё нет в архиве. Для регулярного автоматического бэкапа с ротацией и шифрованием удобнее не самописный cron-скрипт, а готовый пайплайн вроде BorgBackup в Docker Compose, настроенный на оба каталога сразу.

Обновление — стандартный pull + up, миграция схемы БД отрабатывает автоматически через контейнер affine_migration при следующем запуске:

docker compose pull
docker compose up -d

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Можно ли обойтись без PostgreSQL и Redis, оставить только SQLite?

Нет, это обязательные зависимости текущей архитектуры сервера. Нужен более лёгкий стек с однофайловой базой — смотрите Trilium Notes.

Как добавить нового пользователя в команду?

Через панель /admin → Add User, указав email. Публичной саморегистрации нет по умолчанию — доступ выдаёт администратор точечно.

Работает ли AFFiNE без интернета, полностью изолированно?

Документы, доски и совместное редактирование внутри локальной сети — да. Внешний доступ нужен только AI-ассистенту, если он включён: запросы идут к провайдеру модели по вашему ключу.

Что случится с данными при пересоздании контейнеров?

Ничего, пока не тронуты volumes data/postgres, data/storage и config — сами контейнеры состояние не хранят. docker compose down -v данные удаляет, обычный down/up -d — нет.

Чем AFFiNE отличается от связки Outline Wiki и Miro по отдельности?

Специализированные инструменты обычно сильнее в своей нише — Outline Wiki удобнее для чисто текстовой базы знаний. AFFiNE выигрывает тем, что документы и холст живут в одном приложении и одной базе.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →