跳转至

Docker

OpenBot 镜像是 Linux 发行版,以容器形式作为自托管服务器运行。它是你账户中的一个普通远程主机。你可以从桌面应用、iPhone 应用或 openbot.run/app 使用它。镜像为 ghcr.io/nightly-labs/openbot,支持 linux/amd64 和 linux/arm64。

运行

使用 Docker Compose:

curl -fsSLO https://raw.githubusercontent.com/nightly-labs/openbot/main/docker/compose.yaml
curl -fsSLO https://raw.githubusercontent.com/nightly-labs/openbot/main/docker/seccomp.json
docker compose up -d
docker compose exec openbot openbot login

使用 docker run:

curl -fsSLO https://raw.githubusercontent.com/nightly-labs/openbot/main/docker/seccomp.json
docker run -d --name openbot --restart unless-stopped \
  --security-opt seccomp=seccomp.json --security-opt no-new-privileges:true \
  --shm-size 1g --stop-timeout 60 \
  -v openbot-data:/data \
  ghcr.io/nightly-labs/openbot:latest
docker exec -it openbot openbot login

openbot login 会要求你输入邮箱以及 OpenBot 发送到该邮箱的验证码。如果操作员是为他人设置容器,可以使用 openbot login --claim 代替(参见服务器认领)。登录后,该服务器会显示在该账户的服务器列表中。然后像在托管服务器上一样,从应用中连接一个提供商。

要连接手机应用,请在终端中显示一个 Mobile Connect 代码,然后用应用扫描它:

docker exec -it openbot openbot mobile-connect

参见手机应用。

两个示例中的选项都是必需的:

选项 原因
seccomp=seccomp.json Electron 沙箱会创建用户命名空间。Docker 的默认 seccomp 配置会拒绝这些命名空间,容器会停止并显示一条消息。参见安全。
--shm-size 1g Chromium 使用共享内存。Docker 默认的 64 MB 太小。当 /dev/shm 小于 1 GiB 时,Chromium 会使用 /tmp,这样速度较慢。
--stop-timeout 60 OpenBot 在退出前会停止其智能体并关闭数据库。
-v openbot-data:/data 所有数据都位于 /data 中。如果没有卷,数据会随容器一起消失。

对于 Amazon ECS,请参见 AWS ECS。

容器不发布任何端口。OpenBot 仅主动向外连接:连接账户服务器、连接提供商,并连接 Signal 和 TURN 以支持远程使用。Team API 在容器内的 127.0.0.1 上监听。容器不提供 Web 界面。在 openbot login 之后,使用同一个账户访问 openbot.run/app 来选择服务器。此镜像不包含在你自己的域名下运行的 Web 客户端。自托管账户服务 是一个独立的部署,有自己的客户端限制。

网络

容器使用 Docker bridge 网络。在容器内,OpenBot 只能看到自己的 bridge 地址。设备通过主机的公共地址或 TURN 连接到它。在公共服务器上,这已经足够:在 VPS 上的测试表明,bridge 网络和主机网络的连接时间相同。

当设备与主机位于同一本地网络或 Tailscale tailnet 中时,bridge 网络可能会导致连接变慢:容器看不到主机的本地地址或 tailscale0 地址,因此设备通常会使用 TURN。使用主机网络时,OpenBot 可以看到主机的所有地址,设备可以直接连接。在 compose.yaml 的服务中添加 network_mode: host,或在 docker run 中添加 --network host。主机网络在装有 Docker Engine 的 Linux 上可用。

主机网络会移除容器的网络隔离:

  • 主机上的其他程序可以连接到 OpenBot 在 127.0.0.1 上的监听器。这些监听器接收 tokens 和团队请求。
  • 智能体可以连接主机上仅在 127.0.0.1 上监听的服务。
  • 容器的虚拟显示器 :0 和 :1 使用抽象 X 套接字,这些套接字位于网络命名空间中。当主机有自己的显示器 :0 或 :1 时,容器不会启动。主机上的其他程序可以连接到这些显示器,查看智能体的显示器并向其发送输入。

