Cloudflare Containers
在 Cloudflare Worker 和一个命名的 Durable Object 后面运行一个 OpenClaw 安装实例,使用官方 OpenClaw 镜像并将 Litestream 复制到 R2。
Warning
此部署目标处于实验阶段。Litestream 保护的是 SQLite 数据库,而不是完整的 OpenClaw 状态目录。在使用生产凭据之前,请阅读限制与恢复。
你需要什么¶
- 一个启用了 Workers、Containers 和 R2 的 Cloudflare 账户
- 支持
linux/amd64的 Docker Buildx - 用于派生镜像的公共 Docker Hub 仓库,以及一个可以推送到该仓库的
docker login会话 - 一个兼容 S3 的 CLI,例如 AWS CLI,稍后用于验证 Litestream 复制
- Node.js 和 npm
- 用于你的 OpenClaw 设置的 provider 和 channel 凭据
模板位于 scripts/cloudflare。它部署一个 standard-2 容器,并设置 max_instances: 1。
工作原理¶
Worker 将所有 HTTP 和 WebSocket 请求转发到一个稳定的 Durable Object 名称。该 Durable Object 拥有一个 Container 实例,并且是围绕 Litestream 副本的单写入者栅栏。Container 在 8080 端口暴露 OpenClaw,Durable Object 在路由之前会轮询该端口的 /healthz。
flowchart TD
client[Channels, browsers, API clients]
worker[Cloudflare Worker]
durable[Durable Object, one stable name]
container[Container running the OpenClaw Gateway on 8080]
litestream[Litestream sidecar process]
r2[(R2 bucket of SQLite replicas)]
client --> worker
worker --> durable
durable --> container
container --> litestream
litestream -- continuous WAL streaming --> r2
r2 -- restore on boot --> container
Litestream 监视两个 SQLite 根目录:
/home/node/.openclaw/state/*.sqlite/home/node/.openclaw/agents/**/*.sqlite
启动时,入口点使用 R2 的 S3 ListObjectsV2 API 作为恢复清单,拒绝这些根目录之外的路径,恢复每个发现的数据库,然后才启动 Gateway。
在此模板上对照真实 R2 存储桶测量:从写入到副本大约 2.4 秒,将两个数据库恢复到新 Container 大约 9 秒,该 Container 在启动后约 13 秒达到健康的 Gateway。请将这些视为数量级预期,而非保证。
部署¶
1. 准备模板
克隆 OpenClaw 并进入模板目录:
git clone https://github.com/openclaw/openclaw.git
cd openclaw/scripts/cloudflare
npm install
npx wrangler login
npx wrangler whoami
在创建资源之前,确认 Wrangler 选择了预期的 Cloudflare 账户。
2. 创建 R2 存储
创建存储桶:
在 Cloudflare 仪表盘中,创建一个 R2 API 令牌,其对象读写权限仅限于该存储桶。请勿将访问密钥 ID 和秘密访问密钥提交到克隆仓库中。
在 wrangler.jsonc 中,替换端点中的 <account-id>。如果你使用其他存储桶名称,请同时更新 LITESTREAM_BUCKET 和 r2_buckets[].bucket_name。
R2 绑定用于 Worker 端的访问和文档完整性。Litestream 无法在 Container 内部使用 Worker 绑定;它使用通过 Worker secrets 传递的 R2 S3 端点和凭据。
3. 发布 Container 镜像
将 Dockerfile 中的 <official-openclaw-image-digest> 替换为官方 openclaw/openclaw Docker Hub 仓库中的不可变摘要(digest)。
为 Cloudflare 要求的架构构建派生镜像,并将其推送到公共 Docker Hub 仓库:
docker buildx build \
--platform linux/amd64 \
--tag docker.io/<docker-hub-user>/openclaw-cloudflare:<version> \
--push \
.
docker buildx imagetools inspect \
docker.io/<docker-hub-user>/openclaw-cloudflare:<version>
将 wrangler.jsonc 中的 containers[].image 占位符替换为生成的不可变 docker.io/...@sha256:... 引用。Cloudflare Containers 可以直接拉取公共 Docker Hub 镜像;此模板不支持 GHCR 作为来源。
4. 部署 Worker 和 Container
编译 Worker 并部署:
首次部署会创建 Worker、由 SQLite 支持的 Durable Object 类、Container 应用以及 R2 绑定。
5. 设置运行时 secrets
通过 Wrangler 的 secret 提示添加 R2 和 Gateway 凭据:
npx wrangler secret put LITESTREAM_ACCESS_KEY_ID
npx wrangler secret put LITESTREAM_SECRET_ACCESS_KEY
npx wrangler secret put OPENCLAW_GATEWAY_TOKEN
根据需要添加 provider 和 channel 变量。例如:
src/container.ts 会向 Container 传递一个明确的环境变量允许列表。在使用其他基于环境的凭据之前,请先在该文件中添加相应的名称。
6. 引导 OpenClaw
首次启动需要在 Container 内进行一个交互式会话。SSH 访问默认禁用;请暂时在 wrangler.jsonc 的容器条目中添加以下内容来启用它,然后重新部署:
打开已部署的 Worker URL 一次以启动实例。然后找到 application 和 instance ID 并连接:
npx wrangler containers list
npx wrangler containers instances <application-id> --json
npx wrangler containers ssh <instance-id>
SSH 通过 wrangler 进行中介,并且仅限于具有容器写访问权限的账户。引导完成后,你可以移除 ssh 块并重新部署;通过 Litestream 恢复的状态会在替换后保留。
在 Container 内部,运行基于 SecretRef 的设置。以下示例使用 OpenAI 和 Telegram:
cd /app
node openclaw.mjs onboard --non-interactive --accept-risk --skip-health \
--mode local \
--auth-choice openai-api-key \
--secret-input-mode ref \
--gateway-auth token \
--gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
--skip-channels \
--no-install-daemon
node openclaw.mjs channels add --channel telegram --use-env
node openclaw.mjs doctor --json
将你确切的引导配方保存在一份私有的、可复现的运行手册中。全新的 Container 磁盘不会保留已生成的配置。
验证部署¶
在首次引导之后、依赖此部署之前,运行以下检查。
确认 Gateway 能够响应。/healthz 报告监听器已启动。/startupz 额外报告启动工作已完成,同时忽略通道健康状况,因此当某个通道账户异常时它仍保持绿色;该端点仅由 v2026.8.1 或更新版本构建的镜像提供:
curl -sS https://<worker-subdomain>.workers.dev/healthz
curl -sS -H "Authorization: Bearer $OPENCLAW_GATEWAY_TOKEN" \
https://<worker-subdomain>.workers.dev/readyz
确认复制确实到达了 R2。Litestream 将键写入 replicas/state/<database>/<generation>/ 前缀下,因此请使用你提供给 Litestream 的同一套 R2 凭据,通过任何兼容 S3 的客户端列出该前缀。Wrangler 无法列出对象键,只能按精确路径获取:
aws s3 ls "s3://openclaw-backups/replicas/" --recursive \
--endpoint-url "https://<account-id>.r2.cloudflarestorage.com"
Cloudflare 控制台的 R2 对象浏览器会显示相同的目录树。在几分钟活动后如果前缀仍为空,说明复制未生效;请先修复再继续。
在真正需要恢复之前先进行演练。未经测试的恢复路径不能算是备份:
- 发送一条消息,让 Gateway 写入一个会话行。
- 等待约十秒钟以完成复制。
- 删除 Container 实例,或重新部署以强制替换。
- 重新打开 Worker URL,确认对话仍然存在。
如果第 4 步丢失了数据,请在连接生产通道之前停下来修复复制。
成本与规格¶
Containers 要求使用 Workers Paid 套餐。只要 Container 处于唤醒状态,内存和磁盘就按实例类型预置的资源计费;CPU 仅按实际使用量计费。
默认的 standard-2 实例预置 1 个 vCPU、6 GiB 内存和 12 GB 磁盘。因此,让它整月全天候运行的成本主要由预置内存决定,而不是由 agent 的繁忙程度决定。按公布的价格计算,每月大约为 40 至 50 美元(含套餐费用),其中大部分是内存费用,未计入出口流量。
这对下面的生命周期决策很重要:
- Socket 通道会让 Container 保持唤醒,因此它们按全天候运行费率计费。一台小型的常驻虚拟机往往更便宜。这里选择 Cloudflare 是出于其运维模式、与其他 Cloudflare 服务共置,或 R2 持久化路径的考虑,而不是为了省钱。
- 仅使用 Webhook 的安装会休眠,休眠的 Container 不计费。这才是该目标真正便宜的适用场景。
在最终确定之前,请查看 Cloudflare 的 Containers 定价页面 以核实当前费率;这些数字是根据已发布的费率表估算的,并且会独立于 OpenClaw 发生变化。
可观测性¶
在复现问题期间,流式查看 Worker 和 Container 日志:
npx wrangler tail
npx wrangler containers list
npx wrangler containers instances <application-id> --json
Gateway 日志保留在 Container 内部。可以通过引导步骤中所述的临时 SSH 会话访问这些日志,或将它们转发到你自己的收集器。Container 文件系统是临时的,因此请将 Container 内的日志视为调试输出,而不是持久记录。
选择生命周期模式¶
OPENCLAW_WEBHOOK_ONLY 默认为 false,这会使 Container 在空闲期间保持运行。对于维护 socket 或长期运行进程的通道,请保留此默认值,包括:
- Discord
- Slack Socket Mode
仅当每个已启用的通道都通过 HTTP webhook 接收流量时,才将 OPENCLAW_WEBHOOK_ONLY 设置为 true。在该模式下,Container 会在空闲十分钟后停止,并在下一次请求时冷启动。
Warning
缩放到零从全新磁盘开始。仅当外部进程能够重新应用你的声明式引导时才启用它。Litestream 可以恢复 SQLite,但无法重新创建 openclaw.json、凭据文件、已安装的插件或工作区。
限制与恢复¶
- 单一写入者: 每个请求都会解析到同一个 Durable Object 名称,Cloudflare 会为该名称运行一个活动的 Durable Object 实例。不要增加
max_instances,也不要引入绕过此限制的备用路由。在平台替换或滚动发布期间,新旧 Container 的短暂重叠是可接受的实验性权衡。 - 恢复点: 一秒的 Litestream 同步间隔通常会产生秒级 RPO。它不是同步复制,突然终止可能会丢失尚未到达 R2 的写入。
- 临时磁盘: 每次休眠、替换或主机重启都会从镜像加上恢复的 SQLite 数据库开始。对于配置、凭据文件、插件文件和工作区,请使用 完整的 OpenClaw 归档。
- 回滚: 较旧的数据库字节相当于时间旅行。逐步升级的通道凭据(尤其是 WhatsApp)可能会失去同步;审批以及投递/去重状态也会回滚。在恢复运行之前,请重新关联受影响的通道并审查待处理的审批。参见 恢复。
- WebSocket: Worker 和 Container 代理支持 WebSocket。Cloudflare 将接收到的每条 WebSocket 消息限制为 32 MiB。
- 出口流量: 出站请求使用共享的 Cloudflare IP 地址空间。该目标不提供固定的出口地址。
- 提供方边界: 这是一个部署模板,而不是 OpenClaw 的
cloudWorkersprovider。其运维 SSH 访问并未实现该 provider 的 SSH 执行契约。
更新¶
基于新的不可变官方 OpenClaw 摘要构建新的派生镜像,推送它,更新 wrangler.jsonc 中的派生摘要,然后部署:
先在单独的 R2 bucket 上测试更新和回滚。在启用较旧的字节之前,先保留当前状态。
故障排查¶
Worker 返回 5xx 且 Container 始终未就绪 -- Cloudflare 只运行从公共 registry 拉取的 linux/amd64 镜像。请使用 --platform linux/amd64 重新构建,确认派生的 Docker Hub 仓库是公共的,并确认 containers[].image 使用已推送的 digest,而不是浮动标签。
部署成功但每个请求都超时 -- Container 辅助程序等待 GET /healthz。检查 Container 内的 Gateway 是否在端口 8080 上监听,并确认没有任何引导步骤修改端口。
探针通过但 Gateway 实际上并未提供服务 -- Control UI 会对未知路径返回通配 200,因此探测你的镜像未提供服务的路由看起来会一直健康。在信任探针之前,请验证响应体是 JSON 而不是 HTML。
Litestream 记录认证或签名错误 -- Litestream 需要 R2 S3 API 凭据,这与 Cloudflare API token 不同。创建一个 R2 API token,并使用其 access key ID 和 secret access key,同时确认 LITESTREAM_ENDPOINT 包含你的账户 ID。
首次启动日志显示没有要恢复的数据库 -- 在空存储桶上属预期情况。入口点将空副本列表视为全新安装,并正常启动 Gateway。
/readyz 返回 503 而 /startupz 返回 200 -- 这是设计使然。启动已完成,但某个已配置的频道账户不健康。检查频道状态,而不是重启 Container;参见 健康检查。
wrangler containers ssh 被拒绝 -- SSH 默认禁用。将 "ssh": { "enabled": true } 添加到容器条目中,重新部署,然后连接。
睡眠或重新部署后配置消失 -- Litestream 只恢复 SQLite 数据库。openclaw.json、凭据文件、已安装的插件文件和工作区位于临时磁盘上。重新应用你的引导运行手册,或让安装保持始终在线并创建 完整归档。
恢复后频道会话中断 -- 恢复较旧的数据会将棘轮凭据回滚。重新关联受影响的频道并查看待处理的审批;参见 限制与恢复。
WebSocket 连接在大型载荷下关闭 -- 当接收到的 WebSocket 消息超过 32 MiB 时,Cloudflare 会关闭连接。减小附件大小,或通过带外方式传输它们。
相关¶
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw