SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-21

OneCLI 自托管部署:每人一个沙箱代理

了解如何用 Docker Compose、PostgreSQL 和 VPS 自托管 OneCLI,为每个人运行独立沙箱代理,并由单一网关保管 API 密钥。注意每个代理默认需要 2 GiB 内存,1 GB VPS 不适用。

自行托管 OneCLI 后的架构

自行托管 OneCLI 后,团队中的每个人都可以拥有自己的代理。每个代理都运行在独立的沙箱中。API 密钥由网关保管,代理无法读取这些密钥。安装方式是 Docker Compose 堆栈,后端使用 PostgreSQL,可通过 http://localhost:10254 访问。请准备一台实际的服务器。文档规定每个代理沙箱默认需要 2 GiB 内存,因此 1 GB VPS 不适合运行此工作负载。

该堆栈包含七个组件。了解各组件的职责后,后续内容会更容易理解。

  • Web 仪表板(Next.js),端口为 10254。用于创建代理、聊天、编辑记忆和技能、管理连接与密钥。
  • API 服务器,端口为 10256。这是控制平面,负责数据库、会话处理和工作队列。
  • Rust 网关,端口为 10255。拦截代理发出的出站请求,并注入凭据。
  • Runner。README 将其描述为负责“启动、暂停和回收代理沙箱”的组件。它仅处理出站流量,并且不会访问数据库。
  • Sandbox Supervisor。README 将其描述为“在每个沙箱内部运行,通过与供应商无关的 harness 接口通信,因此可以替换代理运行时”。
  • Channel adapter。这是连接 Slack 应用的守护进程,使代理可以使用自己的名称回复频道消息和私信。
  • PostgreSQL。随附的 compose 文件使用 postgres:18-alpine,并挂载 pgdata 卷。

名称是 CLI,但产品是服务器

OneCLI 是一个服务器平台。这个名称容易让人以为它是安装在笔记本电脑上的命令行工具,但本指南介绍的实际产品并非如此。onecli/onecli-cli仓库中确实提供了一个独立的命令行客户端,它会将本地编码代理的流量通过网关路由。这里要部署的是一个多用户 Web 应用:包含账户系统,其中第一个账户拥有整个实例;包含用于存储对话和密钥的数据库;还包含用于启动容器的运行器。

按人员划分代理是整个设计的核心。README 中写道:“为每个人创建一个代理,为每个代理授予所需的访问权限;代理在沙箱中运行,并通过注入凭据且强制执行策略的网关进行路由。”每个代理都有自己的文件系统和 shell、独立的对话页面、由平台保存的记忆,以及可一次编写并重复使用的技能。凭据的使用方式与常见配置相反。不要将 API key 复制到每个人的环境中,而是只存储一次,然后将其授予允许使用它的代理。

开始前服务器需要满足的条件

  • Docker,并安装 2.19 或更高版本的 Compose 插件。Compose 文件使用一次性迁移服务,API 会等待该服务完成;这种依赖形式需要 2.19。
  • 内存,这是实际的限制因素。选择方案前,请先阅读下面的容量规划部分。
  • 空闲的回环端口 1025410255102565432

您不需要自行安装 PostgreSQL:Compose 文件会将其作为服务运行。您也不需要安装 Node.js 或 Rust。这些工具仅用于从源代码构建的路径,其中 mise 会固定工具链版本。

VPS 可容纳多少个 agent 沙箱?

Runner 的文档提供了具体数值,而不是凭感觉估算。每个沙箱分配 2048 MB 内存(RUNNER_SANDBOX_MEMORY_MB)、1 个 CPU(RUNNER_SANDBOX_CPUS)和 512 个进程(RUNNER_SANDBOX_PIDS)。并发上限为 4(RUNNER_MAX_SANDBOXES)。文档建议,除基础服务栈外,额外预留约 10 GiB 可用内存,以满足该并发上限。