仅在您信任主机上的其他程序和用户时使用它。不要与单主机上的多个实例一起使用:它们的防火墙只在 openbot bridge 网络上生效。

标签

标签 镜像
latest 最新的发布版本。
<version>,例如 0.30.0 一个发布版本。使用它来选择何时升级。
v<version> 在 0.31.0 之后发布的版本中,是 <version> 的别名。更早的版本使用不带 v 的标签。
sha-<commit> 该提交对应的发布版本。
<version>-amd64、<version>-arm64 特定架构对应的一个发布版本。

公共镜像无需 GHCR 登录。如果 latest 或不带 v 的版本返回 denied、unauthorized 或 not found,维护者必须检查包的可见性。参见 Docker 包访问。

数据

/data 是用户 openbot(UID 1000,GID 1000)的主目录。OpenBot 及其智能体以该用户身份运行,并将所有内容保存在那里:

路径 内容
/data/.config/OpenBot 数据库 openbot.db、日志、下载的提供商运行时和浏览器数据。
/data/OpenBot 智能体工作区(Agents)、Shared 和 Downloads。
/data/.local/share/keyrings、/data/.config/openbot-container 密钥环及其密码。密钥环持有 /data 中加密数据的密钥。
/data 中的其他文件 提供商的登录信息和智能体工具的文件,例如 ~/.codex 和 ~/.claude。

将 /data 作为一个整体来对待。没有密钥环,加密数据无法打开;没有密码文件,密钥环也无法打开。每次为一个容器使用一个卷。

对于绑定挂载,请先将文件夹的所有权授予镜像的用户:

mkdir openbot-data && sudo chown 1000:1000 openbot-data

这些数据包含你的账户会话、你的提供商登录状态以及你的对话。要像保护主目录一样保护它。要备份它,请停止容器并复制该卷。docker/instances 中的一个实例提供了相关命令,请参阅 备份。

命令

在容器内,openbot status、login、mobile-connect、logout、name 和 version 的用法与自托管服务器上相同。其他任务请在主机上使用 Docker 完成:

任务 命令
读取日志 docker logs openbot
停止、启动、重启 docker stop openbot、docker start openbot、docker restart openbot
升级 docker compose pull && docker compose up -d,或者拉取镜像并用同一卷重新创建容器
删除 docker exec openbot openbot logout,然后运行 docker stop openbot && docker rm openbot。数据仍保留在卷中。docker volume rm openbot-data 会将其删除。

在一台主机上运行多个实例

docker/instances/ 借助 systemd 在一台 Linux 主机上运行多个 OpenBot 容器。每个实例都有自己的 CPU、内存和磁盘限制,自己的数据以及自己的账户。将仓库的 docker 文件夹复制到主机,例如复制到 /opt/openbot-docker,然后以 root 身份运行:

/opt/openbot-docker/instances/openbot-instance setup
/opt/openbot-docker/instances/openbot-instance create main --cpus 8 --mem 32g --disk 200G
/opt/openbot-docker/instances/openbot-instance run main login
路径 内容
instances/<name>.env 实例的名称和限制。修改 OPENBOT_CPUS 或 OPENBOT_MEM 后,运行 openbot-instance up <name>。OPENBOT_DISK 仅用于提供信息:修改它不会产生任何作用。
/srv/openbot/<name>/data.img 一个 ext4 镜像。其大小即磁盘限制。create 会在主机磁盘上预留全部空间,因此这些镜像永远不会需要超出主机容量的空间。
/srv/openbot/<name>/data 该镜像的挂载点,即容器中的 /data。/etc/fstab 会在 Docker 启动前挂载它。X-fstrim.notrim 可阻止 fstrim.timer 释放预留空间。
/srv/openbot/<name>/update-requests 应用的更新请求。容器会在此写入空文件。它是一个 64 KiB、16 个文件的 tmpfs,由 /etc/fstab 挂载。
/srv/openbot/<name>/update-status 更新的状态,由 root 写入。容器只能读取它。

