#OpenClaw #Docker #AI Agent #自托管 #服务器运维

背景

在 N100(Ubuntu, 16GB DDR5)上自托管 AI Agent 网关,目标架构:

  • OpenClaw:AI Agent 网关,负责消息路由与 Agent 编排,对接 Ollama(内网推理机)

  • Hermes Agent:自我进化型个人助手,自动写记忆与技能

  • 推理主机(i9+2070S 或 9800X3D+5080)运行 Ollama,局域网暴露 11434 端口

N100 已有 FRP 加密公网穿透,所有新服务均监听 127.0.0.1 仅限本机访问。


目录结构

1
2
3
4
5
6
7
8
9
10
~/dockerfile/
├── openclaw/
│ ├── docker-compose.yml
│ ├── .env
│ ├── openclaw.json ← 配置文件(JSON 格式)
│ └── data/ ← 容器持久化数据(root 所有)
└── hermes/
├── docker-compose.yml
├── .env
└── data/

第一步:创建 Docker 网络

OpenClaw 负责创建 agent_net 网络,Hermes 以 external: true 加入。

1
2
3
4
5
# openclaw/docker-compose.yml 中的 networks 段
networks:
agent_net:
name: agent_net
driver: bridge

第二步:部署 OpenClaw

最终有效的 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
openclaw:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw
user: root # 关键:必须以 root 运行
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./data:/root/.openclaw # 持久化数据目录
- ./openclaw.json:/root/.openclaw/openclaw.json # 配置文件单独挂载
env_file:
- .env
networks:
- agent_net

networks:
agent_net:
name: agent_net
driver: bridge

.env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NODE_ENV=production

# ── Gateway 鉴权 Token(客户端连接时需携带)
OPENCLAW_GATEWAY_TOKEN=<your-gateway-token>

# ── Gateway 模式(部分旧版读取,实际以 openclaw.json 为准)
OPENCLAW_GATEWAY_MODE=local
OPENCLAW_ALLOW_UNCONFIGURED=true

# ── Secret
OPENCLAW_SECRET=<your-secret>

# ── LLM(下列 env var OpenClaw 实际不读,配置见 openclaw.json)
# OPENCLAW_LLM_PROVIDER=ollama
# OPENCLAW_LLM_API_URL=http://192.168.0.XXX:11434
# OPENCLAW_LLM_MODEL=qwen3.6:27b-q4_k_m

openclaw.json(正式配置文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"gateway": {
"mode": "local",
"bind": "loopback"
},
"models": {
"providers": {
"ollama": {
"baseUrl": "http://192.168.0.XXX:11434"
},
"anthropic": {
"apiKey": "FILL_IN_API_KEY"
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "ollama/qwen3.6:27b-q4_k_m",
"fallbacks": ["anthropic/claude-sonnet-4-6"]
}
}
}
}

第三步:部署 Hermes Agent

OpenClaw 启动后 agent_net 已创建,Hermes 直接加入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# hermes/docker-compose.yml
services:
hermes-agent:
image: nousresearch/hermes-agent:latest
container_name: hermes-agent
restart: unless-stopped
ports:
- "127.0.0.1:8642:8642"
volumes:
- ./data:/home/node/.hermes
env_file:
- .env
networks:
- agent_net

networks:
agent_net:
external: true
1
2
3
4
5
6
# hermes/.env
HERMES_AUTH_TOKEN=<your-hermes-token>
OPENROUTER_API_KEY=FILL_IN_OPENROUTER_KEY
HERMES_AGENT_NAME=MiLin-Assistant
HERMES_MEMORY_AUTO_WRITE=true
HERMES_SKILL_AUTO_WRITE=true
1
cd ~/dockerfile/hermes && docker compose up -d

踩坑记录

坑 1:配置文件格式错误——YAML vs JSON

现象:容器反复重启,日志一直输出:

1
2
3
[gateway] loading configuration…
[gateway] resolving authentication…
Missing config. Run `openclaw setup` or set gateway.mode=local (or pass --allow-unconfigured).

错误尝试:按直觉写了 config.yaml,并在 .env 中设置 OPENCLAW_GATEWAY_MODE=localOPENCLAW_ALLOW_UNCONFIGURED=true,均无效。

根本原因:通过读取容器内 /app/dist/paths-DyelItkH.js 发现:

1
const CONFIG_FILENAME = "openclaw.json";   // JSON 格式,不是 YAML

配置文件路径为 ~/.openclaw/openclaw.json,OpenClaw 不读取 OPENCLAW_LLM_* 之类的 env var,环境变量只有 OPENCLAW_GATEWAY_TOKENOPENCLAW_STATE_DIROPENCLAW_CONFIG_PATH 少数几个被识别。

修复:将 config.yaml 改为 openclaw.json,并用文件级 volume 覆盖挂载:

1
2
3
volumes:
- ./data:/root/.openclaw
- ./openclaw.json:/root/.openclaw/openclaw.json # 文件挂载覆盖目录内的同名文件

坑 2:容器以非 root 用户运行,无法访问 /root/

现象openclaw.json 已正确挂载,容器内 cat /root/.openclaw/config.yaml 执行时:

1
ls: cannot access '/root/.openclaw/': Permission denied

根本原因:镜像默认以 node(uid=1000)用户运行。Linux 下 /root/ 目录权限为 700,非 root 用户无法进入,/root/.openclaw/ 自然也无法访问。

