前阵子想搞一个本地长期运行的智能体服务,试了几个方案都不太顺手,最后是 WSL2 里跑 OpenClaw、外面接 MiniMax 大模型 API 才真正跑起来。整个过程踩了不少坑,尤其是安全配置和网络这部分,几乎每步都有"文档没说但实际必须处理"的细节。这篇就把我从 0 到 1 的完整过程写下来,包括环境怎么选型、目录怎么规划、MiniMax 怎么接入、密钥怎么藏好、哪些坑最容易翻车,适合想在 Windows 笔记本上部署本地 AI 服务的开发者参考。
1. 为什么选 WSL2:先别急着敲命令,把运行方式想清楚
很多 Windows 用户一听到 Linux 服务,第一反应是装个 Docker Desktop,或者干脆开虚拟机。我刚开始也是这个思路,但试了一圈后发现,对 OpenClaw 这种"要常驻、内存不大、需要和 Windows 本机互相访问"的服务,WSL2 反而是最舒服的底座。
1.1 四条路线的实际对比
我把可以跑 Linux 服务的方式摊开看了一遍,各自优缺点其实非常明显。
双系统:性能和兼容性最完美,但每次切换都要重启,服务没法跟 Windows 桌面同时用。OpenClaw 这类常驻服务不适合。
传统虚拟机(VMware / VirtualBox):隔离性好,快照方便,但启动要几十秒,内存开销大。如果只是跑一个智能体服务,有点杀鸡用牛刀。
Docker Desktop:镜像生态方便,但它在 Windows 上底层其实还是要靠 WSL2 内核,等于是"WSL2 之上再套一层 daemon"。内存占用直接翻倍,我实测光 Docker Desktop 常驻就要 2GB 以上,小内存机器会很吃紧。
WSL2:原生 Linux 内核,支持 systemd,内存由 Windows 动态分配,和 Windows 共享 localhost 转发。对需要长时间挂机、偶尔从浏览器或手机调用的服务来说,这是最轻的方案。
1.2 我最终拍板的关键理由
我的使用场景是:白天写代码,晚上让智能体跑一些定时任务和对话接口;服务在局域网内访问,不一定需要公网。这个需求里 WSL2 的"快速启动、低开销、原生 systemd 托管"全部命中。如果你要在 WSL2 里跑 GPU 训练或者扛高并发的生产服务,那我不建议,它会因为 NAT 网络和资源限制让你很难受。
还有一个容易忽略的点:WSL2 的内存不是随用随还的。跑一段时间后,即使服务没占用那么多,它也可能一直占着 Windows 的物理内存。这个可以在 C:\Users\<你的用户名>\.wslconfig 里给它封个顶,比如限制 4GB,防止影响日常使用。配置文件内容很简单:
ini复制[wsl2]
memory=4GB
swap=2GB
processors=4
改完记得 wsl --shutdown 再重新进,配置才会生效。这一步我一开始完全没意识到,导致 OpenClaw 跑着跑着,Windows 整个变卡。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从零搭好 WSL2 环境:初始化、磁盘迁移和 systemd 开启
环境选型定了之后,就是真正的从 0 到 1。WSL2 安装本身不难,但有几个细节会直接决定你后续是否省心。
2.1 安装和版本校验
Windows 11 上直接管理员 PowerShell 执行:
powershell复制wsl --install
它会装好 WSL 内核和默认的 Ubuntu 发行版。装完重启,然后检查版本:
bash复制wsl -l -v
结果里 NAME 一列显示 Ubuntu,VERSION 必须是 2。如果是 1,就执行:
powershell复制wsl --set-version Ubuntu 2
如果提示 WSL 内核太老,先 wsl --update。这个升级操作我建议放在任何进一步配置之前,因为后面的 systemd 支持、镜像网络模式,都依赖比较新的 WSL 版本。
2.2 磁盘迁移:把默认的 vhdx 挪出系统盘
WSL2 的 Ubuntu 默认安装在 C 盘用户目录下,一个 ext4.vhdx 虚拟磁盘文件。我装完各种依赖后,这个文件很容易膨到几十 GB。C 盘要是空间紧张,早晚要炸。
我迁移的流程是标准的三步:导出、注销、再导入。先完全关闭 WSL:
powershell复制wsl --shutdown
wsl --export Ubuntu D:\wsl\ubuntu-backup.tar
wsl --unregister Ubuntu
wsl --import Ubuntu D:\wsl\ubuntu-disk\ D:\wsl\ubuntu-backup.tar --version 2
这里有坑:--import 之后默认用户会变成 root,且不会自动读取原来的配置。解决办法是在 WSL 内新建或指定一个普通用户,然后在 /etc/wsl.conf 里固定默认登录用户:
code复制[user]
default=你的用户名
改完再 wsl --shutdown 一次。我在这一步折腾了很久,因为没有设置默认用户,后面所有文件属主都是 root,OpenClaw 服务以低权限用户跑的时候会碰到一堆权限问题。
2.3 开启 systemd 并验证
新版 WSL2 默认支持 systemd,但部分老发行版镜像没有默认打开。需要自己确认:
code复制[boot]
systemd=true
写进 /etc/wsl.conf 后,执行 wsl --shutdown 再重新进入 Ubuntu。验证是否生效:
bash复制ps -p 1 -o comm=
输出是 systemd 就说明成功了。如果还是 init,就检查是不是没完全关闭 WSL 实例,或者 WSL 内核版本太旧。
3. 部署 OpenClaw:目录规划、独立用户和 systemd 托管
OpenClaw 项目本身是一个开源智能体框架,拉取方式以官方仓库的 Release 或 git clone 为准。我第一次部署时直接在一个临时目录里跑,后面各种配置混在一起,改起来很痛苦。重新规划目录结构之后才顺畅。
3.1 目录与系统用户先行
我的目录规划是这样的:
/srv/openclaw:项目本体+虚拟环境/var/lib/openclaw:运行时的数据目录/etc/openclaw/:配置文件和密钥文件/var/log/openclaw/:日志落盘位置
顺序很重要:先建用户和目录,再放代码。不要图省事直接在 root 下跑。创建独立系统用户:
bash复制sudo useradd --system --create-home --shell /usr/sbin/nologin openclaw
sudo mkdir -p /var/lib/openclaw /var/log/openclaw /etc/openclaw
sudo chown -R openclaw:openclaw /var/lib/openclaw /var/log/openclaw
nologin 是刻意为之:这个用户不允许交互登录,只允许以服务身份运行。这样即便服务被攻破,攻击者也无法直接拿到一个可交互的 shell,能明显降低影响范围。
3.2 安装依赖和最小启动验证
项目要求 Python 3.11 以上的环境。我在 /srv/openclaw 里创建虚拟环境:
bash复制cd /srv/openclaw
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
# 安装项目依赖,这一步以你拉取到的项目 README 为准
pip install -e .
依赖安装如果很慢,可以先换一个公共的 pip 镜像源,具体配置方式就不展开了,属于基本操作。
第一次启动不要急着写 systemd,先前台跑通:
bash复制cd /srv/openclaw
./openclaw serve --config /etc/openclaw/config.yaml
看到日志里出现监听地址、服务健康检查通过,再做托管。前台跑的目的,是让所有配置错误直接暴露在终端里,而不是被 systemd 的日志缓冲掩盖掉。
3.3 用 systemd 托管,而不是 tmux 或 nohup
网上很多教程让你用 nohup 或者 tmux 挂后台,我不推荐。systemd 能提供崩溃重启、开机自启、日志统一管理,还有资源隔离。这是我实际在用的 service 文件:
ini复制[Unit]
Description=OpenClaw Agent Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=openclaw
Group=openclaw
EnvironmentFile=/etc/openclaw/env
WorkingDirectory=/srv/openclaw
ExecStart=/srv/openclaw/.venv/bin/openclaw serve --config /etc/openclaw/config.yaml
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/openclaw /var/log/openclaw
[Install]
WantedBy=multi-user.target
ProtectSystem=full 和 ProtectHome=true 这两行很多人忽略,它们会把系统目录和 home 目录变成只读,服务只能写指定目录。如果配置里模型缓存或临时文件想写别处,把对应路径加进 ReadWritePaths 即可。
启动命令:
bash复制sudo systemctl daemon-reload
sudo systemctl enable --now openclaw
journalctl -u openclaw -f
服务起来后一定要看日志确认没有报错,别只看服务状态是 active 就觉得完事了。
4. MiniMax API 接入:先验证连通性,再动配置文件
OpenClaw 的模型层是通用的 provider 设计,接 MiniMax 本质上只需要告诉它"API 地址、密钥、模型名"这三件事。但很多人踩的坑是顺序反了——先改配置再测试,结果网络问题和配置问题缠在一起,根本没法定位。
4.1 前置确认:MiniMax 接口的兼容性
MiniMax 的 API 是 OpenAI 兼容格式,所以 OpenClaw 里的 provider 接法很简单,base_url、api_key、model 三个核心参数。但模型名要以你当前账号实际可用的为准。我第一次填了个不存在的模型名,接口直接返回 404,日志里甚至没有明显报错,排查了很久。
4.2 最小连通性测试:先 curl 一把
配置之前,我建议先用 curl 直接打 MiniMax 接口,确认网络通、Key 有效、模型名正确:
bash复制export MINIMAX_API_KEY="你的密钥"
curl -sS https://api.minimax.chat/v1/chat/completions \
-H "Authorization: Bearer $MINIMAX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "abab6.5s-chat",
"messages": [{"role": "user", "content": "ping"}]
}'
能返回结果,再写 OpenClaw 配置。如果这步就失败,不要碰 OpenClaw,先解决网络和 Key 的问题。注意接口域名和模型名都以 MiniMax 官方文档当前给的为准,不同时期的调用方式会有变化。
4.3 在 OpenClaw 配置里落地
我把 API Key 放在环境变量文件里,而不是直接写进 config.yaml:
bash复制sudo mkdir -p /etc/openclaw
sudo touch /etc/openclaw/env
sudo chmod 600 /etc/openclaw/env
sudo chown root:openclaw /etc/openclaw/env
写入内容:
code复制MINIMAX_API_KEY=你的密钥
MINIMAX_BASE_URL=https://api.minimax.chat/v1
配置文件中 provider 部分(具体键名按你版本微调,逻辑是同一套):
yaml复制provider:
name: minimax
api_base: ${MINIMAX_BASE_URL}
api_key_env: MINIMAX_API_KEY
model: abab6.5s-chat
streaming: true
timeout_seconds: 60
max_concurrency: 4
timeout_seconds 一定要调大。LLM 接口的首字延迟经常超过默认的 10 秒,特别是走流式或高峰期,我一开始用默认值连续超时,一度以为是网络被卡。
配置完重启服务:
bash复制sudo systemctl restart openclaw
journalctl -u openclaw -f
然后在 OpenClaw 里发起一次对话,看日志中是否出现正常的 completion 调用记录。
4.4 流式、并发和限流
MiniMax 对并发有限制,如果 OpenClaw 的 max_concurrency 调太高,容易触发限流(HTTP 429)。我在调 QueueAgent 之类的并发任务时连续碰到 429,降低并发到 4 之后才稳定。streaming: true 建议保留,流式响应能让首包更快,体感上延迟低很多。
5. 安全配置:从"裸奔跑通"到"可信网络访问"
很多人的 OpenClaw 部署完,直接监听 0.0.0.0 就结束了。说实话,如果这是在自己电脑上只跑通功能,那没什么问题,但一旦要让局域网或公网设备访问,这就是给攻击者递刀。这一节是整个部署过程里我最看重的一块。
5.1 先搞清楚暴露面
OpenClaw 服务如果监听 0.0.0.0:8443,意味着局域网里任何一台设备都能访问。没有鉴权的话,任何人可以花你的 MiniMax 额度,甚至通过智能体的工具接口做更危险的操作。默认安全姿势是只监听本地:
yaml复制server:
bind: 127.0.0.1
port: 8443
需要多设备访问时,不要直接绑 0.0.0.0,而是走 5.3 小节的反向代理方案。
5.2 服务层本身要开鉴权
OpenClaw 支持访问令牌鉴权,我会在配置里开启并要求每次请求带 Authorization 头:
yaml复制server:
auth:
enabled: true
token_env: OPENCLAW_API_TOKEN
对应地,在 /etc/openclaw/env 里设置:
code复制OPENCLAW_API_TOKEN=用随机生成器产生的长串
生成令牌的方式:
bash复制openssl rand -hex 32
不要自己编一串短密码,要够长、够随机。这个 token 一旦泄露,等于把服务直接暴露给网络上的任意程序。
5.3 网络层:Windows 端口转发 + 反代 TLS
WSL2 默认是 NAT 模式,Windows 可以通过 localhost 访问 WSL 里的服务,但局域网其他设备不行。两种解决路线。
路线一:镜像网络模式。新版 WSL 支持在 .wslconfig 里配置:
ini复制[wsl2]
networkingMode=mirrored
这种模式下 WSL 与 Windows 共享网络接口,局域网可以直接访问 WSL 的端口,前提是配好 Windows 防火墙。注意这个模式需要比较新的 WSL 版本,且某些旧项目可能有兼容性问题。
路线二:NAT 加端口转发。这是更传统也更稳定的做法。先查 WSL 的 IP:
bash复制hostname -I
然后在 Windows 管理员 PowerShell 里加转发规则,把 Windows 外网口的 8443 转发到 WSL 的 8443:
powershell复制netsh interface portproxy add v4tov4 listenport=8443 listenaddress=0.0.0.0 connectport=8443 connectaddress=<WSL的IP>
同时放行 Windows 防火墙入站规则:
powershell复制New-NetFirewallRule -DisplayName "OpenClaw 8443" -Direction Inbound -LocalPort 8443 -Protocol TCP -Action Allow
不管哪条路线,我都强烈建议在服务前面加一层反向代理做 TLS 终结,而不是让 OpenClaw 明文裸跑。用 Caddy 或者 Nginx 在 Windows 侧或者 WSL 里都行,Caddy 可以自动申请证书、配置也短。即便是内网,也值得上 TLS,防止同网段内被嗅探 token。
5.4 密钥与日志卫生
这部分是平时最容易被忽略的。
- 密钥文件权限要收敛:
chmod 600 /etc/openclaw/env。 .gitignore里一定把.env、env、config.yaml加进去,防止手滑提交。- 服务日志不要打印完整请求头。把日志级别调到 warn,避免每次对话都把请求体和 token 打进去:
yaml复制logging:
level: warn
- 定期自查:
ss -tlnp看监听地址,确认没有多余的对外端口;journalctl -u openclaw翻一遍,确认没有可疑的鉴权失败日志。
我一个朋友就遇到过登录令牌被日志打出来、然后整个配置文件被扫出来的情况,真的不值得。
6. 踩坑复盘:五个翻车点与完整排查链路
这部分是我最想写的。每一步都是"现象 -> 排查 -> 根因 -> 解决"的完整链路,不是直接给结论。
6.1 WSL2 的 vhdx 持续膨胀问题
现象:C 盘空间越来越小,即使把 WSL 里的文件删了,ext4.vhdx 体积也不变小。
排查:先确认 wsl --shutdown,然后在 Windows 资源管理器里找到对应发行版的 ext4.vhdx,看文件大小;再进入 WSL 里 df -h 看实际占用,两者明显不一致。
根因:WSL2 的虚拟磁盘只增不减,文件删除后块不会自动回收。
解决:在 Windows 管理员命令行执行 diskpart,依次执行:
code复制select vdisk file="<vhdx 的完整路径>"
attach vdisk readonly
compact vdisk
detach vdisk
exit
压缩完磁盘文件能瘦身不少。这个操作为了保险,执行前先备份重要数据,或至少先 wsl --export 一次。
6.2 systemd 一直不生效,服务无法托管
现象:按文档改了 /etc/wsl.conf,重启 WSL,ps -p 1 -o comm= 依旧显示 init。
排查:第一步查 wsl --version,确认 WSL 内核版本;第二步重新打开 WSL 时确认它是全新实例而不是残留会话。
根因:两种可能,一是 WSL 内核太老,不支持 systemd;二是没有真正 wsl --shutdown,实例还在缓存旧配置。
解决:执行 wsl --update 更新内核,然后强制 wsl --shutdown。注意,wsl --shutdown 会把所有发行版全部停掉,如果有其他 WSL 实例正在跑,要提前做好心理准备。
6.3 MiniMax 连接超时,但 401 和 404 同时出现
现象:OpenClaw 日志里一会儿报连接超时,一会儿报鉴权失败,完全没有规律。
排查:直接 WSL 里 curl MiniMax 接口,发现超时;再 ping 域名解析正常;怀疑是 NAT 下 MTU 问题,把 WSL 网卡 MTU 临时改成 1400 再 curl,果然通了。
根因:WSL2 的 NAT 环境下,某些路由器或网络环境对分片处理不友好,默认 MTU 1500 导致请求卡住。
解决:临时调低测一把:
bash复制sudo ip link set dev eth0 mtu 1400
确认有效后,把 MTU 设置固化到启动脚本或路由器层面。这个坑在部分公司网络和特殊宽带环境下尤其常见。
6.4 Windows 重启之后 OpenClaw 没有自动恢复
现象:电脑重启后,WSL 里 systemd 明明配置了 enable,但服务没起来,还得手动开个终端敲 wsl 才会启动。
排查:检查计划任务,发现根本没配开机启动 WSL 的触发器。WSL 不会随 Windows 开机自动启动,这是关键认知。
根因:WSL 实例只在你主动启动它的时候才会加载,systemd 的 enable 只保证"实例启动后服务会拉起来",但不会主动唤醒 WSL。
解决:用 Windows 任务计划程序,创建一个开机触发任务,操作里写:
powershell复制wsl.exe -d Ubuntu -u root -e systemctl start openclaw
触发器选"启动时",延迟 1 分钟,因为 WSL 冷启动需要时间。如果任务失败还要设置"每 5 分钟重试",防止首次启动过快导致命令未执行。
6.5 局域网设备访问被拒,但本机访问正常
现象:OpenClaw 已经通过 Windows 端口转发配好 8443,本机浏览器访问 localhost:8443 正常,手机访问 Windows 的局域网 IP:8443 超时。
排查:分三段查。第一段 WSL 里 ss -tlnp 看服务是否监听在预期端口;第二段 Windows 里 netstat -ano | findstr 8443 看 portproxy 监听的地址是不是 0.0.0.0;第三段检查 Windows 防火墙入站规则是否存在且已放行端口。
根因:portproxy 规则配好了,但 Windows 防火墙没有放行入站 8443,导致局域网请求在 Windows 防火墙这一层就被丢弃。
解决:补上入站规则后,手机再访问就通了。如果用了镜像网络模式,则要注意 Windows 防火墙是否覆盖到对应的网络配置文件(专用网络 vs 公用网络)。
7. 部署完成后的日常维护与个人体会
现在这套环境已经稳定跑了一段时间,日常维护其实很轻。我常用的命令就这几个:
bash复制# 看服务状态和日志
systemctl status openclaw
journalctl -u openclaw -f
# OpenClaw 更新
cd /srv/openclaw && git pull && pip install -e .
# 重启 WSL(碰到奇怪网络问题后的万能疗法)
wsl --shutdown
服务长期运行的资源占用,我这套配置下大概稳定在 500MB 到 1GB 内存,CPU 平时几乎不动。这种量级的服务放在 WSL2 里完全合理,不需要专门的服务器。
个人体会最深的三点。第一,部署顺序很重要:先环境、再配置、再接入 API、最后做安全加固,每一步的验证都前置,能省掉大量"拆东墙补西墙"的时间。第二,OpenClaw 这类智能体服务因为能调用工具、访问外部资源,它比普通 Web 服务更需要做访问控制,"绑定本地 + 令牌鉴权 + 反代 TLS + 低权限运行"这四件套一个都不能少。第三,遇到奇怪问题先别怀疑项目本身,八成是 WSL2 的 NAT 网络或者 systemd 配置的细节问题。先隔离变量,再层层排查,比盲目重启有效得多。
最后分享一个小技巧:我习惯在 OpenClaw 的配置里把健康检查端点单独开出来,不鉴权,只返回一个静态状态码,这样外部探活工具不会污染业务日志,也能快速区分"服务挂了"和"令牌失效"两种情况。这个小设计在几次故障排查里帮我省了不少时间。