容器名为 openbot-<name>。其根文件系统是只读的:容器层位于主机磁盘上,而 Docker 在 ext4 上无法限制其大小。在 /data 之外,容器只向计入内存限制的 tmpfs 文件夹写入内容:/tmp(8 GiB)、/var/tmp(1 GiB)、/run/lock、/run/openbot,以及更新请求所在的小型 tmpfs。在镜像被挂载之前,该文件夹归 root 所有,因此容器无法写入 /data 并会停止。它绝不会在空的主目录下启动。

实例的 CPU 和内存

--cpus 是一个 CPU 时间限制。容器在 /proc/cpuinfo 和 /proc/stat 中仍会看到主机的每个 CPU,因此根据 CPU 数量确定自身规模的程序会启动过多 worker:在给实例分配 8 个 CPU 的 64-CPU 主机上,next build 启动了 63 个 worker,两次构建就占满了 32 GiB。setup 的最后一步是使用 --enable-cfs(lxcfs-openbot.conf)安装 LXCFS;如果该步骤失败,它会打印警告,而其余安装步骤仍已完成。当 LXCFS 运行时,openbot-instance 会添加 lxcfs.yaml:随后容器会在 /proc/cpuinfo 中读取自己的 CPU 限制,在 /proc/meminfo 和 /proc/swaps 中读取自己的内存限制。在 cgroup v2 上,LXCFS 5.0 不会限制 /proc/stat 中的 CPU 行,而 Node 的 os.cpus() 会统计这些行,因此 OpenBot 会自行限制 agent 的 Node 程序(参见 agent 进程)。nproc、Python 的 os.cpu_count() 以及其他读取 CPU 亲和性或 /sys/devices/system/cpu/online 的程序仍会看到主机的 CPU。该文件不会被挂载:LXCFS 会在其中列出 CPU 0 到 limit-1,但进程仍可运行在主机的任何 CPU 上,随后 Gemini CLI 会因 SIGILL 退出。即使没有 LXCFS,实例仍然会启动(应用的更新绝不能失败),并且 ready 会打印警告。

当容器被重新创建时,实例才会获得 LXCFS 文件:在 setup 之后运行 openbot-instance up <name>。重启 LXCFS 会破坏运行中容器内的这些文件,因此当有容器正在使用它时,setup 不会重启它;每次 setup 都会打印警告,直到 LXCFS 以 --enable-cfs 运行为止。Docker 在 LXCFS 之后启动(docker-openbot.conf)。

start 还会检查容器是否无法进行交换(memory.swap.max 为 0);如果可以进行交换,或者无法读取该值,则会打印警告。发生交换的容器在其内存占满时会一次停顿数秒,其客户端会丢失连接。

日志

容器日志通过 syslog 日志驱动进入 rsyslog 的套接字 /var/lib/openbot-docker/dev/log,而不是进入 journal 或 /var/log/syslog,后两者的大小限制由其他服务共享。rsyslog 写入 /var/log/openbot/openbot-<name>.log。logrotate 每天轮转一次,如果超过 200 MB 则更早轮转。openbot-logrotate.timer 每 5 分钟检查一次。它会保留最新的 30 个已轮转文件,并且不保留超过 30 天的文件,因此一个实例最多保留 31 个约 200 MB 的文件。写入量很大的实例保存时间不足 30 天。docker logs 读取 Docker 的本地缓存,该缓存只保留最近的行。rsyslog 会截断超过 8 KB 的行。setup 会安装 rsyslog、logrotate 文件和定时器。其 drop-in 文件 docker.service.d/openbot.conf 让 Docker 在 rsyslog 之后启动,因为没有该套接字容器无法启动;并且要求 openbot-firewall.service:如果防火墙失败,Docker 就不会启动。

主机设置

setup 还会安装 /etc/sysctl.d/90-openbot-docker.conf。使用 userns-remap 时,所有容器都以一个主机用户的身份运行,并共享该用户的 inotify 限制,因此该文件会提高这些限制。每个容器最多可以打开 65536 个文件(Docker 默认只允许 1024 个)。将 "live-restore": true 添加到 /etc/docker/daemon.json,然后运行 systemctl reload docker:这样 Docker 重启或升级时就不会停止这些实例。

隔离

