详细解释Ollama端口冲突的常见原因——后台服务与前台命令重复启动导致,以及如何判断是否真的需要手动启动服务。
几乎所有遇到这条错误的人,都是在已经运行着 Ollama 的机器上又执行了 ollama serve。错误信息是准确的——端口确实被占用了——而解决办法通常是别再试图启动第二个实例。
Error: listen tcp 127.0.0.1:11434: bind: address already in use
在 Windows 上,同样的情况会由操作系统给出自己的措辞:
Error: listen tcp 127.0.0.1:11434: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
两者都是 bind() 失败。Ollama 的 HTTP 服务器向内核请求环回地址上的 TCP 端口 11434,内核拒绝了,因为已经有东西占用了它。此时还没有尝试加载任何模型——这一步发生在所有操作之前。
Ollama 在它支持的每个平台上都会将自己安装为后台服务,并且该服务在开机时启动。然而每篇教程都以 ollama serve 开头,这会在前台启动一个服务器。在一个正常安装的系统中,这是两个服务器争夺同一个端口,第二个启动的注定会输。
一个重要的后果是:你很可能根本不需要手动运行它。在杀掉任何进程之前先测试一下:
curl http://localhost:11434/api/tags
如果返回了 JSON 格式的已安装模型列表,说明服务器已经在正常运行,错误只是因为你试图启动第二个冗余的实例。如果收到 connection refused,说明是其他进程占用了端口,下一节的方法适用。
具体到各平台的表现形式是这样的:macOS 上,菜单栏应用会启动服务器,所以退出应用就是停止它的方式。Linux 上有一个名为 ollama 的 systemd 单元。Windows 上有一个随登录启动的系统托盘应用。在容器中,镜像的 entrypoint 通常已经是 ollama serve,所以你再添加自己的命令就会产生冲突。
有一个设计细节值得了解,因为它解释了为什么这里会产生冲突而不是被优雅地处理。Ollama 是一个同时包含客户端和服务端的二进制文件:ollama run、ollama pull 和 ollama list 都是 HTTP 客户端,通过这个端口与服务器通信,而 ollama serve 本身就是服务器。客户端不会按需启动服务器,服务器也不会检测到同类进程而主动退让。两次 serve 调用就是两个程序同时请求同一个 socket,内核的裁决就是拒绝第二个。
即使你很自信,也不要跳过这一步,因为偶尔占端口的并不是 Ollama,杀错进程会让你的下午变得更糟。
# macOS and Linux
lsof -i :11434
# or, without lsof
ss -tulpn | grep 11434
# Windows PowerShell
Get-NetTCPConnection -LocalPort 11434 | Select-Object OwningProcess
Get-Process -Id <pid>
查看进程名称。如果显示是 ollama,那你遇到的是普通情况。如果不是,你需要解决的是端口冲突,而不是重复服务问题,正确的做法是把 Ollama 挪走,而不是赶走那个其他程序。
有两种情况会导致这个检查返回空,但端口明明已被占用。第一,在 Linux 上,lsof 和 ss 只会显示你有权限查看的 socket 的所属进程,所以如果一个服务器以 ollama 系统用户的身份运行,一个没有特权的 shell 是看不到它的——在断言"没有进程占用"之前,先用 sudo 执行检查。第二,地址和端口同样重要:一个绑定到 0.0.0.0:11434 的进程会在所有接口上占用该端口,包括环回接口,所以一个专门为 127.0.0.1 编写的过滤条件可能会漏掉它。
停止托管服务,而不是杀掉进程。如果你 kill 的是一个被Supervisor管理的服务,它会被 Supervisor 重新拉起,然后你会以为这个端口"闹鬼"了。
# Linux
sudo systemctl stop ollama
# macOS: quit the Ollama menu-bar app
# Windows: exit Ollama from the system tray
通过重新运行 lsof 或 Get-NetTCPConnection 检查来确认端口已释放。一个刚刚退出的进程留下的 socket 可能处于 TIME_WAIT 状态,短时间内仍会占用端口——这种情况下等待是正确的处理方式。
或者把 Ollama 换到其他端口。OLLAMA_HOST 设置了服务器的绑定地址和端口,同时也告诉客户端去哪里查找,所以两端都需要设置:
OLLAMA_HOST=127.0.0.1:11500 ollama serve
# in another shell, the client needs to agree
OLLAMA_HOST=127.0.0.1:11500 ollama list
忘记设置第二处就是为什么人们报告说"服务器在新端口启动成功,但 CLI 看不到它"的原因。还要注意 OLLAMA_HOST 同时承担了绑定地址和客户端目标的双重职责,这意味着对一个有意义的东西对另一个未必有意义:0.0.0.0 是一个有效的绑定目标,却不是一个有效的连接目标。这种不匹配是 CLI 可用而 API 不可用的原因之一。
让它永久生效,且放在正确的位置。在 shell 中 export 一个变量不会传递到 systemd 服务——那需要在 unit override 中加上 Environment= 行并执行 daemon reload。
权限拒绝。 在 Windows 上部分用户会遇到 bind: An attempt was made to access a socket in a way forbidden by its access permissions。这不是端口冲突。它通常意味着该端口落在了操作系统预留的范围内,往往是为 Hyper-V 或 WSL 保留的,解决办法是选一个该范围之外的端口,而不是去找进程——没有进程可找。
容器发布了错误的东西。 在 Docker 内部,绑定发生在容器自己的网络命名空间中,所以宿主机上的 11434 进程不会与之冲突。如果你从容器中看到这个错误,那是容器内部已经在监听了——最可能是镜像的 entrypoint,然后你的命令又加了一个。反过来,如果一个容器只绑定到环回接口,即使做了端口映射也无法从宿主机访问,所以需要在容器内设置 OLLAMA_HOST=0.0.0.0:11434。
绑定到 0.0.0.0 会将 API 暴露到整个网络,而 Ollama API 本身没有认证。只在防火墙之后或由添加了认证的反向代理保护的环境中这样做。
如果服务器启动正常,失败发生在之后——当模型真正被加载的时候——那你看的是 runner 进程终止了。完整的设置说明在 Ollama 指南中有介绍。