返回文档
指南 09

部署

Simmetric Chat 具备 air-gap 能力且 local-first。五种部署目标:Docker Compose 多容器(生产推荐)、单容器 all-in-one(air-gapped 环境)、托管 Coolify 栈、dev override(Docker 上的 hot-reload)以及 Tauri 桌面端。所有产物位于 docker/;桌面 shell 位于 src-tauri/。GHCR 上发布的镜像仅 amd64(linux/amd64 - 通过 QEMU 模拟的 arm64 构…

3 分钟阅读

Simmetric Chat 具备 air-gap 能力且 local-first。五种部署目标:Docker Compose 多容器(生产推荐)、单容器 all-in-one(air-gapped 环境)、托管 Coolify 栈、dev override(Docker 上的 hot-reload)以及 Tauri 桌面端。所有产物位于 docker/;桌面 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:每个 all-MiniLM-L6-v2 约 2 GB RAM(Xenova,CPU)。
  • 本地 LLM:Ollama 并预拉取模型(默认 gemma4:latest)。
  • Vision OCR:Ollama 并带 vision 模型(默认 glm-ocr:latest)。

Docker Compose(生产)

生产 compose(docker/docker-compose.yml)定义 6 个活跃 service:frontend(nginx,:80/:443)、server(:3000)、collector(:3210)、widget(:3211)、postgres(16,镜像 pgvector/pgvector:pg16)、redis(7-alpine,可选的 scaling cache)。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 - 必需,加密材料(unset 时派生 ENCRYPTION_KEY)。
  • COLLECTOR_SECRET - 必需,必须在 server 和 collector 之间一致。
  • WIDGET_API_KEY - widget 必需,必须在 widget 和 server 之间一致。
  • DATABASE_URL - PostgreSQL 16。
  • LICENSE_KEY + LICENSE_SECRET - 仅 Enterprise。
  • ENCRYPTION_KEY(base64 32 字节)- 推荐,用于在 JWT_SECRET 轮换之外持久化 data-at-rest。
密钥轮换:设置 LEGACY_PREVIOUS_ENCRYPTION_KEYS(CSV base64 32-byte)以在轮换期间保持旧备份/数据的可解密性。不存在自动 re-encryption job:手动过程 decrypt-old → re-encrypt-new。

密钥自动 provisioning(server entrypoint)

生产 entrypoint(docker/entrypoint-server.sh)在每次 boot 时执行:ENCRYPTION_KEY 和 API_KEY_HMAC_SECRET 的 provisioning,优先级为 operator 提供的值 > 持久化在 server-storage volume 中的值 > 生成一次并持久化(绝不在已有材料上重新生成);然后执行 prisma migrate deploy + seed 以及 server 启动。dev 配置(docker-compose.dev.yml)跳过 provisioning 并保留 scrypt fallback。

卷与持久化

  • PostgreSQL:专用卷(pgdata)。
  • LanceDB:${STORAGE_PATH}/vectors/lancedb(collector)。
  • File storage:${STORAGE_PATH}(collector,文档/头像/上传)。
  • Backup:按 job 配置的目标(disk/S3/Dropbox/...)。

部署后 health check

容器通过 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

docker/Dockerfile 构建一个由 supervisord 监督的单一镜像,运行 PostgreSQL 16(pgvector 为 air-gap 离线编译)、server、collector 和 widget。暴露 3000/3210/3211,使用 localhost 上的 DATABASE_URL,healthcheck 于 /api/health。面向 air-gapped/离线/demo 环境:默认 env 是 placeholder - 通过 .env 或 -e 覆盖。

Coolify

docker/docker-compose.coolify.yml + docker/nginx.coolify.conf 自动化镜像构建、内部网络、卷、healthcheck 以及 enterprise 插件的 mount(绝对主机路径 ${ENTERPRISE_PLUGIN_PATH:-/opt/simmetric-enterprise})。所需密钥使用 :? 语法(缺失时 Coolify 拒绝部署):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)。

本地开发

bash
pnpm install
pnpm db:generate      # Prisma client(+ 修复 Prisma/pnpm symlink)
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,应用在主机上运行。

桌面端(Tauri)

src-tauri/ - 将前端包装为桌面应用的 app。用 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(document multer 限制)。
  • CORS:allowlist 在 app 侧管理(ALLOWED_ORIGINS);widget 有按 origin 的动态 CORS(allowedOrigins)。

生产备份

  1. 创建 destination(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 admin 密码;在生产中设置 ALLOW_REGISTRATION=false(关闭自助注册)。
  • DLP_ENABLED 在 v0.22 默认 "true" - 对 10 个内置 pattern 进行 chat 输入/输出 redaction(见 05 - Feature Guide);用 DLP_BYPASS_ROLES 豁免选定角色。
  • 启用 audit_log_immutable(Enterprise)以获得 append-only audit。
  • HTTPS 由 reverse proxy 终止;server 上 helmet header 活跃(widget 为 embedding 放宽)。
  • Rate limit 已配置(见 04 - API Reference § Rate limit):聊天限制现由 agent 的 budget tracker 管理(per-user 并发、wallclock、token)加上全局 200 req/min 保护网。

交叉链接