代理(Agents)在其容器内拥有完全访问权限,因此宿主机会限制容器可以访问的内容。

  • 网络。 setup 会创建 Docker 网络 openbot(172.30.0.0/16,桥接 br-openbot),并关闭容器间流量,因此实例之间无法互相访问。它还会安装 openbot-firewall.service,该服务在 Docker 启动前运行 instances/openbot-firewall。DOCKER-USER 中的 OPENBOT-EGRESS 链会丢弃发往私有、本地、组播地址范围以及 /etc/openbot-docker/deny 中所列地址的流量。INPUT 开头的一条规则会丢弃从桥接器发往宿主机的流量。容器只能访问互联网。create、up 和 upgrade 在防火墙关闭时会拒绝启动容器。
  • 其他服务器。 来自容器的流量以宿主机的地址离开。将信任该地址的服务器逐行写入 /etc/openbot-docker/deny,然后再次运行 setup。
  • 用户。 在 /etc/docker/daemon.json 中设置 "userns-remap": "default" 后,容器中的 UID 1000 对应宿主机上 /etc/subuid 中 dockremap 范围的起始值加 1000,即宿主机上一个没有任何权限的用户。create 会将数据交给该用户。此设置作用于整个 Docker 守护进程:Docker 随后会使用另一个数据文件夹,因此之前创建的容器、镜像和网络在移除该设置之前不可见。要对现有实例启用它:先 down 每个实例,添加该设置,重启 Docker,运行 setup,对每个 /srv/openbot/<name>/data 执行 chown -R <uid>:<gid>,然后 up 每个实例。setup 在该设置关闭时会显示警告。
  • DNS。 Docker 会从容器所在的网络发送容器的 DNS 查询,因此防火墙同样适用于这些查询。当宿主机使用私有或链路本地地址上的解析器时(例如在 AWS 或 GCP 上),容器将没有 DNS。此时可在 compose.yaml 中将 dns: 设置为互联网上的解析器。
  • 内核。 容器与宿主机共享内核。对于你不信任的所有者,请使用没有其他服务的主机,或为每个实例使用一台虚拟机。
任务 命令
显示实例 openbot-instance list
停止、启动、重启 openbot-instance down <name>、up <name>、restart <name>
读取日志 openbot-instance logs <name> -f(最新行),或 /var/log/openbot/openbot-<name>.log*(30 天)
升级 在应用中(参见来自应用的更新),或 openbot-instance upgrade <name> 或 upgrade --all
openbot 命令 openbot-instance run <name> status、login、mobile-connect、name <name>
扩大磁盘 down <name>、umount /srv/openbot/<name>/data、fallocate --length <new size> data.img、e2fsck -f data.img、resize2fs data.img、mount /srv/openbot/<name>/data、up <name>

没有用于删除实例的命令。若要删除实例并清除其数据:执行 run <name> logout、down <name>、systemctl disable --now openbot-instance-update@<name>.path、umount /srv/openbot/<name>/data /srv/openbot/<name>/update-requests,删除 /etc/fstab 中与其相关的两行,然后删除 /srv/openbot/<name> 和 instances/<name>.env。

来自应用的更新

所有者或管理员可以在服务器设置 › 更新中,或在更新对话框中更新实例,这与托管服务器上的操作相同。容器永远不会获得 Docker 套接字。OpenBot 会向 /srv/openbot/<name>/update-requests 写入一个空请求文件。openbot-instance-update@<name>.path 检测到该文件后,会以 root 身份运行 openbot-instance update-request <name>。Root 只会测试并移除该请求;它从不读取或跟踪该请求。它拉取实例的镜像,并且仅当镜像版本新于容器时,才将版本写入 update-status。对于安装操作,它会使用该镜像重新创建容器,并保留相同的数据。它会将请求状态写入 update-status,容器以只读方式挂载该文件。

setup 会安装这些单元。create、up 和 upgrade 会创建文件夹,将请求的 tmpfs 添加到 /etc/fstab,并启用实例的路径单元。env 文件中带有 OPENBOT_TAG 的实例会保留该标签,并且在应用中没有更新可提供。在此版本之前创建的实例可通过 setup,然后执行一次 openbot-instance upgrade <name> 来获得这些功能。使用 docker/compose.yaml 或 docker run 启动的容器没有请求文件夹,因此在应用中没有更新可提供。

