OpenMAIC v1.2.1 Docker Compose 部署指南

Ubuntu 24.04 + Docker Compose 从零部署 OpenMAIC v1.2.1 实测指南:源码构建、PostgreSQL 16 编排、访问口令与单用户身份、健康检查与课程库写入验证、数据备份恢复、升级回滚与常见问题排障。

OpenMAICOpenMAICDocker ComposePostgreSQLNode.js2026/10/25

OpenMAIC v1.2.1 Docker Compose 部署指南

目标:在 Ubuntu 24.04 服务器上用 Docker Compose 从零部署 OpenMAIC v1.2.1(开源多智能体互动课堂),覆盖源码获取、应用与 PostgreSQL 16 双服务编排、访问口令配置、启动核验、健康检查、课程库写入验证、数据备份恢复、升级回滚与常见问题排障。文中全部命令与回显均在真实部署环境(Ubuntu 24.04.5、Docker 29.9.2、Docker Compose v5.6.1)实测,取材日期 2026-10-25。项目定位与选型对比见《OpenMAIC v1.2.1 介绍与选型指南》。

一、环境信息

本篇的实测环境与部署目标如下,读者操作时把路径、端口与口令换成自己的取值即可:

  • 操作系统:Ubuntu 24.04.5 LTS,x86_64
  • Docker 引擎:29.9.2(Compose v2 插件,实测 v5.6.1)
  • 应用版本:OpenMAIC v1.2.1(Git 标签日期 2026-10-18)
  • 运行时:应用发行内置 Node.js v22.23.4、Next.js 16.3.3
  • 数据库:PostgreSQL 16.15(官方编排使用 postgres:16)
  • 对外端口:8144(映射到应用内 3000)
  • 部署目录:/opt/openmaic,源码在 src/ 子目录,编排文件为 docker-compose.yml
  • 编排项目名:openmaic-121-stack(由编排文件 name: 字段固定)
  • 数据卷:openmaic-data(挂载到 /app/data)、openmaic-postgres(PostgreSQL 数据目录)

版本与宿主机能力取证:

docker --version && docker compose version

实测回显:

Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
git -C /opt/openmaic/src describe --tags

实测回显:

v1.2.1

二、部署形态说明

官方给出两种部署入口,选择哪条路线取决于你是否愿意自己维护数据库:

形态组成适用场景本文是否实测
Docker Compose(推荐)应用服务 + postgres:16 服务,编排自动注入 DATABASE_URL个人服务器、小团队、希望一条命令拉起是,全文以此为主线
源码运行 + 外部 PostgreSQLpnpm start 常驻进程 + 自建 PostgreSQL已有数据库、需要接入 systemd 或进程管理器否,仅给出等价步骤

无论哪种形态,都有三条硬约束:

  1. 必须常驻:课程生成由服务进程内的 worker 执行,Serverless 平台无法承载;进程退出后任务从数据库检查点续跑。
  2. 必须有 PostgreSQL:缺少 DATABASE_URL 时服务直接拒绝启动并退出(实测回显见第七章)。
  3. 必须配置模型:应用能启动、界面能打开,但没有模型配置时生成课程会返回 MISSING_MODEL(第四章给出实测回显)。

可选组件:MP4 导出依赖独立的渲染服务(render-service),官方以编排 profile video-export 提供。该服务 standard 档要求 8 GiB 内存,low-memory 档要求 4 GiB,并需要 NET_ADMIN 能力做网络隔离。普通课堂生成不需要它,本文不启用。

三、前置条件

3.1 宿主机资源

  • 磁盘:应用发行占用约 889 MB,加上源码、构建缓存与数据库,建议预留 10 GB 以上;构建阶段会产生较大的 node_modules 与 .next 中间产物。
  • 内存:建议至少 4 GiB 可用内存。官方 Dockerfile 把工作区包构建的 JS 堆限制为 1 GiB,但 Next.js 生产构建与 PostgreSQL 同时运行仍需余量。
  • CPU:2 核可跑,构建阶段会明显吃满。

3.2 端口检查

先确认 8144 未被占用:

ss -ltn | grep ':8144' || echo "8144 空闲"

部署前实测回显:

8144 空闲

