OpenClaw Gateway 启动报错 Error: listen EADDRINUSE: address already in use :::18789 的解决方案

1. 问题描述

执行 openclaw gateway 启动网关服务时,很多人会遇到进程直接崩溃退出,终端里打印出这样一段 Node.js 报错栈:

Error: listen EADDRINUSE: address already in use :::18789
    at Server.setupListenHandle [as _listen2] (node:net:...)
    at listenInCluster (node:net:...)
    at Server.listen (node:net:...)
    at Gateway.start (/openclaw/dist/gateway/server.js:...)
Emitted 'error' event on Server instance at:
    at emitErrorNT (node:net:...)
    at process.processTicksAndRejections (node:internal/process/task_queues:...) {
  code: 'EADDRINUSE',
  errno: -4091,
  syscall: 'listen',
  address: '::',
  port: 18789
}

如果是用 openclaw onboard 初始化向导时触发,终端往往会更笼统地提示 Gateway 启动失败,紧接着 Dashboard(管理面板)打不开,浏览器访问 http://localhost:18789 直接显示"无法访问此网站":

✗ Gateway failed to start
✗ Dashboard is not reachable at http://localhost:18789

这个问题在同一台机器上重复执行 openclaw gateway start电脑重启前 Gateway 进程没有正常退出同时在跑另一套 OpenClaw 实例(比如测试环境和正式环境)这台机器上还跑着别的服务恰好用了同一个端口这几种场景下特别常见。很多人第一反应是重装 OpenClaw 或者反复重启命令,但这个报错和"程序装得对不对"完全无关——它是最典型的端口资源被占用问题,18789 是 OpenClaw Gateway 默认监听的端口号,只要这个端口在系统层面已经被别的进程独占,新启动的 Gateway 进程就无法再绑定上去。

2. 原因分析

EADDRINUSE(Error Address In Use)是操作系统网络栈返回的标准错误码,含义是:同一个 IP + 端口组合,同一时刻只能被一个进程独占监听。当 OpenClaw Gateway(底层是一个 Node.js 进程)尝试在 18789 端口上启动 HTTP 服务时,如果这个端口已经被占用,listen() 这个系统调用就会失败,Node.js 把这个失败原样抛成一个 Error 事件,最终表现为你看到的这段报错栈。

常见的占用来源可以归纳成几类:

占用来源 典型表现
上一次 Gateway 进程未正常退出 Ctrl+C 强行中断、或者电脑异常关机,导致旧的 Node.js 进程变成"孤儿进程"继续占着端口
同时运行了多个 OpenClaw 实例 一个用默认配置跑,另一个用相同端口的自定义配置又跑了一次
daemon 后台服务已经在跑 openclaw onboard --install-daemon 已经把 Gateway 注册成了后台自启动服务,你又手动执行了一次 openclaw gateway
其他无关服务恰好用了同一端口 概率较低,但在端口分配混乱的开发机上确实会发生
Docker 容器重复映射了该端口 用容器方式部署,旧容器没清理干净,新容器映射同一端口时冲突

用一张流程图梳理触发链路:

执行 openclaw gateway(或 daemon 自动拉起)
        ↓
Node.js 进程尝试 listen() 绑定 0.0.0.0:18789 / :::18789
        ↓
   操作系统检查该端口是否已被其他 socket 独占监听
        ├─ 已占用 → 返回 EADDRINUSE → Gateway 进程抛异常退出
        └─ 未占用 → 绑定成功,Gateway 正常启动,Dashboard 可访问

需要特别说明的是:openclaw onboard --install-daemon 这个参数会把 Gateway 注册为随系统持续运行的后台守护进程,这意味着即便你没有主动执行任何命令,Gateway 也可能已经在后台悄悄跑着——这是很多人排查这个问题时最容易忽略的一点,明明"感觉自己什么都没启动",但端口早就被自己之前配置的守护进程占着了。

3. 解决方案

方案一:使用 openclaw 内置命令查看并停止已有 Gateway(最推荐)

OpenClaw 自带了管理 Gateway 生命周期的命令,优先用它而不是手动去杀进程:

# 查看当前 Gateway 运行状态
openclaw status

# 如果显示 Gateway 已经在运行,直接用内置命令停止
openclaw gateway stop

# 确认已经停止后,重新启动
openclaw gateway start

这种方式是最安全的,因为它走的是 OpenClaw 自己的进程管理逻辑,能确保相关的子进程、锁文件、临时状态都被正确清理,不会留下"看起来关了但其实还有残留"的隐患。