备份

openbot-instance backup <name> 会将实例的全部 /data 复制到一个存档中:包括会话、附件、代理、例程、记忆、技能、工作区、带有共享表的 Shared、两个数据库、提供方登录信息以及带有密码的密钥环。恢复此存档会返回同一个实例,并保留相同的代理、会话和记忆 ID。这不是迁移,迁移会将带有新 ID 的代理添加到另一台服务器。

在 instances/<name>.env 中设置备份文件夹,然后以 root 身份运行第一次备份:

echo OPENBOT_BACKUP_DIR=/var/backups/openbot >>/opt/openbot-docker/instances/main.env
/opt/openbot-docker/instances/openbot-instance backup main --verify
设置 用途
OPENBOT_BACKUP_DIR 备份所在的文件夹。每个实例在其中都有名为 <name> 的子文件夹。该文件夹不能位于某个实例的数据目录中,因为备份包含该实例的密钥。请使用另一块磁盘,或将文件夹复制到另一台计算机。
OPENBOT_BACKUP_KEEP 要保留的完整备份数量。默认值为 7。只有在新的备份完整完成后,才会删除较旧的备份;使用 --verify 时,只有在新的备份通过验证后才会删除。
OPENBOT_BACKUP_MAX_AGE 小时。当最新的完整备份更旧时,backups 会将其显示为逾期,并以退出码 1 结束。
OPENBOT_BACKUP_WORK verify 解包备份的文件夹。默认值为 /var/tmp。它需要足够的空间来存放数据,并且数据的用户必须能够访问该文件夹。

备份会停止容器,等待 OpenBot 关闭其数据库,写入存档,然后重新启动容器。在写入存档期间,实例处于离线状态。输出和状态会显示耗时。正在进行的轮次(turn)会停止,就像重启时一样。每个备份都是一个 <UTC time> 文件夹,其中包含:

文件 内容
data.tar.zst 不包含 lost+found 的 /data。当主机没有 zstd 时,使用 gzip(data.tar.gz)。
manifest.json OpenBot 版本和镜像、大小、归档的校验和、/data 中的文件夹、停止时间和离线时间。
required.sha256 openbot.db、密钥环、其密码和 agent-data.db 的校验和。
instance.env 实例 env 文件的一份副本。
SHA256SUMS 其他文件的校验和。
verified.json 最后一次验证的结果(如果有)。

备份会写入 <UTC time>.partial,并且仅当所有文件都已写入后才重命名该文件夹。当某个文件无法读取、文件夹的可用空间少于数据所占空间、复制中断,或者数据库或密钥环缺失时,备份会失败并保留之前的备份。

openbot-instance verify <name> [<backup>] 检查最新备份或指定备份。它需要在主机上安装 sqlite3 3.37 或更高版本。它会比较校验和,将归档解压到 OPENBOT_BACKUP_WORK 中的临时文件夹,并检查数据库、密钥环及其密码是否存在且是被复制的文件。它会对两个数据库运行 PRAGMA integrity_check,读取模式版本,统计代理、线程、消息、记忆和共享表,并找出所有者不是备份中的代理的共享表。文件夹中不会运行任何内容:没有例程启动,也没有消息发出。root 不会解压归档或打开数据库,因为代理可以更改 /data 中的每个文件。数据的用户会执行这些操作,并且 sqlite3 以 -safe 模式运行。因此,该用户(UID 1000,无用户命名空间重映射)可以读取临时文件夹,直到验证将其删除。验证不会检查每个附件文件。

openbot-instance backups <name> 显示备份以及最后一次备份和验证,并在失败时显示错误。--json 为监控器提供相同的信息,其中 lastBackup 和 lastVerify 设置为 ok、failed、running、stopped 或 none。stopped 表示运行在没有结果的情况下结束,例如它被终止或主机重启。当最后一次备份或验证失败或停止,或最新备份已逾期时,命令以退出码 1 退出。root 用户将状态保存在 /srv/openbot/<name>/backup-status。