部署成功后同一命令应能看到 LISTEN 行(见 4.5)。

3.3 基础工具

git、curl 与 Docker 引擎需可用;curl 用于后续全部验证。若服务器访问 GitHub 缓慢,可在克隆前配置代理或使用可达的镜像源,但不要修改仓库内的源码来绕过构建依赖。

四、部署步骤

4.1 获取源码

创建部署目录并检出 v1.2.1 标签(--depth 1 只取该标签的快照,减少下载量):

mkdir -p /opt/openmaic
cd /opt/openmaic
git clone --branch v1.2.1 --depth 1 https://github.com/THU-MAIC/OpenMAIC.git src

检出后核验版本,必须输出 v1.2.1:

git -C /opt/openmaic/src describe --tags

实测回显:

v1.2.1

4.2 编写编排文件

在 /opt/openmaic/docker-compose.yml 写入以下内容(与本文实测运行文件一致):

name: openmaic-121-stack

services:
  openmaic:
    build:
      context: ./src
      dockerfile: Dockerfile
    ports:
      - '8144:3000'
    env_file:
      - ./src/docker-compose.defaults.env
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - ACCESS_CODE=q1w2e3r4
      - OPENMAIC_PUBLISH_ADDRESS=0.0.0.0
      - DATABASE_URL=postgres://openmaic:q1w2e3r4@postgres:5432/openmaic
    volumes:
      - openmaic-data:/app/data
    restart: unless-stopped

  postgres:
    image: postgres:16
    environment:
      - POSTGRES_DB=openmaic
      - POSTGRES_USER=openmaic
      - POSTGRES_PASSWORD=q1w2e3r4
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U openmaic -d openmaic']
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 5s
    volumes:
      - openmaic-postgres:/var/lib/postgresql/data
    restart: unless-stopped

networks:
  default:
    ipam:
      config:
        - subnet: 10.89.70.0/24

volumes:
  openmaic-data:
  openmaic-postgres:

编排要点逐项说明:

  • build.context: ./src:应用没有官方预构建发行,必须从源码构建;构建上下文指向 4.1 检出的源码目录。
  • env_file:加载官方默认环境文件,其中包含单用户身份模式(OWNER_SINGLE_USER=true)等默认值。Compose 会先读该文件,再用 environment: 覆盖同名键。
  • DATABASE_URL:显式写成与 POSTGRES_PASSWORD 一致的值,避免默认文件里 ${PERSISTENCE_POSTGRES_PASSWORD:-openmaic-dev} 插值带来的歧义。数据库名、用户名、密码三处必须一致。
  • ACCESS_CODE:站点级访问口令。官方建议至少 16 位随机值;本篇为可复现实测使用示例口令 q1w2e3r4,启动日志会给出长度告警(见 4.6),生产环境务必换成长随机串。
  • OPENMAIC_PUBLISH_ADDRESS=0.0.0.0:官方编排默认只发布到 127.0.0.1;要让同网段访问必须显式改成 0.0.0.0,同时确保已设置访问口令。
  • 健康检查:数据库服务用 pg_isready 探活,应用通过 depends_on.condition: service_healthy 等数据库就绪后再启动,避免首次启动时的连接风暴。
  • 数据卷:openmaic-data 保存上传资料、解析结果、课堂媒体、用量记录与实例密钥;openmaic-postgres 保存全部课程与任务记录。
  • 重启策略:unless-stopped,宿主机重启后自动拉起。

4.3 构建应用

cd /opt/openmaic
docker compose build openmaic

首次构建需要下载 Node 22 基础层并安装全部依赖,耗时较长。构建日志节选(实测首尾):

#14 DONE 166.7s
#19 DONE 594.1s
#20 DONE 376.9s
#23 DONE 123.3s
#24 1.927 ▲ Next.js 16.3.3 (Turbopack)
#24 2.068   Creating an optimized production build ...

上述阶段依次覆盖依赖安装、工作区包编译与 Next.js 生产构建;本机从零构建的整体耗时在 20 分钟量级。构建完成后确认发行已生成:

docker images --filter reference='openmaic-121-stack-openmaic' --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.Size}}'

实测回显:

openmaic-121-stack-openmaic:latest 2c1ae369a4bf 889MB

⚠️ 构建失败排查:最常见原因是拉取基础层或 npm 依赖超时。先为 Docker 引擎配置代理/加速地址,再重试 docker compose build openmaic;构建缓存会保留已完成的层,不必从零开始。

4.4 启动服务

cd /opt/openmaic
docker compose up -d

实测回显:

 Container openmaic-121-stack-postgres-1 Healthy
 Container openmaic-121-stack-openmaic-1 Starting
 Container openmaic-121-stack-openmaic-1 Started

首次启动会先构建应用层,期间会先打印数据库健康等待,再启动应用;最终以 docker compose ps 的结果为准(见 4.5)。

4.5 核验运行状态

docker compose ps

实测回显:

NAME                            IMAGE                         COMMAND                  SERVICE    CREATED          STATUS                   PORTS
openmaic-121-stack-openmaic-1   openmaic-121-stack-openmaic   "docker-entrypoint.s…"   openmaic   8 seconds ago    Up 2 seconds             0.0.0.0:8144->3000/tcp, [::]:8144->3000/tcp
openmaic-121-stack-postgres-1   postgres:16                   "docker-entrypoint.s…"   postgres   8 seconds ago    Up 7 seconds (healthy)   5432/tcp

两个服务均为 Up、数据库为 healthy 即启动成功。再看端口监听:

ss -ltn | grep ':8144'

实测回显:

LISTEN 0      4096         0.0.0.0:8144       0.0.0.0:*
LISTEN 0      4096            [::]:8144          [::]:*

4.6 查看启动日志

docker compose logs openmaic --since 5m

实测回显(输出节选):

openmaic-1  | ▲ Next.js 16.3.3
openmaic-1  | - Local:         http://localhost:3000
openmaic-1  | - Network:       http://0.0.0.0:3000
openmaic-1  | ✓ Ready in 0ms
openmaic-1  | ✓ Running next.config took 0.9ms
openmaic-1  | [2026-10-25T07:53:21.705Z] [INFO] [GenerationRunner] generation runner 1:b56db764-fbe7-468c-84bf-756a8af92ec1 started (scan=1000ms, heartbeat=2000ms, leaseTtl=10000ms, maxConcurrent=4)
openmaic-1  | [2026-10-25T07:53:34.972Z] [WARN] [AccessCode] ACCESS_CODE is shorter than 16 characters. Use a long random value so the code cannot be brute-forced.

最后一条告警在第一次用口令换取访问凭证时出现(见 5.2),不是启动阶段打印。

这段日志包含三个关键信号:

  1. Ready 表示 Next.js 服务已就绪;
  2. GenerationRunner ... started 表示课程生成 worker 已启动,参数 scan=1000ms / heartbeat=2000ms / leaseTtl=10000ms / maxConcurrent=4 是默认租约与并发;
  3. ACCESS_CODE is shorter than 16 characters 是本文示例口令触发的告警,不是故障,但生产环境应按提示换成长随机值。

数据库侧日志同样应显示就绪:

docker compose logs postgres --since 5m | tail -6

实测回显(输出节选):