ChartConcurrent agent sandboxes per box, at the documented 2 GiB default
The data behind this chart
[
  {
    "plan": "2 GB box",
    "ram_gb": 2,
    "sandbox_slots": 0
  },
  {
    "plan": "4 GB box",
    "ram_gb": 4,
    "sandbox_slots": 1
  },
  {
    "plan": "8 GB box",
    "ram_gb": 8,
    "sandbox_slots": 3
  },
  {
    "plan": "16 GB box",
    "ram_gb": 16,
    "sandbox_slots": 7
  },
  {
    "plan": "32 GB box",
    "ram_gb": 32,
    "sandbox_slots": 15
  }
]

这些槽位数量是算术结果,不是基准测试结果:总内存减去 PostgreSQL 和 4 个长期运行服务约 2 GB 的占用,再除以沙箱 2 GiB 上限。按此计算,2 GB box 可容纳 0 个沙箱,因此最便宜的方案根本无法运行托管 agent。16 GB box 可容纳 7,明显高于默认的 4 个并发沙箱,也高于 Runner 文档要求的约 10 GiB 可用内存。32 GB box 可容纳 15 个沙箱。

有两点会影响这个计算结果。运行后台进程的沙箱不会进入休眠,因此会永久占用一个槽位。这意味着应按 RUNNER_MAX_SANDBOXES 的持续负载进行容量规划,而不是按最繁忙的一分钟计算。另一个限制是内存会先于 CPU 耗尽。每个沙箱最多使用 1 个 CPU,因此 4 个繁忙的 agent 需要 4 个核心;但 4 个处于空闲状态却仍保持运行的 agent 仍会占用 8 GiB 内存。

可能需要调整的 Runner 设置
  • RUNNER_MAX_SANDBOXES(默认值:4):同时运行的沙箱数量。
  • RUNNER_SANDBOX_MEMORY_MB(默认值:2048):每个沙箱的内存上限。
  • RUNNER_SANDBOX_CPUS(默认值:1):每个沙箱的 CPU 上限。
  • RUNNER_SANDBOX_PIDS(默认值:512):每个沙箱的进程数上限。
  • RUNNER_NETWORK_INTERNAL(默认值:true):使沙箱网络无法访问外部路由。保持启用。
  • RUNNER_SANDBOX_NETWORK(默认值:onecli-sandboxes):沙箱加入的网络。
  • RUNNER_RECONCILE_SECONDS(默认值:60):Runner 协调状态的频率。
  • RUNNER_ORPHAN_GRACE_SECONDS(默认值:3600):销毁孤立容器和卷的存留时间。
  • RUNNER_AGENT_IMAGE:覆盖沙箱镜像;否则沙箱镜像遵循 ONECLI_VERSION

使用 Docker Compose 安装 OneCLI

上游自托管文档给出了以下完整步骤。该步骤会将 3 个密钥写入 compose 文件旁边的 docker/.env,然后启动堆栈。

git clone https://github.com/onecli/onecli.git && cd onecli/docker
cat > .env <<EOF
SECRET_ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
GATEWAY_INTERNAL_SECRET=$(head -c 32 /dev/urandom | base64)
BETTER_AUTH_SECRET=$(head -c 32 /dev/urandom | base64)
COMPOSE_PROFILES=runner
EOF
chmod 600 .env
docker compose up -d --wait

运行前先阅读这段内容。此处的 heredoc 标记未加引号,因此 shell 会执行其中的每个 head -c 32 /dev/urandom | base64,并写入执行结果,而不是写入字面文本。SECRET_ENCRYPTION_KEY 是数据库中所有密钥使用的 AES-256-GCM 密钥。GATEWAY_INTERNAL_SECRET 用于验证网关对 API 的身份。BETTER_AUTH_SECRET 用于签名会话 Cookie。COMPOSE_PROFILES=runner 最重要,因为 runner 服务位于 Compose profile 后面:如果省略它,堆栈会显示为健康,但不会启动任何 agent sandbox。

--wait 会一直等待,直到每个服务都报告健康状态。因此,非零退出状态是发现问题的第一个信号。然后检查实际启动了哪些服务。