每天 04:00 进行备份,并在其后进行验证:

systemctl enable --now openbot-instance-backup@main.timer
systemctl edit openbot-instance-backup@main.timer   # another time: OnCalendar= under [Timer]

计时器在容器外运行,因此它也可以备份无法工作的实例。setup 会安装这些单元。同一实例的备份、验证、恢复、更新以及 up、down、restart 和 upgrade 操作会使用同一把锁。一个操作会等待另一个操作结束,因此不会在另一个操作运行时启动或停止容器。

要恢复,实例必须存在。对于新主机,请先用相同的名称 create 该实例。

openbot-instance restore main 20261014T040000Z --replace
openbot-instance up main

restore 在停止容器之前会检查校验和并在临时文件夹中验证备份。它使用与 verify 相同的检查和临时空间,即使该备份之前已验证过。因此,restore 也需要主机上安装 sqlite3 3.37 或更高版本。验证失败会使目标保持当前状态,其数据不变。验证状态会记录失败,并保留上次成功的结果。验证之后,restore 会停止容器,并将备份解压到数据旁边。只有当归档解压完成后,它才会替换 /data。当实例已有数据时,restore 需要 --replace,这会删除这些数据,因此请先备份。由于旧数据会保留到最后,实例磁盘需要为备份的数据留出可用空间。如果空间不足,请将旧数据复制到另一块磁盘,从 /srv/openbot/<name>/data 中删除,然后恢复到空文件夹中。restore 不会启动实例:

  • 仅当备份中的实例没有在其他任何地方运行时才启动它。两个副本会使用相同的服务器身份和相同的账户会话。
  • restore 会拒绝来自比实例镜像更新的 OpenBot 的备份。请先拉取更新的镜像。较旧的 OpenBot 无法打开较新版本迁移过的数据库。不要使用备份回退到较旧版本。
  • 较新的 OpenBot 会在首次启动时迁移数据库。如果你想保留备份中的版本,请在升级前再次备份恢复后的数据。
  • 提供商登录信息包含在备份中。当提供商会话过期时,它仍可能要求重新登录。账户会话也包含在备份中。

升级

OpenBot 不会在容器内自行更新。新镜像会替换它。docker/instances 的实例可以通过应用向主机请求该更新,参见来自应用的更新。不要回退到较旧的标签:较旧的 OpenBot 无法打开较新版本迁移过的数据库,因此它不会启动。

更新自动化

latest 跟随最新发布的版本。发布工作流不会发布浮动的次要版本标签(如 0.39),也不会发布单独的 stable 标签。对于受控升级,请在 Compose 文件中保留精确版本,或在实例 env 文件中保留 OPENBOT_TAG。

GitHub latest-release API 是获取最新完整发布的机器可读来源。例如,以下只读命令会打印其标签:

gh api repos/nightly-labs/openbot/releases/latest --jq '.tag_name'

自动化可以轮询该元数据并下载镜像。安装需要以下检查:

  1. 阅读发布说明,并确认候选版本比正在运行的版本更新。遇到 API 错误或意外标签时停止。不要用 latest 替代失败的版本查找。
  2. 将你的部署文件与候选发布标签处的文件进行比较。检查 docker/compose.yaml 和 docker/seccomp.json。对于 docker/instances 的实例,也要检查 docker/instances/ 中的文件。这些文件在不同发布之间可能发生变化;不能保证旧副本仍然够用。当它们发生变化时,停下来等待操作员审查。保留本地设置。
  3. 在替换容器之前,拉取确切的镜像标签,并检查其发布版本和架构。发布工作流会在镜像标签之前发布 GitHub release,因此新版本可能会出现,但其镜像尚不可用。拉取失败会使安装处于待处理状态。
  4. 备份完整的 /data 恢复单元,包括密钥环和密码文件。对于 docker/instances 的实例,运行 openbot-instance backup <name> --verify,参见备份。对于来自 docker/compose.yaml 或 docker run 的容器,停止容器,确认它已停止,然后复制卷,参见数据。保留之前完整的备份。
  5. 使用所选镜像以及相同的卷和运行时选项替换容器。对于 docker/instances 的实例,在其 env 文件中设置 OPENBOT_TAG,然后运行 openbot-instance upgrade <name>。此命令会获取实例的锁,因此来自应用的备份、恢复或更新不会同时运行。手动替换不会获取锁。在升级被报告为完成之前,检查 openbot status 和容器日志。

