本文记录一次完整的 OpenClaw 部署过程:
使用 Ubuntu Server 24.04,以 Docker Compose 管理 OpenClaw,将服务部署到一台内网主机,再通过另一台反向代理主机对外提供 HTTPS 访问。
文中的以下信息均已脱敏:
- 安装路径
- 内网 IP
- 对外域名
- 宿主机发布端口
但是部署逻辑、命令顺序、踩坑过程都保留了,照着改成自己的值即可复现。
一、部署目标
目标架构如下:
- OpenClaw 运行在一台 Ubuntu Server 主机上
- 使用 Docker Compose 管理
- 数据目录固定在自定义路径下
- 服务只监听一个宿主机端口
- 前面放一台独立的反向代理主机
- 外部通过 HTTPS 域名访问
- Gateway 只允许可信反代访问,避免被局域网其他机器直接绕过反代访问
二、环境信息
本文部署环境:
- 系统:Ubuntu Server 24.04
- Docker:Docker Engine + Docker Compose v2
- OpenClaw:官方仓库 Docker 方案
- 运行方式:Docker Compose
- 反向代理:另一台局域网主机负责 HTTPS 终止和 WebSocket 转发
脱敏后的示例变量如下:
INSTALL_DIR=/opt/docker/openclaw
CONFIG_DIR=/opt/docker/openclaw/data/config
WORKSPACE_DIR=/opt/docker/openclaw/data/workspaceHOST_IP=192.168.100.10
REVERSE_PROXY_IP=192.168.100.20
HOST_PORT=10443PUBLIC_DOMAIN=openclaw.example.com
实际部署时,把这些替换成自己的值。
三、最终拓扑
最终结构如下:
浏览器
↓ HTTPS / WSS
https://openclaw.example.com
↓
反向代理主机(REVERSE_PROXY_IP)
↓ 反代到
http://HOST_IP:HOST_PORT
↓
OpenClaw Gateway(Docker 容器内固定监听 18789)
四、为什么选择 Docker Compose
OpenClaw 官方本身就提供 Docker 方式,适合以下场景:
- 希望安装与宿主机环境隔离
- 不想在宿主机直接安装整套运行依赖
- 后续只想通过
docker compose up / stop / restart管理 - 想明确掌控数据目录与容器生命周期
对我来说,Docker Compose 的优势主要在于:
- 安装目录固定
- 数据可持久化
- 重建容器不会丢配置
- 后续维护简单
五、准备工作
先安装基础组件:
apt update
apt install -y git curl screen
如果系统尚未安装 Docker 和 Docker Compose,需要先安装它们。
六、拉取 OpenClaw 仓库
mkdir -p /opt/docker
cd /opt/docker
git clone https://github.com/openclaw/openclaw.git
cd /opt/docker/openclaw
七、创建自定义数据目录
mkdir -p data/config data/workspace
这里的目的很明确:
不要把配置散落在默认目录,而是明确固定在安装路径下。
八、准备 .env
先写一份 .env:
cat > .env <<'EOF'
OPENCLAW_CONFIG_DIR=/opt/docker/openclaw/data/config
OPENCLAW_WORKSPACE_DIR=/opt/docker/openclaw/data/workspace
OPENCLAW_GATEWAY_PORT=10443
OPENCLAW_GATEWAY_BIND=lan
OPENCLAW_TZ=Asia/Tokyo
EOF
这里有一个非常重要的坑,后面会专门讲:
只提前写
.env还不够。
官方setup.sh运行时,会优先读取当前 shell 环境变量,再把值回写到.env。
如果你只是手工写了.env,却没有在执行脚本时传入环境变量,脚本仍可能按默认值运行,并覆盖你写的内容。
九、删除 bridge 端口映射,只保留 Gateway 端口
官方当前 docker-compose.yml 里通常还会保留一个历史 bridge 端口映射,但当前版本实际上已经不再使用 TCP bridge。
所以可以直接删掉那一行,只保留 Gateway 端口:
sed -i '/OPENCLAW_BRIDGE_PORT/d' docker-compose.yml
确认结果:
grep -n 'OPENCLAW_GATEWAY_PORT\|OPENCLAW_BRIDGE_PORT' docker-compose.yml
正常情况下应只剩下一行:
"${OPENCLAW_GATEWAY_PORT:-18789}:18789"
这行没有问题。它的含义是:
- 左边:宿主机端口
- 右边:容器内端口
- 如果环境变量
OPENCLAW_GATEWAY_PORT有值,就用它 - 如果没有,就回退到默认
18789
例如:
10443:18789
表示宿主机监听 10443,容器内部仍监听 18789。
十、正确执行安装脚本
错误思路
很多人会这样做:
./scripts/docker/setup.sh
这样做不一定会吃到你手工写进去的 .env 端口值。
正确思路
应该在执行安装脚本时,直接把环境变量传进去:
OPENCLAW_CONFIG_DIR=/opt/docker/openclaw/data/config \
OPENCLAW_WORKSPACE_DIR=/opt/docker/openclaw/data/workspace \
OPENCLAW_GATEWAY_PORT=10443 \
OPENCLAW_GATEWAY_BIND=lan \
OPENCLAW_TZ=Asia/Tokyo \
./scripts/docker/setup.sh
这一步非常关键。
因为官方脚本会把当前环境变量当作来源,再写回 .env。
十一、安装过程中的选择建议
执行 ./scripts/docker/setup.sh 后,会进入一个交互式流程。
1. 是否继续(个人使用 / 多用户提示)
选:
Yes
因为这里本质是在确认你知道它默认更偏向个人使用场景。
2. Setup mode
选:
QuickStart
原因:
- 先把服务装起来
- 后续再补配置
- 不需要一开始把所有功能都配满
3. Model/auth provider
如果只是先把网关跑通,可以选:
Skip for now
因为 provider 后面可以再配,不影响 Docker 主服务先启动。
4. Filter models by provider
选:
All providers
这一步只是模型筛选器,不是必须项。
5. Search provider
选:
Skip for now
Web Search 后面单独配置即可,不影响当前安装。
6. Configure skills now?
选:
No
先跑通服务,不在 onboarding 阶段展开额外依赖。
7. Hooks
选:
Skip for now
这些都不是基础启动必须项。
十二、一个容易误判的点:/app 和 /home/node/.openclaw
安装日志中会看到类似:
WORKDIR /app
COPY package.json ...
RUN pnpm ...
这不是路径异常。
/app 是什么?
这是Docker 镜像构建阶段的工作目录。
它是容器镜像内部的构建目录,不是宿主机安装路径。
/home/node/.openclaw 是什么?
这是容器运行时的配置目录。
OpenClaw 会把宿主机的自定义目录挂载到这里。
所以这两类路径完全不是一回事:
/app:构建镜像时用/home/node/.openclaw:运行服务时用
十三、SSH 断线怎么办
第一次构建很慢,中途如果 SSH 断开,前台构建通常就中断了。
所以强烈建议在 screen 或 tmux 中执行。
用 screen
启动:
screen -S openclaw
在里面执行安装:
cd /opt/docker/openclaw
OPENCLAW_CONFIG_DIR=/opt/docker/openclaw/data/config \
OPENCLAW_WORKSPACE_DIR=/opt/docker/openclaw/data/workspace \
OPENCLAW_GATEWAY_PORT=10443 \
OPENCLAW_GATEWAY_BIND=lan \
OPENCLAW_TZ=Asia/Tokyo \
./scripts/docker/setup.sh
临时离开但不中断任务:
Ctrl + A,然后按 D
重新接回:
screen -r openclaw
如果只是想共享查看:
screen -x openclaw
区别:
screen -r:接回会话screen -x:共享附加进去
十四、安装完成后的关键输出
安装成功后,一般会看到类似:
- Gateway 已启动
- 生成了 token
- 提示 dashboard 地址
- 显示
docker compose logs -f openclaw-gateway - 显示 health 检查命令
此时不要急着结束,还需要继续做一件事:
检查实际发布的宿主机端口是不是你想要的那个。
十五、如果安装后端口仍然是默认值
可能会出现这种情况:
docker compose ps
输出类似:
0.0.0.0:18789->18789/tcp
这说明脚本还是按默认值跑了,没有用你想要的宿主机端口。
修正方法
先改 .env:
sed -i 's/^OPENCLAW_GATEWAY_PORT=.*/OPENCLAW_GATEWAY_PORT=10443/' .env
grep '^OPENCLAW_GATEWAY_PORT=' .env
然后强制重建容器:
docker compose up -d --force-recreate openclaw-gateway
再检查:
docker compose ps
正常结果应类似:
0.0.0.0:10443->18789/tcp
十六、健康检查
确认服务是否真正起来:
curl -fsS http://127.0.0.1:10443/healthz
curl -fsS http://127.0.0.1:10443/readyz
正常返回:
{"ok":true,"status":"live"}
{"ready":true}
这时就说明:
- 端口正确
- 容器已运行
- Gateway 已就绪
十七、配置反代相关参数
如果你的 OpenClaw 不只是本机访问,而是要通过反向代理给域名提供服务,那么必须补以下配置:
gateway.mode=localgateway.bind=langateway.controlUi.allowedOriginsgateway.trustedProxies
示例:
docker compose run --rm openclaw-cli config set --batch-json '[
{"path":"gateway.mode","value":"local"},
{"path":"gateway.bind","value":"lan"},
{"path":"gateway.controlUi.allowedOrigins","value":["https://openclaw.example.com","http://192.168.100.10:10443","http://127.0.0.1:10443"]},
{"path":"gateway.trustedProxies","value":["192.168.100.20"]}
]'
然后重启:
docker compose restart openclaw-gateway
十八、为什么要配 allowedOrigins 和 trustedProxies
这不是可有可无。
gateway.controlUi.allowedOrigins
用于限制允许访问 Control UI 的来源。
如果你要通过浏览器从域名访问,就必须把自己的 HTTPS 域名放进去。
gateway.trustedProxies
当前面还有反向代理时,要告诉 OpenClaw:
哪台代理是可信的。
这样它才会正确处理:
X-Forwarded-ForX-Forwarded-Proto
十九、反向代理需要满足哪些条件
反向代理至少应满足以下条件:
- 反代到
http://HOST_IP:HOST_PORT - 支持 WebSocket upgrade
- 正确传递并覆盖
X-Forwarded-For - 正确传递
X-Forwarded-Proto - 对外只开放域名入口
- 宿主机网关端口不应被其他来源直接访问
示例 Nginx 配置核心段:
location / {
proxy_pass http://192.168.100.10:10443;
proxy_http_version 1.1; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
二十、防火墙建议
最稳的策略是:
- 只允许反代主机访问 OpenClaw 主机的宿主机端口
- 其他来源全部拒绝
例如用 UFW:
ufw allow from 192.168.100.20 to any port 10443 proto tcp
ufw deny 10443/tcp
ufw reload
ufw status numbered
二十一、第一次网页登录为什么会失败
第一次打开 https://openclaw.example.com 后,可能会看到连接失败。
常见原因有两个:
1. 没填 token
Web 页面里:
WebSocket URL应填:wss://openclaw.example.comGateway Token填安装时生成的 tokenPassword留空
如果忘了 token:
grep '^OPENCLAW_GATEWAY_TOKEN=' .env
2. pairing required
这是第一次浏览器设备接入时常见情况,不是故障。
先查看待批准设备:
docker compose run --rm openclaw-cli devices list
会看到类似:
Pending (1)- 一个
Request ID
然后批准:
docker compose run --rm openclaw-cli devices approve <request-id>
批准后回到网页刷新,再连接即可。
二十二、设备配对记录说明
OpenClaw 不是传统的“账号系统”,更接近:
- 一个 Gateway
- 一组已配对设备
- 每个设备有角色和 scopes
这意味着:
- 同一浏览器、同一设备后续通常可以直接继续用
- 换浏览器、清缓存、换设备,可能会出现新的 pairing 请求
- 旧的设备记录可以删除
二十三、删除多余设备记录
查看设备:
docker compose run --rm openclaw-cli devices list
删除某个设备:
docker compose run --rm openclaw-cli devices remove <device-id>
需要注意的是:
- 有些记录可能是初始化阶段生成的内部配对记录
- 有些是你实际网页登录时产生的
- 如果一个设备正在活跃连接,删掉后它可能再次出现
所以做法很简单:
- 先确认哪个是当前有效设备
- 再删掉多余设备
- 重新
devices list检查结果
二十四、安装是否成功的判断标准
我认为判断是否真正完成,不看某一条日志,而看以下几项是否全部满足:
1. 容器状态正常
docker compose ps
看到:
healthy
2. 健康检查通过
curl -fsS http://127.0.0.1:10443/healthz
curl -fsS http://127.0.0.1:10443/readyz
3. 宿主机端口正确
看到:
0.0.0.0:10443->18789/tcp
4. 域名网页可以打开
浏览器可访问:
https://openclaw.example.com
5. WebSocket 正常连接
页面状态显示 OK,不再报 token 或 pairing 错误。
二十五、日常管理命令
后续真正常用的命令只有这些:
cd /opt/docker/openclawdocker compose up -d openclaw-gateway
docker compose stop openclaw-gateway
docker compose restart openclaw-gateway
docker compose logs -f openclaw-gateway
健康检查:
curl -fsS http://127.0.0.1:10443/healthz
curl -fsS http://127.0.0.1:10443/readyz
查看设备:
docker compose run --rm openclaw-cli devices list
批准新设备:
docker compose run --rm openclaw-cli devices approve <request-id>
删除旧设备:
docker compose run --rm openclaw-cli devices remove <device-id>
二十六、这次部署中最关键的几个坑
这次实际部署里,最容易踩坑的点有这些:
1. 以为写 .env 就够了
不够。setup.sh 运行时优先看当前环境变量,再回写 .env。
所以正确方式是:
在执行
setup.sh时就把变量传进去。
2. 看到 /app 以为路径错了
不是。
那只是构建镜像阶段的工作目录。
3. SSH 断线导致前台构建中断
长时间构建一定要放到 screen 或 tmux 里跑。
4. 安装后端口还是默认值
这通常不是 OpenClaw 没启动,而是容器仍按默认端口发布。
改 .env 后强制重建容器即可。
5. 域名能打开但 Connect 报错
多数情况不是服务坏了,而是:
- token 没填
- pairing 还没批准
6. 设备列表里出现看不懂的已配对设备
有些是初始化阶段自动生成的,不一定是异常登录。
二十七、结论
这次部署最终达成了这些目标:
- OpenClaw 跑在 Ubuntu Server 上
- 使用 Docker Compose 管理
- 数据目录固定在自定义路径
- 宿主机只发布一个网关端口
- 通过反代主机对外提供 HTTPS / WSS
- 配置了可信反代与允许来源
- Control UI 可正常登录
- 设备配对机制正常
- 日常维护只需要极少量命令
对我来说,这种部署方式的优点很明显:
- 路径清晰
- 结构清晰
- 维护成本低
- 不依赖宿主机额外的复杂运行环境
- 后续扩展或迁移也更容易
如果只是个人使用,这套方案已经足够稳定。
二十八、附:一套最短可复用流程
最后把最核心的步骤压缩成一版:
mkdir -p /opt/docker
cd /opt/docker
git clone https://github.com/openclaw/openclaw.git
cd /opt/docker/openclawmkdir -p data/config data/workspacesed -i '/OPENCLAW_BRIDGE_PORT/d' docker-compose.ymlOPENCLAW_CONFIG_DIR=/opt/docker/openclaw/data/config \
OPENCLAW_WORKSPACE_DIR=/opt/docker/openclaw/data/workspace \
OPENCLAW_GATEWAY_PORT=10443 \
OPENCLAW_GATEWAY_BIND=lan \
OPENCLAW_TZ=Asia/Tokyo \
./scripts/docker/setup.shdocker compose run --rm openclaw-cli config set --batch-json '[
{"path":"gateway.mode","value":"local"},
{"path":"gateway.bind","value":"lan"},
{"path":"gateway.controlUi.allowedOrigins","value":["https://openclaw.example.com","http://192.168.100.10:10443","http://127.0.0.1:10443"]},
{"path":"gateway.trustedProxies","value":["192.168.100.20"]}
]'docker compose restart openclaw-gatewaycurl -fsS http://127.0.0.1:10443/healthz
curl -fsS http://127.0.0.1:10443/readyz