docker compose ps
docker compose logs migrations

固定版本。ONECLI_VERSION 会一次性为所有服务设置标签;除非 RUNNER_AGENT_IMAGE 指向其他位置,否则 agent sandbox 镜像也会使用该标签。截至 19 August 2026,当前版本为 v2.0.1,于 18 August 2026 发布。将它添加到同一个文件中,然后重新启动堆栈。

echo 'ONECLI_VERSION=v2.0.1' >> .env
docker compose up -d --wait

此外还有安装程序 curl -fsSL https://onecli.sh/install | sh。它会将配置写入 ~/.onecli/.env,并执行相同的操作。Compose 方式允许您在任何内容运行前读取所有文件;如果服务器上已经运行其他 Compose 堆栈,应使用这种方式。第三种方式是从源代码构建,克隆的仓库中将其文档列为 pnpm install,然后是 pnpm run setup。这种方式仍需要 mise、用于网关的 Rust 和 Docker,适用于准备修改代码的用户。

从笔记本电脑访问仪表板

随附的 compose 文件中,每个发布的端口都绑定到 ${ONECLI_BIND_HOST:-127.0.0.1}。在 VPS 上,这表示仪表板正在运行,但主机外部无法访问它。这个默认设置是正确的。请保留该设置,并建立隧道:

ssh -N -L 10254:127.0.0.1:10254 you@your-server

现在在笔记本电脑上打开 http://localhost:10254。流量通过 SSH 连接传输,因此公网中不会暴露未加密的仪表板,也无需为额外端口配置防火墙。

设置 ONECLI_BIND_HOST=0.0.0.0 会通过普通 HTTP 发布仪表板,同时发布 PostgreSQL。如果需要让多个人访问仪表板,请在端口 10254 前配置带 TLS(传输层安全)的反向代理,并保持绑定主机不变。应在该实例有负责人之前完成此操作。上游文档明确说明了原因:“在此之前,该实例没有负责人;任何首先访问到它的人都会成为负责人。”如果反向代理已经为其他自托管应用提供入口,则通过 自托管的单点登录层转发身份验证,可将仪表板置于团队现有的登录保护之后。这样,在一个位置移除某人的权限,也会同时关闭这条访问路径。

创建首个账户,然后授予模型密钥

打开控制面板后立即创建账户。该账户拥有此实例。账户创建后,其他人必须通过邀请加入。

然后在创建 agent 前存储模型密钥。托管 agent 需要已授予的模型密钥,操作顺序很重要:先在控制面板中存储密钥,再将其授予 agent,最后启动对话。如果跳过授予步骤,沙箱将不会启动,表现为 agent 一直没有任何操作。

请严格限制授予范围。每个 agent 只能使用授予它的权限,网关会在每个请求上强制执行这些限制。因此,只读取一个代码仓库的 agent 无法访问支付服务提供商的密钥。相同的授予列表也可用于控制支出。允许每个人的 agent 调用您拥有的任何模型,就等于为每个人单独生成一份账单。因此,在向十个人授予权限前,请先了解如何 限制 agent 在模型调用上的支出

网关如何让密钥不进入代理程序

该网关是使用 Rust 编写的 HTTPS 代理,监听 10255 端口。代理程序的 HTTP 客户端指向网关,代理程序携带的是占位凭据,而不是真实凭据。网关根据代理程序的授权规则匹配出站请求,解密真实密钥,将其替换到请求中,然后转发请求。密钥使用 AES-256-GCM(高级加密标准、256 位、伽罗瓦/计数器模式)加密后存储在 PostgreSQL 中,并且只在请求时解密。每次调用都会记录代理程序的身份和目标。这会形成审计记录,而当密钥存放在十个人的 shell 配置文件中时,无法获得这样的记录。