修复:在 docker-compose.yml 中加一行:

1
2
3
services:
openclaw:
user: root

坑 3:gateway.bind 容器模式下默认 0.0.0.0,拒绝无鉴权启动

现象:加了 user: root 后,配置文件能正常读取,但新报错:

1
2
3
Refusing to bind gateway to auto without auth.
Container environment detected — the gateway defaults to bind=auto (0.0.0.0) for port-forwarding compatibility.
Set OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD, or pass --token/--password *** start with auth.

根本原因:OpenClaw 检测到容器环境,将 bind 默认改为 auto(即 0.0.0.0),而绑定公共地址必须配置鉴权。

修复:在 openclaw.json 中显式设置绑定模式为 loopback,同时在 .env 中加入 Token 作为双保险:

1
2
3
4
5
6
{
"gateway": {
"mode": "local",
"bind": "loopback"
}
}
1
OPENCLAW_GATEWAY_TOKEN=<your-gateway-token>

坑 4:gateway.auth 字段类型错误

现象:尝试在 JSON 中设置 "gateway": { "auth": "token" } 时报:

1
2
Gateway failed to start: Invalid config at /root/.openclaw/openclaw.json.
gateway.auth: Invalid input

根本原因gateway.auth 是一个对象,不是字符串。正确格式为:

1
2
3
4
5
6
7
{
"gateway": {
"auth": {
"mode": "token"
}
}
}

实际处理:改用 gateway.bind: loopback 后不再需要显式配置 auth,问题绕过。


坑 5:LLM 默认模型为 openai/gpt-5.5

现象:启动日志显示 agent model: openai/gpt-5.5,不是我们期望的 Ollama 模型。

根本原因.env 中的 OPENCLAW_LLM_* 环境变量 OpenClaw 根本不识别,LLM 配置必须写在 openclaw.json 里。

通过 node openclaw.mjs config schema 查看 JSON Schema,模型配置结构为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"models": {
"providers": {
"ollama": { "baseUrl": "http://HOST:11434" },
"anthropic": { "apiKey": "sk-ant-..." }
}
},
"agents": {
"defaults": {
"model": {
"primary": "ollama/MODEL_NAME",
"fallbacks": ["anthropic/claude-sonnet-4-6"]
}
}
}
}

修复后日志确认:

1
2
3
4
5
[gateway] auto-enabled plugins for this runtime without writing config:
- ollama/qwen3.6:27b-q4_k_m model configured, enabled automatically.
- anthropic/claude-sonnet-4-6 model configured, enabled automatically.
[gateway] agent model: ollama/qwen3.6:27b-q4_k_m (thinking=medium, fast=off)
[gateway] ready

NanoClaw 的误解澄清

原计划用 NanoClaw 作为公网安全入口层(代理 OpenClaw),但实际上:

  • NanoClaw 是独立的个人 Claude 助手(同 OpenClaw 是平行竞品关系),不是代理层

  • 无预构建 Docker 镜像,需 bash nanoclaw.sh 交互式安装

  • NANOCLAW_UPSTREAM_URL 之类的 env var 是我们凭空假设的,NanoClaw 并不支持

公网访问替代方案:OpenClaw 本身已有 Token 鉴权,直接调整绑定模式即可:

  1. openclaw.jsongateway.bind 改为 lan

  2. docker-compose.yml 端口改为 0.0.0.0:3000:3000

  3. FRP 增加 local_port=3000 隧道


最终运行状态

1
2
3
4
$ docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES STATUS PORTS
openclaw Up 2 minutes (healthy) 127.0.0.1:3000->3000/tcp
hermes-agent Up 7 minutes 127.0.0.1:8642->8642/tcp

待完成事项

填入 Ollama 主力机 IP

找到推理机(i9+2070S 或 9800X3D)的局域网 IP 后,更新配置:

1
2
ssh MyUbuntuLocal "docker exec openclaw node openclaw.mjs config set \
models.providers.ollama.baseUrl http://192.168.0.YYY:11434"

同时 Win11 推理机需设置 Ollama 监听所有接口:

1
2
3
# 环境变量(System > Advanced)
OLLAMA_HOST=0.0.0.0
# 然后重启 Ollama 服务

填入 Anthropic API Key(可选)

1
2
ssh MyUbuntuLocal "docker exec openclaw node openclaw.mjs config set \
models.providers.anthropic.apiKey sk-ant-xxxxx"

填入 OpenRouter API Key(Hermes Agent)

1
2
3
# 编辑 ~/dockerfile/hermes/.env,填入 OPENROUTER_API_KEY
# 然后重启
docker compose -f ~/dockerfile/hermes/docker-compose.yml restart

WeChat 渠道(可选)

取消注释 ~/dockerfile/openclaw/.env 中的微信配置后重启:

1
2
OPENCLAW_CHANNEL=wechat
OPENCLAW_WECHAT_MODE=polling

快速运维参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看容器状态
docker ps

# 重启 OpenClaw
cd ~/dockerfile/openclaw && docker compose restart

# 实时查看日志
docker logs -f openclaw
docker logs -f hermes-agent

# 修改 OpenClaw 配置(无需重启容器)
docker exec openclaw node openclaw.mjs config set <key> <value>

# 查看完整 JSON Schema(了解所有可配置项)
docker exec openclaw node openclaw.mjs config schema

延伸阅读