部署
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 构…
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 即可使用)。
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 自我验证(见引言)。等价的手动检查:
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)。
本地开发
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)。
生产备份
- 创建 destination(
POST /api/backup-destinations)。 - 创建 job(
POST /api/backup-jobs),带 cronfrequency/time+retentionDays。 - 用
POST /backup-destinations/:id/test和手动 runPOST /backup-jobs/:id/run验证。 - 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 保护网。
交叉链接
- 完整 env:03 - Configuration
- 开发:10 - Development
- Deploy 故障排查:12 - Troubleshooting