方案二:手动定位并结束占用端口的进程

如果 openclaw status 显示不在运行、但端口依然报占用,说明是一个脱离了 OpenClaw 管理范围的孤儿进程在占着端口,需要手动排查:

# macOS / Linux:查看 18789 端口被哪个进程占用
lsof -i :18789

# 或者用 ss(现代 Linux 系统更推荐)
ss -tulnp | grep 18789

Windows(PowerShell):

netstat -ano | findstr 18789

拿到 PID 后,先用 ps(Linux/macOS)或任务管理器(Windows)确认这确实是一个 OpenClaw/Node.js 相关的孤儿进程,再决定终止:

# 确认进程身份
ps -p <PID> -o pid,ppid,cmd

# 确认无误后终止
kill <PID>          # 优先用不带-9的正常终止信号
kill -9 <PID>        # 如果上面无效,再考虑强制终止

⚠️ 风险提示kill -9 是强制终止,跳过了进程正常的清理逻辑(比如关闭数据库连接、清理临时文件)。如果确认这个进程正在处理重要任务,建议先用不带 -9 的普通 kill 信号让它优雅退出,只有确认无效时才升级为强制终止。

方案三:检查是否已经配置了后台守护进程(daemon)

如果之前执行过 openclaw onboard --install-daemon,Gateway 很可能早就作为系统服务在后台自启动运行了,此时你再手动执行 openclaw gateway 自然会撞上端口冲突。检查是否存在这样的守护进程:

# macOS:检查 launchd 服务
launchctl list | grep openclaw

# Linux:检查 systemd 服务
systemctl status openclaw

# Windows:检查后台服务
Get-Service | Where-Object { $_.Name -like "*openclaw*" }

如果确认存在守护进程,日常使用时不需要再手动执行 openclaw gateway,它已经在后台持续运行了,直接访问 http://localhost:18789 使用 Dashboard 即可。如果确实需要手动干预(比如调试、修改配置后重启),应该通过对应平台的服务管理命令来重启,而不是绕开它直接再起一个新进程:

# Linux systemd 场景
sudo systemctl restart openclaw

方案四:修改 Gateway 监听端口,从根源规避冲突

如果确认占用方是一个你无法轻易停掉的其他服务(比如同一台开发机上还跑着别的项目恰好也用了 18789),可以修改 OpenClaw 的配置,让 Gateway 换一个端口监听:

# 查看配置文件路径
openclaw config path

# 编辑配置文件,找到 gateway.port 配置项

在配置文件中修改:

gateway:
  port: 18790   # 改成一个确认空闲的端口

修改后重新启动:

openclaw gateway restart

修改端口后,记得同步更新你本地浏览器书签或脚本里访问 Dashboard 用的 URL,避免"改完端口却还在用老地址访问"这种低级失误。

方案五:Docker 部署场景下清理残留容器再重新拉起

如果 Gateway 是跑在 Docker 容器里的,端口冲突通常来自旧容器没有彻底清理,新容器又尝试映射同一个端口:

# 查看是否有旧的容器还占着这个端口
docker ps -a --filter "publish=18789"

# 停止并移除旧容器
docker stop <容器名或ID>
docker rm <容器名或ID>

# 确认端口已释放
lsof -i :18789   # 应该没有任何输出

# 重新启动新容器
docker compose up -d

如果是用 docker compose 管理的,更彻底的清理方式是:

docker compose down
docker compose up -d

down 命令会连带清理网络和容器,比单独 stop 更干净,能避免残留配置带来的隐藏冲突。

4. 各方案对比总结

方案 适用场景 推荐指数
openclaw status/stop 内置命令 首选排查方式,能识别自身管理的进程 ⭐⭐⭐⭐⭐
手动定位并结束孤儿进程 openclaw status 显示未运行但端口仍占用 ⭐⭐⭐⭐
检查是否已配置daemon后台服务 之前执行过 --install-daemon 参数 ⭐⭐⭐⭐⭐
修改监听端口 占用方是无法停掉的其他关键服务 ⭐⭐⭐
Docker 场景清理残留容器 容器化部署环境 ⭐⭐⭐⭐

5. 常见问题 FAQ

5.1 为什么昨天关机前明明用 Ctrl+C 停掉了,今天开机还是报端口占用?

Ctrl+C 发送的是 SIGINT 信号,理论上会触发进程的优雅退出逻辑,但如果 Gateway 当时正在处理某个耗时任务(比如一次长时间的 Agent 推理调用),进程可能没有来得及完全退出就被系统强行结束(比如电脑直接休眠/关机),导致端口的释放没有被操作系统及时回收。开机后先用 lsof -i :18789ss -tulnp 确认一下是否真的还有残留进程,通常这类"假死"的残留进程重启电脑后就会自然消失,如果没消失再手动清理。