部署方式取决于以下两个机制。

  • HTTPS 拦截属于中间人攻击。网关生成本地证书颁发机构,代理程序信任该证书颁发机构。网关随后终止代理程序的 TLS 连接,再与上游服务建立新的 TLS 连接。因此,如果代理程序的 HTTP 客户端不信任网关的证书颁发机构,连接会因证书验证错误而失败,而不是因身份验证错误而失败。
  • 代理程序通过 Proxy-Authorization 标头标识自身。在单机部署中,代理程序和网关共享内部 Docker 网络,该标头不会经过您不拥有的网络。将不在同一台主机上的代理程序指向网关时,代理端口需要单独启用 TLS,因为该标头属于 bearer token。

需要明确的是:网关会按设计以明文读取代理程序发出的每个请求。它是主机上最敏感的进程。应相应保护其所在主机,并通过 最小权限 Linux 用户限制可登录该主机的人员数量。

运行器无需入站端口

运行器仅发起出站连接。其文档说明:“它不占用任何外部网络可访问的端口,因此笔记本电脑、家庭实验室或位于 NAT 后的 VPC 都无需入站连接、隧道或 TLS 终止配置即可使用。”NAT 是网络地址转换,即家用路由器执行的功能。运行器向控制平面发起连接并从中获取任务,因此无需转发任何内容,也无需开放任何端口。

这种设计在沙箱网络中发挥了作用。Compose 文件定义了第二个标记为 internal: true 的网络。在 Docker 中,这表示该网络完全无法从主机向外部路由。沙箱会加入此网络。网关同时连接两个网络,因此它是唯一的出站路径。运行器文档明确说明了这一点:“将网关同时连接到 internal 网络,才能使仅允许通过网关出站成为真正的边界,而不是一项建议。”如果代理决定将您的源代码发送到它自行选择的地址,它没有路由可执行此操作。

请在您自己的主机上确认这一点,不要只相信上面的说明。

docker network ls
docker network inspect onecli-sandboxes | grep -i internal

您应看到 "Internal": true。如果输出为 false,则出站控制已关闭,网关又只是一项建议。请使用 docker network ls 输出的沙箱网络名称,因为 onecli-sandboxes 只是默认名称。

OneCLI sandbox 的隔离强度如何?

请仔细阅读本节。项目自身的描述非常强调“沙箱化”,但其实现机制只在一个地方有说明。

README 声明,每个 agent 都有“自己的隔离沙箱,其中包含文件系统和 shell”,并称 Sandbox Supervisor 是“在每个沙箱内运行的组件,通过供应商中立的 harness 接口通信,因此可以替换 agent runtime”。这两句话都没有说明隔离由什么实现。runner 的文档给出了答案:默认后端是 Docker(RUNNER_BACKEND=docker),沙箱是一个 Docker 容器,设置了内存上限、CPU 上限和进程数上限,并连接到内部网络。代码为其他后端预留了扩展点,文档则将 Kubernetes 和 microVM 等列为需要自行编写的模块。当前在你的机器上,沙箱就是一个容器。

文档没有说明的内容同样重要。文档没有威胁模型,也没有说明 Docker daemon 是否以 rootless 模式运行、是否启用用户命名空间重映射,或是否使用超出 Docker 默认配置的 seccomp 和 AppArmor 配置文件。文档也没有声称存在类似 gVisor 或 microVM 的内核边界。因此,应采用较窄的解读。各种上限是资源限制。内部网络确实限制了出站访问。agent 与主机之间的隔离能力,等同于标准 Docker 容器提供的能力;而容器与主机共享内核。

还需要考虑第二个事实。runner 服务挂载了 /var/run/docker.sock,因为它通过该路径创建沙箱。能够访问 Docker socket,等同于拥有主机上的 root 权限,因为能够调用该 API 的进程可以启动一个容器,并将主机文件系统挂载到容器内。所有基于 Docker 的 runner 都以这种方式工作。因此,runner 进程与 gateway 具有同等敏感性。