postgres-1  | 2026-10-25 07:53:15.868 UTC [1] LOG:  starting PostgreSQL 16.15 (Debian 16.15-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
postgres-1  | 2026-10-25 07:53:15.869 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
postgres-1  | 2026-10-25 07:53:15.869 UTC [1] LOG:  listening on IPv6 address "::", port 5432
postgres-1  | 2026-10-25 07:53:15.870 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres-1  | 2026-10-25 07:53:15.872 UTC [28] LOG:  database system was shut down at 2026-10-25 07:53:15 UTC
postgres-1  | 2026-10-25 07:53:15.874 UTC [1] LOG:  database system is ready to accept connections

五、部署验证

5.1 页面与健康接口

curl -s -o /dev/null -w 'HTTP %{http_code} type=%{content_type}\n' http://127.0.0.1:8144/

实测回显:

HTTP 200 type=text/html; charset=utf-8
curl -s http://127.0.0.1:8144/api/health

实测回显:

{"success":true,"status":"ok","version":"0.1.0","accessCodeConfigured":true,"capabilities":{"webSearch":false,"imageGeneration":false,"videoGeneration":false,"tts":false},"generation":{"parallelSceneConcurrency":0}}

解读:status: ok 表示进程健康;accessCodeConfigured: true 证明访问口令已生效;capabilities 中的四项全为 false,因为本文没有配置联网搜索、图像、视频与语音模型,属预期结果;parallelSceneConcurrency: 0 表示场景内容串行生成(默认)。

5.2 访问口令验证

未携带口令访问业务接口应被拒绝:

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8144/api/folders

实测回显:

HTTP 401

用示例口令换取的登录凭证(浏览器会自动完成这一步,命令行验证需要手工携带):

curl -s -D - -o /dev/null -X POST http://127.0.0.1:8144/api/access-code/verify \
  -H 'Content-Type: application/json' --data '{"code":"q1w2e3r4"}' | grep -i '^set-cookie'

实测回显(令牌值已截断):

set-cookie: openmaic_access=1792914698937.2a52dcf36442(令牌已截断); Path=/; Expires=Sun, 01 Nov 2026 07:51:38 GMT; Max-Age=604800; Secure; HttpOnly; SameSite=lax

⚠️ 纯 HTTP 场景的关键坑:生产构建给访问凭证带 Secure 标记,浏览器只在 HTTPS 或 http://localhost 下保存它。通过 http://<服务器IP>:8144 访问时,浏览器会丢弃该 Cookie,表现为「每次刷新都要重新输口令」「写入返回 403」。解决办法是在编排中加 COOKIE_SECURE=0(必须正好是 0,写 false 无效),并确保只在可信内网使用。

5.3 课程库写入验证

拿到访问凭证后,创建一个文件夹验证数据库读写链路。先用与浏览器相同的方式换取凭证并存入变量 TOK:

TOK=$(curl -s -D - -o /dev/null -X POST http://127.0.0.1:8144/api/access-code/verify \
  -H 'Content-Type: application/json' --data '{"code":"q1w2e3r4"}' \
  | sed -n 's/.*openmaic_access=\([^;]*\).*/\1/p')
echo "TOK=$TOK"

回显形如 TOK=<时间戳>.<十六进制串>。完整令牌是访问凭证,此处不展开;变量已在当前 shell 中生效,后续命令直接引用 $TOK 即可。

再创建文件夹:

curl -s -X POST http://127.0.0.1:8144/api/folders \
  -H "Cookie: openmaic_access=$TOK" -H 'Content-Type: application/json' \
  --data '{"name":"部署验证"}'

实测回显:

{"folder":{"id":"folder-2glSpJRO5GE8","name":"部署验证","order":0,"createdAt":1792914719248,"updatedAt":1792914719248,"userKey":"local"}}

userKey: "local" 说明单用户身份模式生效,所有请求归属同一个固定所有者。列出并删除该验证文件夹:

curl -s -H "Cookie: openmaic_access=$TOK" http://127.0.0.1:8144/api/folders

实测回显:

{"folders":[{"id":"folder-2glSpJRO5GE8","name":"部署验证","order":0,"createdAt":1792914719248,"updatedAt":1792914719248,"userKey":"local"}]}
curl -s -X DELETE "http://127.0.0.1:8144/api/folders/folder-2glSpJRO5GE8?mode=ungroup" \
  -H "Cookie: openmaic_access=$TOK"

实测回显:

{"ok":true,"removedStageIds":[]}

再次列表为空,说明创建、查询、删除整条写入链路正常:

{"folders":[]}

5.4 生成课程的前提验证

课程生成必须有模型。本文没有配置任何模型 Key,直接提交生成请求会得到明确的缺失提示:

curl -s -X POST http://127.0.0.1:8144/api/generate-classroom \
  -H "Cookie: openmaic_access=$TOK" -H 'Content-Type: application/json' \
  --data '{"requirement":"用 10 分钟讲清 TCP 三次握手"}'

实测回显:

{"success":false,"errorCode":"MISSING_MODEL","error":"No model is configured for course.outline. Set one in the model settings, or assign the slot (or an ancestor) in openmaic.yml."}

这条 400 不是部署失败,而是「流水线已就绪、只差模型」。按第六章配置模型后重新提交即可进入真正的大纲生成。

六、模型配置

6.1 配置方式

OpenMAIC v1.2.x 的模型配置以 openmaic.yml 为准,文件在服务启动时读取一次,修改后必须重启应用。把配置挂载到应用工作目录:

services:
  openmaic:
    volumes:
      - ./openmaic.yml:/app/openmaic.yml:ro
      - openmaic-data:/app/data

同时在部署目录写 /opt/openmaic/openmaic.yml,最小可用内容如下(服务商密钥通过环境变量注入,不要明文写进文件):

providers:
  openai:
    preset: openai
    apiKey: ${OPENAI_API_KEY}
    models: [gpt-5.5]

slots:
  llm: openai:gpt-5.5
  course.outline: openai:gpt-5.5

然后在编排的 environment: 中注入密钥,或在部署目录旁的 .env 文件中定义 OPENAI_API_KEY,重启服务:

cd /opt/openmaic
docker compose up -d
docker compose logs openmaic --since 2m | grep -i 'boot\|Invalid model'

启动校验通过时不会出现 [boot] Invalid server configuration 行;配置有误时服务拒绝启动并逐项指出字段位置(第七章给出实测报错)。

6.2 槽位与授权规则

  • providers:声明服务端可调用的账号。每个带密钥的服务商必须给出 models 允许列表;列表之外的模型不会被服务端使用。
  • slots:声明每一处 AI 用途的默认模型。llm 是根槽位,未单独指定的对话槽位跟随它;course.outline、course.content.*、classroom、agent 等可分别指定。
  • lock:把某些槽位固定给所有人,用户不能改。
  • allowUserKeys:是否允许用户在设置里添加自己的服务商与密钥。
  • fallback:语言模型槽位可配置备用模型,主模型遇到可重试错误时兜底一次。

💡 技术拓展:模型接入的三种常见方式。

方式特点适用场景
官方云 API直接填厂商 Key 与 Base URL,按量计费快速上线、模型质量优先
OpenAI 兼容网关统一协议接入多家模型,便于切换与审计多模型混用、内网网关
本地推理服务数据不出内网,需自备 GPU/内存合规敏感、离线环境

本文按官方云 API 形态配置;若接 Ollama 等自建服务,需把地址写成 http://host.docker.internal:11434/v1 并在 Linux 上为应用服务添加 extra_hosts: ["host.docker.internal:host-gateway"]。

七、数据持久化与备份

7.1 持久化对象

数据位置说明
课程、对话、学习进度、生成任务openmaic-postgres 数据卷PostgreSQL 数据目录,升级与重启都不丢
上传资料与解析结果openmaic-data 卷的 /app/data/materials由服务进程写入
课堂讲解媒体、用量记录/app/data 下对应子目录与数据库记录配合使用
实例密钥/app/data/instance-secret.key未显式设置 OPENMAIC_SECRET_KEY 时,首次保存模型密钥会触发生成;本文未配置模型,实测该文件尚未出现

查看实际数据目录:

docker compose exec -T openmaic ls -l /app/data

实测回显:

total 4
drwxr-xr-x    3 nextjs   nogroup       4096 Oct 25 07:34 materials

7.2 数据库备份

cd /opt/openmaic
docker compose exec -T postgres pg_dump -U openmaic -d openmaic > backup-20261025.sql
ls -lh backup-20261025.sql

实测回显:

-rw-r--r-- 1 root root 36K Oct 25 15:51 backup-20261025.sql

备份文件开头为标准 PostgreSQL 转储头:

--
-- PostgreSQL database dump
--

7.3 恢复演练

把备份恢复到临时库 openmaic_restore,确认可完整重建:

docker compose exec -T postgres createdb -U openmaic openmaic_restore
docker compose exec -T postgres psql -U openmaic -d openmaic_restore -q -f - < backup-20261025.sql
docker compose exec -T postgres psql -U openmaic -d openmaic_restore -Atc \
  "select count(*) from information_schema.tables where table_schema='public'"

实测回显:

 set_config
------------
 
(1 row)

25

临时库中恢复了全部 25 张表,恢复链路可用。演练完成后删除临时库:

docker compose exec -T postgres dropdb -U openmaic openmaic_restore

7.4 恢复注意事项

  • PERSISTENCE_POSTGRES_PASSWORD 只在数据库目录首次初始化时设置密码;之后修改环境变量不会修改已有库用户的密码。
  • instance-secret.key 若存在,必须与数据库一起备份;丢失它之后,之前在设置里保存的模型密钥将无法解密,只能重新填写。
  • 多实例共用一个数据库时,每个实例还必须共用同一个 /app/data 持久化目录,否则接手的生成任务读不到上传资料。

八、常用运维命令

cd /opt/openmaic
docker compose ps
docker compose logs -f openmaic
docker compose restart openmaic
docker compose stop
docker compose down

逐条说明:

  • ps:查看两个服务状态与端口映射,数据库应显示 healthy。
  • logs -f openmaic:跟踪应用日志,生成任务、访问口令告警与模型错误都记录在这里。
  • restart openmaic:只重启应用,不重建数据卷;改环境变量后用 up -d 才会应用新配置。
  • stop / down:停止服务;down 默认保留数据卷,加 -v 才会删除数据,执行前务必备份。

8.1 升级与回滚

升级时先备份数据库,再检出新标签并重建:

cd /opt/openmaic
docker compose exec -T postgres pg_dump -U openmaic -d openmaic > backup-before-upgrade.sql
git -C src fetch --tags
git -C src checkout v1.2.1
docker compose build openmaic
docker compose up -d

回滚到上一版本时,检出旧标签并重建,再按升级前的备份恢复数据库结构。由于数据库迁移可能不可逆,回滚前必须在旧数据库上验证过迁移脚本;跨次要版本升级(如 1.1.x → 1.2.x)还要阅读官方升级章节,处理 MODEL_ROUTES 迁移、旧课程导入与浏览器本地数据迁移。

九、常见问题

Q1:启动后立刻退出,日志中反复出现 [boot] Invalid server configuration ... DATABASE_URL is not set

openmaic-1  | [boot] Invalid server configuration; the server will not start: DATABASE_URL is not set. OpenMAIC stores courses in PostgreSQL and cannot start without a database. For local development, run `pnpm db:up` and set DATABASE_URL in .env.local (see .env.example). For a deployment, set DATABASE_URL to a PostgreSQL database, or use `docker compose up`, which starts one.

原因:环境变量被清空或数据库地址写错。检查 docker compose config 中 DATABASE_URL 是否有值,以及数据库服务是否先于应用就绪。实测该故障下容器状态为 Restarting (1),docker inspect 的 ExitCode=1、RestartCount=7,日志中同一段报错随每次重试重复出现:

openmaic-121-stack-openmaic-1    Restarting (1) Less than a second ago
openmaic-121-stack-postgres-1    Up 15 seconds (healthy)

Q2:修改 POSTGRES_PASSWORD 后应用连不上数据库

原因:PostgreSQL 只在数据目录为空时初始化用户密码,修改环境变量不会改已有库的密码。要么进入数据库手工改密码,要么在确认可删数据后重建数据卷(会丢数据)。

Q3:浏览器每次刷新都要求重新输入访问口令,或写入返回 403

原因:纯 HTTP 访问时 Secure 标记的 Cookie 不会被浏览器保存。在编排中加 COOKIE_SECURE=0,重启后重新验证。

Q4:点击生成课程,接口返回 400 MISSING_MODEL

原因:没有配置模型或槽位缺失。按第六章写 openmaic.yml 并重启;报错信息会直接点名缺失的槽位(如 course.outline)。

Q5:验证模型时提示 Cannot connect to API server, please check the Base URL

原因:应用所在网络无法访问模型服务地址,或 Base URL 写错。先确认应用服务内能解析并访问该域名;自建内网模型需开启 ALLOW_LOCAL_NETWORKS=true,并把地址写成编排网络可达的地址。

Q6:构建耗时很长或在大模型下载阶段失败

原因:首次构建需拉取 Node 22 基础层与全部 npm 依赖。为 Docker 引擎配置代理或加速地址后重试,已完成的层会被缓存。

十、参考链接