5.2 用 pm2 或其他进程管理工具托管 Gateway,报端口占用要怎么排查?

如果用了 pm2 这类进程管理器来管理 Gateway 进程,端口冲突往往是因为pm2 里已经有一个同名进程在跑,你又手动执行了一次原生命令。先查看 pm2 的进程列表:

pm2 list
pm2 stop openclaw-gateway   # 停止 pm2 托管的实例

确认 pm2 里没有残留后,再决定是继续用 pm2 管理(pm2 restart openclaw-gateway)还是切回手动命令,不要两套管理方式混用。

5.3 Kubernetes 部署场景下,Pod 报同样的端口冲突,是同一个原因吗?

原理类似,但通常是因为同一个节点上调度了两个使用了 hostPort: 18789 的 Pod,导致节点级别的端口冲突(Kubernetes 集群内部的 Service 端口隔离通常不会有这个问题,只有显式用了 hostPorthostNetwork 才会暴露到节点层面产生冲突)。排查方式:

kubectl get pods -o wide --all-namespaces | grep openclaw
kubectl describe pod <pod名> | grep -A5 "hostPort"

处理方式是避免多个副本使用同一个 hostPort,或者改用 Service 的方式暴露端口,而不是依赖节点级别的端口绑定。

5.4 端口占用问题解决后,Dashboard 页面依然打不开,是什么原因?

端口冲突解决、Gateway 进程成功启动之后,如果浏览器访问依然失败,需要区分两种情况:一是防火墙拦截(尤其是云服务器场景,安全组/防火墙没有放行该端口的入站规则);二是监听地址范围问题(比如配置文件里写的是只监听 127.0.0.1,导致从其他机器无法访问,只能在本机访问)。排查步骤:

# 确认进程确实在监听且状态正常
openclaw status

# 确认监听的具体地址范围
ss -tulnp | grep 18789

如果监听地址是 127.0.0.1:18789,说明只能本机访问;如果需要从局域网/公网访问,需要在配置里显式改成监听 0.0.0.0,并同时配置好防火�w/安全组规则、身份认证等安全防护措施。

5.5 团队协作中,多人共用一台开发服务器时如何避免互相抢占默认端口?

建议团队约定:共享开发机上不要都用默认端口跑各自的 OpenClaw 实例,每个人在自己的配置文件里指定一个专属端口(比如按工号/用户名分配 18790、18791……),并在团队文档里维护一张端口分配表。这样既能避免互相冲突,也方便后续排查"这个端口是谁的服务"这类问题。

5.6 排查清单速查表

□ 1. 先执行 openclaw status 确认 OpenClaw 自身是否已经在运行
□ 2. 检查是否配置了 --install-daemon 后台守护进程,日常无需再手动启动
□ 3. 用 lsof -i :18789 / ss -tulnp / netstat -ano 定位真正占用端口的进程
□ 4. 确认占用进程身份后,优先用普通 kill/openclaw gateway stop 优雅停止
□ 5. Docker 场景检查是否有未清理的旧容器仍占用该端口映射
□ 6. K8s 场景检查是否多个 Pod 使用了同一个 hostPort
□ 7. 确认无冲突后重新执行 openclaw gateway start,观察是否正常监听
□ 8. 长期无法解决冲突时,考虑修改配置文件中的 gateway.port 换一个端口

6. 总结

Error: listen EADDRINUSE: address already in use :::18789 本质上是一个端口资源被占用的问题,与 OpenClaw 程序本身是否安装正确没有关系。核心排查思路可以浓缩成三句话:

  1. 先用 OpenClaw 自带的 status/stop 命令排查——这是最安全、最了解自身进程状态的排查入口,优先于手动 kill
  2. 重点检查是否配置过后台守护进程——--install-daemon 会让 Gateway 在你毫无察觉的情况下持续在后台运行,这是很多人排查时最容易忽略的盲点;
  3. 手动清理孤儿进程时优先优雅终止——只有确认普通终止无效时才考虑 kill -9,避免破坏正在进行的任务状态。

最佳实践建议:如果你的工作流里经常需要频繁重启 Gateway 做调试,建议固定使用 openclaw gateway stop && openclaw gateway start 这样的组合命令,而不是依赖 Ctrl+C 强行中断,能大幅降低孤儿进程和端口残留占用的发生概率。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