在上游明确记录边界之前,应将该边界视为未经证实。实际操作中,应遵循以下 3 点。

  1. 在只运行 OneCLI 的机器上部署。不要在该机器上运行无关的生产服务、共享数据库或其他团队的数据。
  2. 假设能够在沙箱内执行任意代码的 agent 可能访问主机,并通过将备份保存在其他机器上,使这种情况发生后仍可恢复。
  3. 在向同事说明 agent 已被隔离之前,先阅读 apps/runner/src,或向上游项目确认。

如需了解有文档记录的边界应如何描述,以及应向上游提出哪些问题,请对照真实 agent 沙箱边界的表现形式。两者的区别在于,是否有人明确记录了隔离机制及其无法阻止的行为。

许可证划分,以及构建前检查的原因

OneCLI 的核心部分采用 Apache-2.0 许可证,允许在生产环境中自行托管。名为 ee/ 的目录适用单独的 OneCLI Enterprise License:开发、测试和评估可免费使用,生产环境使用则需要订阅。2026 年 8 月 18 日发布的 v2.0.1 版本说明提到,项目恢复了可被 GitHub 检测到的 Apache-2.0 许可证文件,因此仓库页面上的许可证标识最近发生了变化。请检查实际部署的 tag,不要依据其他日期编写的摘要。

cd onecli && find . -type d -name ee -not -path '*/node_modules/*'

这些路径下的内容都属于商业部分。如果计划依赖的功能位于其中,请先确认价格,再围绕该功能构建流程。

升级、迁移,以及绝不能丢失的那个文件

升级包括提升版本和重启。每次 up 时,都会先于 API 运行一次性迁移服务。如果迁移失败,整个堆栈会拒绝启动,而不是使用只完成一半迁移的架构提供服务。这正是需要的行为,因为升级失败会表现为服务中断,而不是悄无声息的数据损坏;docker compose logs migrations 说明了原因。

cd onecli/docker
docker compose pull
docker compose up -d --wait
docker compose logs migrations

如果您使用安装脚本完成安装,请重新运行该脚本,不要手动拉取镜像。这样 compose 文件会始终与其中引用的镜像保持同步。

请备份两项内容。PostgreSQL 保存 agents、会话、memory 和加密密钥。docker/.env 文件保存 SECRET_ENCRYPTION_KEY。没有该密钥就无法读取加密密钥,因此仅备份数据库转储无法恢复任何可用数据。

cd onecli/docker
docker compose exec -T postgres pg_dump -U onecli onecli | gzip > ~/onecli-db.sql.gz
install -m 600 .env ~/onecli-env.backup

请将两份备份都保存到服务器之外。所需流程与任何有状态 Compose 堆栈相同。如果您已经按计划备份和升级 Docker Compose 堆栈,只需将这两个路径加入流程即可,不必再手动处理。

无法正常工作时

  • 堆栈始终无法变为健康状态,并且 docker compose up -d --wait 以非零状态退出。先读取 docker compose logs migrations,因为 API 会有意等待该服务。
  • Agent 处于空闲状态,且没有出现 sandbox。确认 COMPOSE_PROFILES=runner 位于 docker/.env 中,并确认 docker compose ps 列出了一个 runner。然后检查该 agent 是否已获得可用的模型密钥,因为没有模型密钥时不会启动 sandbox。
  • 没有可用槽位。RUNNER_MAX_SANDBOXES 默认为 4,运行后台进程的 sandbox 会永久占用其槽位。docker ps 可显示实际仍在运行的内容。
  • 容器消失,或主机运行缓慢。主机内存不足。dmesg -T | grep -i oom 会记录内核因内存不足而终止进程的事件,单个 sandbox 就可能独占 2048 MB。
  • Agent 的 HTTPS 请求失败,并显示证书验证错误,而不是身份验证错误。其 HTTP 客户端不信任网关的证书颁发机构。
  • 删除 agent 后,旧容器或卷仍然存在。runner 每 60 秒执行一次协调,并销毁存在时间超过 RUNNER_ORPHAN_GRACE_SECONDS 的孤立资源;RUNNER_ORPHAN_GRACE_SECONDS 默认为 3600。因此,请等待 1 小时后再判断这是内存泄漏。

这适合您运行吗?

适用性测试很简单。当多人各自需要一个 agent,而您希望将凭据集中管理时,OneCLI 才有价值:只需维护一个存储、查看一份审计日志,并在一个控制面板中撤销某人的访问权限;撤销操作应立即生效。这是实际的运维问题。相比之下,将 API key 复制到 6 台笔记本电脑上是更糟糕的解决方案。

对于个人用户,这套架构投入大,但没有实际收益。您需要运行 PostgreSQL、控制平面、网关和 runner,只为自己提供一个 agent。网关要解决的凭据问题,在 key 唯一持有者是您时几乎不存在。请改为在更小的主机上运行单个 harness:VPS 上的单个 agent harness 可以完成这项工作,但只需占用很少的内存。如果您还没有确定方向,先阅读 自托管 AI agent 对比 会是成本更低的第一步。

FAQ

自托管 OneCLI 的最低服务器要求是什么?

需要 Docker Compose plugin 2.19 或更高版本,以及足够的内存。PostgreSQL 已包含在 compose 文件中,因此无需单独安装。内存决定可运行的规模:runner 默认会为每个 agent sandbox 分配 2048 MB;其文档要求除基础堆栈外额外提供约 10 GiB 可用内存,以支持默认上限的四个 sandbox;PostgreSQL 和四个长期运行的服务约占用 2 GB。4 GB 的服务器可同时运行一个 agent。16 GB 的服务器可稳定支持默认上限。1 GB 或 2 GB 的 VPS 根本无法启动 hosted agent。

OneCLI 需要 PostgreSQL,还是可以使用 SQLite?

需要 PostgreSQL。DATABASE_URL 的文档定义为 PostgreSQL 连接字符串;随附的 compose 文件会使用 postgres:18-alpinepgdata 卷;独立的 migrations service 会在 API 启动前应用数据库 schema。文档未提供 SQLite 选项。如果您已经在其他位置运行 PostgreSQL,请将 DATABASE_URL 指向该实例,并保留 migrations service,因为迁移失败会停止整个堆栈,而不会让应用使用只应用了一部分的 schema 对外提供服务。

OneCLI agent sandbox 是真正的安全边界吗?

文档说明的机制是 Docker 容器,并设置内存、CPU 和进程数上限;容器连接到标记为 internal: true 的网络,因此除通过 gateway 外没有其他出站路由。出站控制确实生效,您可以使用 docker network inspect 进行验证。主机隔离能力属于容器级别,但上游没有发布威胁模型,也没有声明支持 rootless 或 user namespace,更没有说明使用 gVisor 或 microVM 等内核级边界。runner 还会挂载 /var/run/docker.sock,这相当于获得主机上的 root 权限。在上游明确说明这一边界之前,应视 agent 与主机之间的隔离为未经证实。请在专用服务器上运行 OneCLI,并将备份保存到该服务器之外。

是否需要为 OneCLI 开放任何入站端口?

不需要。runner 仅发起出站连接,不监听任何外部网络可访问的端口,因此在 NAT 后无需隧道即可运行。compose 文件默认将 dashboard、gateway、API 和 PostgreSQL 绑定到 127.0.0.1。您可以通过 SSH 隧道访问 dashboard;如果需要让多人访问,可在 10254 端口前配置带 TLS 的反向代理。10255 端口上的 gateway 供 agent 使用;在单服务器部署中,这些 agent 会通过内部 Docker 网络访问 gateway。

在公司内部使用 OneCLI 是否免费?

核心组件采用 Apache-2.0 许可,自托管生产环境使用无需商业许可。名为 ee/ 的目录受 OneCLI Enterprise License 约束;该许可可免费用于开发、测试和评估,但生产环境需要订阅。不同 release 之间的目录划分可能变化。2026 年 8 月 18 日发布的 v2.0.1 notes 提到恢复 GitHub 可识别的 Apache-2.0 许可文件,因此在围绕某个单独功能构建工作流之前,请检查您实际部署的 tag 中的 LICENSEee/ 目录。