Hermes-WebUI 在 WSL2 下的部署与局域网访问实践
本文要回答的核心问题
如何在 WSL2 环境中正确部署 Hermes-WebUI,并实现稳定的局域网访问能力?
这不是一个“能不能跑起来”的问题,而是一个典型的三层结构问题:
-
应用层:Hermes-WebUI 是否正常启动 -
运行层:WSL2 是否正确暴露服务 -
网络层:局域网设备是否可达
很多失败都发生在第三层,而不是第一层。
第一部分:跨界参照 —— “后台舞台上的戏剧调度系统”
本节要回答的问题
为什么 Hermes-WebUI 在 WSL2 下的部署,本质像一个复杂的后台舞台调度系统?
如果用一个非技术领域的参照来理解 WSL2 + WebUI 的组合,可以把它类比成:
一场戏剧演出,但舞台分成三层:后台(WSL)、控制室(Windows)、观众席(局域网设备)
三层结构对应关系
| 系统层级 | 类比角色 | 实际职责 |
|---|---|---|
| WSL2 | 后台舞台 | Hermes-WebUI 运行环境 |
| Windows | 控制室 | 网络转发与端口管理 |
| 局域网设备 | 观众席 | 访问 WebUI 的终端 |
问题就出在一个经典误解:
“后台演员已经在演出 = 观众就一定看得到”
现实是:
-
后台在演(WSL 内服务启动) -
控制室没开门(Windows 未转发端口) -
观众依然看不到(局域网无法访问)
第二部分:部署路径拆解 —— Hermes-WebUI 在 WSL2 的真实运行结构
本节要回答的问题
Hermes-WebUI 在 WSL2 中到底是怎么启动和运行的?
从你提供的日志可以还原完整结构:
repo root : /mnt/f/WSL/hermes-webui
agent dir : /root/.hermes/hermes-agent
python : /root/.hermes/hermes-agent/venv/bin/python
state dir : /root/.hermes/webui
workspace : /root/.hermes/webui/workspace
host:port : 0.0.0.0:8787
config file : /root/.hermes/config.yaml
这说明系统被拆成四个核心模块:
1. 代码目录(Repo Root)
/mnt/f/WSL/hermes-webui
这是你的“项目本体”,位于 Windows 挂载盘。
特点
-
属于 WSL 与 Windows 共享文件系统 -
IO 性能略低,但方便管理
2. Agent 环境(隔离 Python)
/root/.hermes/hermes-agent/venv/bin/python
本质
这是 Hermes 自动创建的虚拟运行环境。
作用
-
隔离依赖 -
避免污染系统 Python -
固定运行版本
3. 状态目录(运行核心)
/root/.hermes/webui
内容可能包括:
-
session 状态 -
cache -
workspace -
runtime metadata
这是“WebUI 的大脑状态区”。
4. 网络入口(最关键)
0.0.0.0:8787
这是局域网访问的唯一前提。
第三部分:启动行为分析 —— 为什么“看似失败但其实成功”
本节要回答的问题
为什么你会看到 bootstrap failed,但服务其实正常运行?
关键日志:
ERROR: Web UI did not become healthy
但后续又出现:
/health status 200
/api/... status 200
Hermes Web UI listening on http://0.0.0.0:8787
结论:
这是一个“启动探测机制误判”。
启动流程真实逻辑:
Step 1:启动 Web 服务
✔ 进程已启动
Step 2:bootstrap 等待 health check
❗ 默认等待窗口较短
Step 3:health endpoint 实际返回正常
✔ /health = 200
Step 4:bootstrap 判定超时(误判)
❗ 输出 error
作者视角反思
这个问题本质不是“系统坏了”,而是“观测者太急”。
很多 WebUI 框架都会出现类似问题:
-
服务已经活着 -
但监控脚本还没反应过来
这在分布式系统里属于典型的“启动一致性错觉”。
第四部分:WSL2 网络问题的本质
本节要回答的问题
为什么 WSL2 的 Web 服务默认无法被局域网访问?
WSL2 默认是:
NAT 网络 + 虚拟子网隔离
结构如下:
局域网设备
↓
Windows IP(可访问)
↓
WSL2 NAT(默认不可直连)
↓
Hermes-WebUI
核心问题不是 WebUI,而是“网络边界”
即使你绑定:
0.0.0.0:8787
也只解决:
-
✔ WSL 内部访问 -
✔ Windows localhost 转发(部分情况)
但不保证:
-
❌ 手机访问 -
❌ 局域网设备访问
第五部分:完整局域网访问方案(工程级实践)
本节要回答的问题
如何让 Hermes-WebUI 真正被局域网设备访问?
Step 1:启动服务(必须绑定 0.0.0.0)
HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PORT=8787 ./start.sh
Step 2:确认监听
lsof -i :8787
目标结果:
LISTEN 0.0.0.0:8787
Step 3:获取 WSL IP(用于调试)
ip addr | grep inet
示例:
172.29.xx.xx
Step 4:Windows 端口转发(关键)
netsh interface portproxy add v4tov4 listenport=8787 listenaddress=0.0.0.0 connectport=8787 connectaddress=127.0.0.1
Step 5:防火墙放行
netsh advfirewall firewall add rule name="hermes-webui" dir=in action=allow protocol=TCP localport=8787
Step 6:访问路径
http://<Windows-IP>:8787
第六部分:安全机制与风险提示
本节要回答的问题
为什么 Hermes-WebUI 会提示 NO PASSWORD SET?
日志:
WARNING: Binding to 0.0.0.0 with NO PASSWORD SET
本质风险:
当绑定 0.0.0.0 时:
-
同一局域网所有设备可访问 -
包含文件系统能力 -
包含 agent 执行能力
安全建议:
export HERMES_WEBUI_PASSWORD=your_password
或在 config.yaml 设置。
第七部分:三番式问题复盘(关键理解模型)
第一番:你以为的问题
“为什么我打不开局域网?”
第二番:真实问题
“WSL2 网络隔离 + 未做端口转发”
第三番:本质问题
“服务运行层与网络暴露层被混淆”
实用摘要(可操作清单)
-
Hermes-WebUI 必须绑定 0.0.0.0 -
WSL2 默认不等于局域网可达 -
Windows 需要 portproxy 转发 -
防火墙必须放行端口 -
bootstrap error 可忽略(只要 health=200)
一页速览(One-page Summary)
-
启动: HERMES_WEBUI_HOST=0.0.0.0 ./start.sh -
访问: http://Windows-IP:8787 -
WSL2:NAT 网络隔离 -
必做:portproxy + firewall -
常见误判:bootstrap health timeout
FAQ(5–8条)
Q1:为什么 bootstrap 显示失败但还能访问?
因为 health check 超时误判,但服务已运行。
Q2:WSL2 可以直接局域网访问吗?
默认不可以,需要 Windows 转发。
Q3:必须用 0.0.0.0 吗?
是,否则只能本机访问。
Q4:为什么手机访问不到?
因为没有做 portproxy 或防火墙未放行。
Q5:8787 可以改吗?
可以,但需同步修改启动参数与转发规则。
Q6:是否安全?
不安全,必须设置 password。
Q7:为什么 Windows localhost 可以访问?
因为 WSL2 自动做了部分 NAT 映射。
Q8:推荐稳定方案?
Docker 或 Tailscale(避免 NAT 复杂性)。