无人值守的镜像监视程序不会执行这些检查,也不会创建一致的备份。除非你自己的作业实现了这些操作,否则请将安装步骤置于操作员控制之下。升级失败时,绝不能让旧镜像针对已升级的数据启动。恢复应使用兼容版本的完整备份,而不是对在线卷进行镜像标签回滚。

内存

容器使用托管服务器的内存守卫(参见 内存)。当可用内存不足时,新的轮次会在队列中等待,并在内存可用时再开始。来自人的消息会最先开始。浏览器不会打开新的标签页,并且会更早地卸载空闲页面。

内存上限取主机内存与容器内存限制(--memory,或 Compose 中的 mem_limit)中的较小值。若不设限制,上限即主机内存。同一时间运行的轮次数量固定:上限不超过 5 GiB 时为 2 个,不超过 10 GiB 时为 8 个,超过 10 GiB 时为 16 个。/dev/shm 和 tmpfs 文件夹也计入该限制。docker exec openbot openbot status 会显示上限、可用内存、级别(ok、low 或 critical)以及轮次限制。

守卫读取 cgroup v2,当前的 Docker 主机均使用该版本。在运行 cgroup v1 的主机上,容器只能看到主机内存,因此守卫会使用该数值,并在日志中记录一条警告。

主进程(Main)在容器中的 OOM 分数调整为 0,而它启动的每个进程为 500。因此当容器内存不足时,内核更有可能停止提供商 CLI、MCP 服务器或智能体工具,而不是 OpenBot。智能体进程还以 nice 10 运行;在 critical 级别下,主进程会停止残留的智能体进程,例如父进程已被停止的构建所对应的 worker 进程。参见 智能体进程。

环境

通过 -e 或 Compose 文件中的 environment 传入这些变量,切勿将它们写入镜像。智能体会继承 OpenBot 的环境,因此可以读取这些值。

变量 用途
XAI_API_KEY, CURSOR_API_KEY, CLINE_API_KEY 提供商 API 密钥。
OPENBOT_CODEX_PATH, OPENBOT_CLAUDE_PATH, OPENBOT_GROK_PATH, OPENBOT_OPENCODE_PATH, OPENBOT_CURSOR_PATH, OPENBOT_CLINE_PATH, OPENBOT_ANTIGRAVITY_PATH, OPENBOT_PI_PATH, OPENBOT_MUSE_PATH 另一个提供商的可执行文件。在容器中将其只读挂载。
OPENBOT_AUTH_API_URL 另一个账户服务器,例如 自托管账户服务。
OPENBOT_DISPLAY_SIZE Computer Use 读取的虚拟显示器尺寸,格式为 WIDTHxHEIGHT,范围从 640x480 到 2576x2576。默认值为 1280x800。无效的值会在 docker logs 中写入一条警告,并且容器会以 1280x800 启动。何时使用 1920x1080,请参见 显示尺寸。docker/instances/compose.yaml 不会传递该变量,因此实例会使用 1280x800。

安全

  • Electron 沙箱保持开启。 seccomp.json 是 Docker 的默认配置文件(moby/profiles),并额外增加一条规则:对 clone 和 unshare 的标志位不加限制。这让容器可以创建用户命名空间,从而让容器内的进程可以调用更多内核功能。对于不在自身用户命名空间内的进程,内核仍然拒绝提供其他命名空间。请勿改用 seccomp=unconfined、--privileged 或 --no-sandbox。
  • 不以 root 身份运行。 镜像以 UID 1000 运行。发布版本的文件由 root 所有,因此智能体无法更改它们。该发布版本没有 SUID 文件。no-new-privileges 可保持这种状态。
  • 智能体在容器内拥有完全访问权限。 它们可以运行命令、更改 /data 下的所有内容并使用网络。容器就是安全边界。请勿挂载 Docker socket、你的主目录或智能体不得更改的其他主机文件夹。
  • 检查镜像。 每个发布镜像都有构建来源证明:
