К документации
Руководство 09

Deployment

Simmetric Chat - air-gap capable и local-first. Пять целей развёртывания: Docker Compose multi-container (рекомендуется для продакшена), единый all-in-one container (air-gapped окружения), управляемый стек Coolify, dev override (hot-reload на Docker) и d…

4 мин чтения

Simmetric Chat - air-gap capable и local-first. Пять целей развёртывания: Docker Compose multi-container (рекомендуется для продакшена), единый all-in-one container (air-gapped окружения), управляемый стек Coolify, dev override (hot-reload на Docker) и desktop Tauri. Все артефакты живут в docker/; desktop shell в src-tauri/. Опубликованные на GHCR образы только amd64 (linux/amd64 - эмулированные через QEMU arm64-сборки зависали; на arm64 делается локальный self-build с нативного хоста). Все контейнеры используют wget healthcheck (--spider): server GET /api/health, collector GET /api/health на 3210, widget GET /health на 3211, Postgres pg_isready, Redis redis-cli ping.

Предварительные требования

  • Node.js ≥ 24, pnpm 11.24.0, PostgreSQL 16.
  • Для локального embedding: ~2 GB RAM на all-MiniLM-L6-v2 (Xenova, CPU).
  • Для локального LLM: Ollama с предварительно загруженной моделью(ями) (gemma4:latest по умолчанию).
  • Для vision OCR: Ollama с vision-моделью (glm-ocr:latest по умолчанию).

Docker Compose (продакшен)

Продакшен-compose (docker/docker-compose.yml) определяет 6 активных сервисов: frontend (nginx, :80/:443), server (:3000), collector (:3210), widget (:3211), postgres (16, образ pgvector/pgvector:pg16), redis (7-alpine, опциональный кэш масштабирования). ollama и qdrant отключены по умолчанию (закомментированные блоки: раскомментируй + задай OLLAMA_BASE_URL / VECTOR_DB_PROVIDER=qdrant для их использования).

bash
cp .env.example .env
# Отредактируй .env: JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, DATABASE_URL, LICENSE_KEY (если Enterprise)
docker compose -f docker/docker-compose.yml up -d

Критичные продакшен-env (см. 03 - Configuration):

  • JWT_SECRET - обязателен, криптографический материал (выводит ENCRYPTION_KEY при unset).
  • COLLECTOR_SECRET - обязателен, должен совпадать между server и collector.
  • WIDGET_API_KEY - обязателен для виджета, должен совпадать между widget и server.
  • DATABASE_URL - PostgreSQL 16.
  • LICENSE_KEY + LICENSE_SECRET - только Enterprise.
  • ENCRYPTION_KEY (base64 32 байта) - рекомендован для постоянного data-at-rest за пределами ротаций JWT_SECRET.
Ротация ключей: задай LEGACY_PREVIOUS_ENCRYPTION_KEYS (CSV base64 32-byte) для сохранения расшифровываемости старых бэкапов/данных во время ротации. Автоматического job re-encryption не существует: ручная процедура decrypt-old → re-encrypt-new.

Авто-provisioning ключей (entrypoint server)

Продакшен entrypoint (docker/entrypoint-server.sh) при каждом boot выполняет: provisioning ENCRYPTION_KEY и API_KEY_HMAC_SECRET с приоритетом значение оператора > значение, сохранённое в volume server-storage > сгенерировано один раз и сохранено (никогда не перегенерируется на существующем материале); затем prisma migrate deploy + seed и запуск сервера. Dev-конфигурации (docker-compose.dev.yml) пропускают provisioning и сохраняют scrypt fallback.

Volumes и персистентность

  • PostgreSQL: выделенный volume (pgdata).
  • LanceDB: ${STORAGE_PATH}/vectors/lancedb (collector).
  • File storage: ${STORAGE_PATH} (collector, документы/аватары/загрузки).
  • Backup: назначения, настроенные per-job (disk/S3/Dropbox/...).

Health check после deploy

Контейнеры само-верифицируются через wget (см. интро). Эквивалентные ручные проверки:

bash
curl http://localhost:3000/api/health
# { "status": "ok", "checks": { "database": "ok", "collector": "ok", "disk": "ok" } }
curl http://localhost:3210/api/health   # collector
curl http://localhost:3211/health        # widget

Единый all-in-one container

docker/Dockerfile собирает единый образ, управляемый supervisord, который запускает PostgreSQL 16 (pgvector скомпилирован offline для air-gap), server, collector и widget. Экспонирует 3000/3210/3211, использует DATABASE_URL на localhost, healthcheck на /api/health. Рассчитан на air-gapped/offline/demo окружения: env по умолчанию - placeholder'ы, переопредели их через .env или -e.

Coolify

docker/docker-compose.coolify.yml + docker/nginx.coolify.conf автоматизируют сборку образов, внутренний networking, volumes, healthchecks и mount enterprise-плагина (абсолютный host-путь ${ENTERPRISE_PLUGIN_PATH:-/opt/simmetric-enterprise}). Требуемые секреты используют синтаксис :? (Coolify отказывает в deploy при их отсутствии): JWT_SECRET, COLLECTOR_SECRET, WIDGET_API_KEY, POSTGRES_PASSWORD, APP_URL, ALLOWED_ORIGINS. Отличия от базового compose: нет container_name, нет host-порта у приложений (ingress доверен Traefik, HTTP-only - TLS терминируется Traefik), ENCRYPTION_KEY/API_KEY_HMAC_SECRET не интерполируются (авто-provisioning в volume).

Локальная разработка

bash
pnpm install
pnpm db:generate      # Prisma client (+ fix symlink Prisma/pnpm)
pnpm --filter server db:migrate   # применяет миграции
pnpm dev              # turborepo: server + collector + widget + frontend параллельно

Детали в 10 - Development.

Dev override (Docker)

docker/docker-compose.dev.yml монтирует исходники на продакшен-compose для hot-reload (NODE_ENV=development). docker-compose.infra.yml запускает только Postgres/Qdrant, приложение работает на хосте.

Desktop (Tauri)

src-tauri/ - desktop-приложение, оборачивающее фронтенд. Сборка через pnpm tauri build. Настрой src-tauri/tauri.conf.json для удалённого server URL или локального bundle.

Reverse proxy (Nginx)

Ключевые моменты:

  • SSE: отключить buffering (proxy_buffering off;, X-Accel-Buffering: no уже отправляется server/widget).
  • Timeout: медленные локальные LLM → высокий proxy_read_timeout (≥ 600s, дефолтный agent watchdog - 10 min).
  • Upload: client_max_body_size ≥ 100 MB (лимит multer документов).
  • CORS: allowlist управляется на стороне приложения (ALLOWED_ORIGINS); у виджета динамический per-origin CORS (allowedOrigins).

Backup в продакшене

  1. Создай назначение (POST /api/backup-destinations).
  2. Создай job (POST /api/backup-jobs) с cron frequency/time + retentionDays.
  3. Проверь через POST /backup-destinations/:id/test и ручной run POST /backup-jobs/:id/run.
  4. Restore: POST /api/backups/restore/:logId (dry-run рекомендуется заранее: /restore/:logId/dry-run). Атомарность через psql --single-transaction.

См. 05 - Feature Guide § Backup.

Продакшен-безопасность

  • Смени bootstrap-пароль админа; задай ALLOW_REGISTRATION=false в prod (самостоятельная регистрация закрыта).
  • DLP_ENABLED по умолчанию "true" в v0.22 - redaction input/output чата по 10 встроенным паттернам (см. 05 - Feature Guide); используй DLP_BYPASS_ROLES для освобождения выбранных ролей.
  • Включи audit_log_immutable (Enterprise) для append-only аудита.
  • HTTPS терминируется на reverse proxy; header'ы helmet активны на server (ослаблены на widget для embedding).
  • Rate limit уже настроены (см. 04 - API Reference § Rate limit): лимит чата теперь управляется budget tracker агента (per-user конкурентность, wallclock, token) плюс глобальная защитная сеть 200 req/min.