用 Docker Compose + Caddy 在 VPS 上部署 OpenClaw
一、准备
环境假设:
- Ubuntu / Debian VPS
- 已安装 Docker 和 Docker Compose v2
- 宿主机已安装 Caddy
- 域名 A/AAAA 记录已指向 VPS,80/443 已放行
目录:
mkdir -p ~/openclaw
cd ~/openclaw二、docker-compose.yml
services:
openclaw-gateway:
image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest}
container_name: openclaw-gateway
env_file: .env
environment:
HOME: /home/node
OPENCLAW_HOME: /home/node
TERM: xterm-256color
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
OPENCLAW_GATEWAY_PORT: "18789"
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
TZ: ${OPENCLAW_TZ:-Asia/Shanghai}
volumes:
- ./data/config:/home/node/.openclaw
- ./data/workspace:/home/node/.openclaw/workspace
- ./data/auth-profile-secrets:/home/node/.config/openclaw
ports:
- "127.0.0.1:18789:18789"
cap_drop:
- NET_RAW
- NET_ADMIN
security_opt:
- no-new-privileges:true
extra_hosts:
- "host.docker.internal:host-gateway"
init: true
restart: unless-stopped
command:
- node
- dist/index.js
- gateway
- --bind
- lan
- --port
- "18789"
healthcheck:
test: ["CMD", "node", "dist/docker-healthcheck.js"]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
openclaw-cli:
profiles: ["cli"]
image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest}
network_mode: "service:openclaw-gateway"
env_file: .env
environment:
HOME: /home/node
OPENCLAW_HOME: /home/node
TERM: xterm-256color
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_CONFIG_DIR: /home/node/.openclaw
OPENCLAW_WORKSPACE_DIR: /home/node/.openclaw/workspace
OPENCLAW_GATEWAY_PORT: "18789"
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
TZ: ${OPENCLAW_TZ:-Asia/Shanghai}
BROWSER: echo
volumes:
- ./data/config:/home/node/.openclaw
- ./data/workspace:/home/node/.openclaw/workspace
- ./data/auth-profile-secrets:/home/node/.config/openclaw
stdin_open: true
tty: true
init: true
entrypoint: ["node", "dist/index.js"]
depends_on:
- openclaw-gateway几个刻意的选择:
ports写成127.0.0.1:18789:18789,不是18789:18789- 容器内
--bind lan,否则 Docker 端口映射进不来 openclaw-cli用profiles收起,平时不启动cap_drop和no-new-privileges沿用官方 compose 的加固
三、.env
DOMAIN=openclaw.example.com
OPENCLAW_GATEWAY_TOKEN=用 openssl rand -hex 32 生成
OPENCLAW_IMAGE=ghcr.io/openclaw/openclaw:latest
OPENCLAW_TZ=Asia/Shanghai
# 可选
#ANTHROPIC_API_KEY=sk-ant-xxxx生成 Token:
openssl rand -hex 32数据目录权限:
mkdir -p data/config data/workspace data/auth-profile-secrets
sudo chown -R 1000:1000 data容器内以 node 用户运行,通常是 UID 1000。权限不对时,容器会以 EACCES 反复重启。
四、初始化
拉镜像:
docker compose pull非交互式 onboarding,以 Anthropic 为例:
set -a; . ./.env; set +adocker compose run --rm --no-deps --entrypoint node openclaw-gateway dist/index.js onboard \
--non-interactive --accept-risk --skip-health --mode local \
--auth-choice apiKey \
--anthropic-api-key "$ANTHROPIC_API_KEY" \
--secret-input-mode ref \
--gateway-auth token \
--gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
--skip-channels --no-install-daemon如果暂时不配置模型,把 provider 参数换成:
--auth-choice skip然后写入公网地址和代理信任:
docker compose run --rm --no-deps --entrypoint node openclaw-gateway dist/index.js config set --batch-json "$(printf '[{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.publicOrigin","value":"https://%s"},{"path":"gateway.controlUi.allowedOrigins","value":["https://%s"]},{"path":"gateway.trustedProxies","value":["127.0.0.1","::1","172.16.0.0/12"]}]' "$DOMAIN" "$DOMAIN")"trustedProxies 这里用 172.16.0.0/12 覆盖 Docker 默认私网段。因为 18789 只绑回环,这个范围是可控的;想更严格就换成实际网桥网关地址。
启动:
docker compose up -dcurl -fsS http://127.0.0.1:18789/healthz五、Caddy 配置
标准情况:Caddy 直接监听 443
openclaw.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:18789 {
flush_interval -1
}
}新的情况:443 → 8443 的 layer4 端口复用
Caddy 全局用了 https_port 8443,再在 :443 上用 layer4 按 SNI 分流:Reality、Naive 各走各的,剩下的 HTTPS 流量转发到本机 8443。
关键配置:
{
http_port 80
https_port 8443
servers {
listener_wrappers {
proxy_protocol {
timeout 5s
allow 127.0.0.1/32 ::1/128
fallback_policy ignore
}
tls
}
}
layer4 {
:443 {
@reality tls sni reality.example.com
route @reality {
proxy 127.0.0.1:10118
}
@naive tls sni naive.example.com
route @naive {
proxy 127.0.0.1:10115
}
route {
proxy 127.0.0.1:8443 {
proxy_protocol v2
}
}
}
}
}
:80 {
root * /usr/share/caddy
file_server
}
openclaw.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:18789 {
flush_interval -1
}
}校验并重载:
sudo caddy validate --config /etc/caddy/Caddyfilesudo systemctl reload caddy六、首次登录
打开 https://openclaw.example.com/,在 Settings 里粘贴 .env 中的 OPENCLAW_GATEWAY_TOKEN。
如果页面提示等待设备批准:
cd ~/openclaw && docker compose exec openclaw-gateway node dist/index.js devices listcd ~/openclaw && docker compose exec openclaw-gateway node dist/index.js devices approve <requestId>注意:宿主机上没有 openclaw 命令是正常的,CLI 装在容器里。所有命令都要通过 docker compose exec / docker compose run 执行。
七、安全与维护
- 18789 始终只绑
127.0.0.1 - 容器保留
cap_drop和no-new-privileges OPENCLAW_GATEWAY_TOKEN用 64 位十六进制随机串- 生产环境固定镜像版本,不要长期用
latest - 定期备份
data/、.env和 Caddyfile
升级:
cd ~/openclaw && docker compose pull && docker compose up -d备份:
cd ~/openclaw && tar czf openclaw-backup-$(date +%F).tar.gz data .env docker-compose.yml