gh attestation verify oci://ghcr.io/nightly-labs/openbot:<version> --repo nightly-labs/openbot

工作原理

发布工作流(.github/workflows/release.yml)会先核对 AppImage 与发布版校验和一致,然后在每种架构的 runner 上基于该发布版的 AppImage 构建镜像。docker/Dockerfile 将 AppImage 解包到 /opt/OpenBot/app,并安装该发布版中自托管服务器所需的软件包。工作流会启动和停止每个镜像各一次,并且仅在 GitHub Release 发布后才将镜像推送到 GHCR。

tini 是进程 1。它从发布版中启动 resources/hosting/openbot-container,该脚本会检查能否创建用户命名空间,启动 D-Bus、两个虚拟显示器和密钥环,然后以 OPENBOT_SERVER=1 身份变为 OpenBot。OpenBot 在 :0 上以 1280x800 显示其窗口。智能体在 :1 上工作,尺寸为 OPENBOT_DISPLAY_SIZE,脚本将其命名为 OPENBOT_AGENT_DISPLAY。Computer Use 和远程桌面使用 :1,因此不会显示 OpenBot 窗口。停止时,tini 向 OpenBot 发送信号并等待其结束。/opt/OpenBot/hosted/mode 为 container,因此 openbot 命令与控制套接字通信,而不使用 systemd。

限制

  • 远程桌面和 Computer Use 需要显示器。容器为智能体提供了虚拟显示器,但远程桌面和 Computer Use 未在其中测试。arm64 发布版没有远程桌面运行时。
  • 语音提示不可用:容器没有麦克风,Web 应用也没有语音提示。
  • Gemini 会在容器浏览器的 Google 页面上登录。所有者或管理员在应用中使用 Connect 打开该页面,并通过该页面的实时视图登录。这需要两端都带有 provider-host-sign-in-v1 的发布版。它尚未在容器中测试。
  • Bitwarden 和 Vaultwarden 使用 API 密钥和主密码(bitwarden-connector-v1)连接。容器必须通过 HTTPS 访问 vault 服务器。在运行多个实例的主机上,防火墙会阻止私有地址,因此无法使用本地网络上的 Vaultwarden 服务器。

未确认

  • 已在 Docker Desktop 4.45(Docker Engine 28.3,linux/arm64)上使用 0.29.0 AppImage 和此镜像的脚本进行了测试:在沙盒开启的情况下启动、openbot status、重启、在 kill 后于同一卷上新建容器、退出码为 0 的停止操作,以及缺少 seccomp 配置文件和用户无法写入卷时的提示消息。发布工作流也会启动每个镜像。

  • 未测试:在容器中使用 openbot login 登录、从应用远程使用容器、Podman、Kubernetes、rootless Docker,以及使用 AppArmor 限制用户命名空间的主机(如 Ubuntu 23.10 或更高版本)。在此类主机上,容器可能需要一个允许 userns 的 AppArmor 配置文件。

  • 主机网络:用户报告称,在本地网络和 Tailscale tailnet 上,它能使连接更快。此处未对此进行测试。在运行 Docker 29 的公共 Ubuntu 24.04 VPS 上,桥接网络的连接时间为 2.0 至 2.6 秒,主机网络的连接时间为 2.0 至 2.4 秒。

  • 入口点的两个显示使用 OpenBot 的替代品进行了测试::0 为 1280x800,:1 为显示尺寸。未测试 Computer Use 和 :1 上的远程桌面。

  • 未测试:在容器中仅具有 Workspace 访问权限的 Codex agent。Codex 在 bubblewrap 中运行其命令,bubblewrap 会在用户命名空间中挂载其根目录。seccomp 配置文件仅允许在 CAP_SYS_ADMIN 下执行 mount,并始终拒绝 pivot_root,而 docker-default AppArmor 配置文件拒绝 mount,因此这些命令可能会失败。具有完整访问权限的 agent 不会使用 bubblewrap。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot