首页/文章/多个 Claude Code 会话抢同一个 3000 端口?在新加坡 Mac mini 上按 worktree 分配开发端口

多个 Claude Code 会话抢同一个 3000 端口?在新加坡 Mac mini 上按 worktree 分配开发端口

· Zumac.Ai

问题:会话越开越多,端口开始打架

把 Claude Code 放到一台常开的 Mac mini 上以后,很多人会很快走到下一步:用 git worktree 开出三四个工作目录,在 tmux 里每个窗口跑一个 Claude Code 会话,各做一个功能。有人甚至让单个会话连续跑十几个小时,自己只是偶尔从笔记本或平板上看一眼进度。

这时最容易踩的坑不是模型,而是开发服务器的端口。前端项目默认都想用 3000,后端默认 8000 或 8080。第一个会话起了服务,第二个会话再起就报 EADDRINUSE;更糟的是有的工具会自动换到 3001,Claude 却还在按 3000 去测,结果测的是另一个会话的代码,日志看起来一切正常,结论却是错的。

请使用你自己的 Claude 账号与官方 Claude Code,并遵守 Anthropic 的使用条款与适用政策。账号与订阅由你自行准备;Zumac.Ai 与这些服务无官方合作关系,本文只谈稳定合规的使用环境。

思路:一个 worktree 一段固定端口

解决办法很朴素:不要让端口「自动找空位」,而是给每个 worktree 预先分好一段端口,并且把这段端口写进三个地方:项目的环境变量文件、会话能读到的说明文件、你自己的转发配置。这样无论哪个会话重启、哪个服务崩了再起,端口都不会漂移。

下面的步骤假设你在新加坡 Mac mini 上已经装好了 Claude Code、git 和 tmux,并能用 SSH 或 UU 远程连接登录。

步骤一:建 worktree 时顺手分配端口段

约定一个简单规则:第 N 个 worktree 使用 3000 + N*10 开始的 10 个端口。前端用段内第一个,后端用第二个,其余留给数据库面板、Storybook 之类。

# 在主仓库目录执行
cd ~/work/myapp
mkwt() {
  name="$1"; n="$2"
  git worktree add "../myapp-$name" -b "feat/$name"
  base=$((3000 + n*10))
  cat > "../myapp-$name/.env.local" <<EOT
PORT=$base
API_PORT=$((base+1))
EXTRA_PORT=$((base+2))
EOT
  echo "$name -> $base-$((base+9))" >> ~/work/ports.txt
}
mkwt login 1
mkwt billing 2
mkwt search 3

~/work/ports.txt 是你的端口登记表,后面排查时一眼就能看出谁占了哪段。记得把 .env.local 加进 .gitignore,它只属于这台机器。

步骤二:让项目真正读这些变量

很多框架的启动脚本把端口写死在 package.json 里。检查一遍,改成读环境变量,例如:

"scripts": {
  "dev": "next dev -p ${PORT:-3000}",
  "api": "node server.js --port ${API_PORT:-8000}"
}

同时关掉「端口被占就自动换一个」的行为(不少开发服务器有 strict port 一类选项)。宁可启动失败报错,也不要悄悄换端口,否则 Claude 拿到的地址和实际服务对不上。

步骤三:把端口写进每个 worktree 的 CLAUDE.md

Claude Code 会读取项目里的 CLAUDE.md。在每个 worktree 里加一小段,明确告诉会话:

## 本工作目录的端口
- 前端:http://localhost:3010(来自 .env.local 的 PORT)
- 后端:http://localhost:3011
- 不要使用 3000 或其他 worktree 的端口;启动失败时先报告,不要自行换端口。
- 结束任务前用 lsof 确认自己启动的进程已停止。

最后一条很重要:长会话常常起了服务就忘了关,几轮之后机器上挂着一堆孤儿进程。把「谁启动谁收尾」写进说明,比事后手动清理省事。

步骤四:tmux 里一个窗口对应一个 worktree

tmux new-session -d -s dev -n login -c ~/work/myapp-login
tmux new-window -t dev -n billing -c ~/work/myapp-billing
tmux new-window -t dev -n search -c ~/work/myapp-search
# 进入后在每个窗口里启动 claude
tmux attach -t dev

窗口名和 worktree 名保持一致,再配合 ports.txt,你从任何设备接回来都能立刻对上号。断开 SSH 或 UU 远程连接并不会影响 tmux 里的会话,这正是把它放在常开机器上的意义。

步骤五:在笔记本或平板上看预览,用 SSH 端口转发

服务只监听在 Mac mini 的 localhost 上,不要为了方便改成监听所有网卡。需要看页面时,在你自己的设备上做本地转发:

ssh -N -L 3010:localhost:3010 -L 3020:localhost:3020 user@你的机器地址

然后在本地浏览器打开 http://localhost:3010 就是 login 分支的预览。平板上可以用支持端口转发的 SSH 客户端做同样的事,或者直接 UU 远程连接到 Mac mini 桌面,在那边的浏览器里看。只转发你此刻需要的端口,用完断开。

步骤六:出问题时怎么查

  1. 先看谁占着端口:lsof -nP -iTCP -sTCP:LISTEN | grep -E ':30[0-9]{2}',对照 ports.txt。
  2. 孤儿进程:工作目录已经删了但进程还在,用 lsof -p 进程号 | grep cwd 看它属于哪个 worktree,确认后再结束。
  3. 会话测错了服务:让 Claude 在测试前先 curl -s localhost:端口/健康检查路径 并打印返回里的分支名或版本号,确认是自己的服务再继续。
  4. 端口段不够用:把规则从每段 10 个改成 20 个即可,重新生成 .env.local,不需要改代码。

验收清单

  • 三个 worktree 同时 npm run dev,三个服务都正常起来,端口与 ports.txt 一致。
  • 故意先占用某个 worktree 的端口,它的服务启动失败并报错,而不是换到别的端口。
  • 关掉笔记本、隔一段时间再连回 tmux,所有会话仍在,服务仍在原端口。
  • 通过 SSH 转发能在本地打开每个预览,Mac mini 上没有服务监听在外网卡上。

为什么放在一台常开的新加坡 Mac mini 上

并行会话、长时间运行、随时从别的设备接回来查看,这三件事都要求机器一直开着、网络稳定、环境不被别人动。笔记本合盖、换网络、系统更新重启,都会让一晚上的会话前功尽弃。整机独享的 Mac mini 可以按你的端口规则长期配置下去,tmux、worktree、.env.local 都不会被打乱。

Zumac.Ai 在新加坡提供 Mac mini M6 整机独享,接入新加坡家庭宽带,适合把 Claude Code 长期放在一个稳定合规的使用环境里运行。新客户整机租用 ¥999/月;季付、半年付、年付有折扣。交付后可用 UU 远程连接,SSH 按你的安全策略自行开启。Claude 账号与订阅由你自行准备。咨询可 Telegram @singatao。实际延迟取决于你本地的网络线路。

小结

多会话并行的瓶颈往往不在模型,而在端口这种小事。给每个 worktree 分一段固定端口,写进 .env.local 和 CLAUDE.md,禁止自动换端口,用 tmux 管会话、用 SSH 转发看预览,几个 Claude Code 会话就能在新加坡常开 Mac mini 上各干各的、互不干扰。

Zumac.Ai 新加坡 Mac mini M6,整机独享,¥999/月起,联系 Telegram @singatao。
查看方案与费用 · 帮助中心

返回首页 · 更多文章

Telegram 客服