VPS 上服务启动失败,日志里出现:
Address already in use
EADDRINUSE: address already in use
bind() to 0.0.0.0:80 failed (98: Address already in use)
port is already allocated
这类问题通常不是代码突然坏了,而是你要监听的端口已经被别的进程占着。最常见的场景是:Nginx 和 Apache 抢 80/443,Node 服务开了两份,Docker 容器映射了同一个端口,或者旧进程没退出干净。
不要一上来就 kill -9。先确认是谁占端口,再决定是停掉旧服务、换端口、改 Docker 映射,还是修 systemd 启动方式。
现代 Linux 上优先用 ss:
sudo ss -lntp
只查某个端口,比如 80:
sudo ss -lntp | grep ':80 '
查 3000:
sudo ss -lntp | grep ':3000 '
输出里重点看:
Local Address:Port:监听地址和端口;users:(...):哪个进程占着;pid=:进程 ID;LISTEN:说明这个端口正在被监听。
如果你看到:
LISTEN 0 511 0.0.0.0:80 users:(("nginx",pid=1234,fd=6))
说明 80 端口已经被 Nginx 占用了。
lsof 也很好用:
sudo lsof -i :3000
只看 PID:
sudo lsof -t -i :3000
常见输出会告诉你:
- 命令名;
- PID;
- 用户;
- IPv4 / IPv6;
- LISTEN 状态。
如果没有安装:
sudo apt install lsof -y
或:
sudo dnf install lsof -y
ss 更适合系统排查,lsof 更适合快速看“哪个进程占着这个端口”。
查看端口占用:
sudo fuser -v 3000/tcp
只显示 PID:
sudo fuser 3000/tcp
如果你已经确认这个进程可以停掉,可以用:
sudo fuser -k 3000/tcp
但生产环境不建议直接这样杀。更安全的是先看进程属于哪个服务:
ps -fp PID
systemctl status 服务名 --no-pager
能用 systemctl stop,就不要直接 kill -9。
Web 服务最常见的端口冲突就是 80/443。
查端口:
sudo ss -lntp | egrep ':80 |:443 '
查服务状态:
sudo systemctl status nginx --no-pager
sudo systemctl status apache2 --no-pager
sudo systemctl status caddy --no-pager
如果 Nginx 报:
bind() to 0.0.0.0:80 failed (98: Address already in use)
通常是 Apache、Caddy、另一个 Nginx 或 Docker 容器已经占了 80。
解决方式不是“哪个都杀”:
- 只保留一个 Web 入口;
- 把另一个服务改到 8080/8443;
- 让 Nginx/Caddy 做反向代理;
- 停用不用的服务:
sudo systemctl stop apache2
sudo systemctl disable apache2
改完 Nginx 配置后:
sudo nginx -t
sudo systemctl reload nginx
Nginx 配置错误可以看:VPS Nginx 配置错误怎么办。
Docker 报错通常像这样:
Bind for 0.0.0.0:8080 failed: port is already allocated
先看容器端口映射:
docker ps --format "table {{.Names}}\t{{.Ports}}"
再看宿主机端口:
sudo ss -lntp | grep ':8080 '
常见原因:
- 两个容器都写了
-p 8080:80; - 宿主机 Nginx 已经占用 80,容器又要
-p 80:80; - 旧容器没删,仍占着端口;
- docker-proxy 或 Docker 网络状态异常。
处理方式:
docker ps -a
docker stop 容器名
docker rm 容器名
或者改端口映射:
ports:
- "8081:80"
不要为了跑起来随便把多个服务都映射到 80。宿主机一个 IP 的同一个端口,同一时间只能被一个监听者占用。
如果服务由 systemd 管理,先看:
sudo systemctl status app --no-pager
sudo journalctl -u app -n 100 --no-pager
常见日志:
Error: listen EADDRINUSE: address already in use :::3000
Failed with result 'exit-code'
这说明应用启动时绑定端口失败,systemd 只是报告服务失败,不是 systemd 本身的问题。
排查顺序:
sudo ss -lntp | grep ':3000 '
ps -fp PID
sudo systemctl stop 冲突服务
sudo systemctl restart app
如果服务反复重启,还可能触发 start-limit-hit。这时先修端口冲突,再:
sudo systemctl reset-failed app
sudo systemctl start app
更详细的 systemd 启动失败排查可以看:VPS 上 systemd 服务启动失败怎么办。
有时你明明只看到一个服务,却还是报端口冲突,原因可能是 IPv4/IPv6 双栈监听。
看监听地址:
sudo ss -lntp | grep ':80 '
你可能看到:
0.0.0.0:80
[::]:80
含义大致是:
0.0.0.0:80:监听所有 IPv4 地址;[::]:80:监听 IPv6 地址;- 某些配置下
[::]:80也可能同时接 IPv4。
Nginx 里建议明确写:
listen 80;
listen [::]:80;
如果只想监听 IPv4:
listen 0.0.0.0:80;
不要让一个服务监听 0.0.0.0:80,另一个服务又监听 [::]:80,再指望它们各管各的。实际行为会受系统和应用配置影响,很容易造成排查混乱。
很多人看到一堆 TIME_WAIT,就以为端口被占用。
查看:
ss -tan | grep TIME-WAIT | wc -l
TIME_WAIT 是 TCP 正常状态,通常和短连接很多有关。它一般不会阻止服务监听 0.0.0.0:80 这类固定端口。
真正需要关注的是:
sudo ss -lntp
有没有进程处于 LISTEN 状态占着目标端口。
不要乱改 tcp_tw_reuse、tcp_fin_timeout。如果是临时端口耗尽,那是另一类问题;如果是服务启动时报 EADDRINUSE,优先查 LISTEN 进程。
Node.js 常见:
Error: listen EADDRINUSE: address already in use :::3000
排查:
sudo ss -lntp | grep ':3000 '
如果是开发环境,可以换端口:
PORT=3001 npm start
Python / Flask / FastAPI 也类似:
uvicorn app:app --host 0.0.0.0 --port 8001
生产环境不要让服务随机换端口。要么固定端口并释放冲突进程,要么让 Nginx 反代到新端口,并同步修改配置。
网上常见命令:
sudo kill -9 $(sudo lsof -t -i:3000)
它确实能释放端口,但风险是:你可能杀掉正在提供服务的进程、数据库代理、反向代理或别人的后台任务。
更安全的流程:
sudo lsof -i :3000
ps -fp PID
systemctl status 相关服务 --no-pager
确认无误后再:
sudo systemctl stop 服务名
只有确认它不是 systemd 服务、也不是关键进程时,再考虑 kill。
遇到 Address already in use、EADDRINUSE、port is already allocated,按这个顺序查:
sudo ss -lntp | grep ':端口 '找 LISTEN 进程。sudo lsof -i :端口看进程名、用户和 PID。ps -fp PID确认它是什么。- 如果是 systemd 服务,先
systemctl status 服务名。 - 如果是 Docker,
docker ps --format "table {{.Names}}\t{{.Ports}}"。 - 如果是 80/443,检查 Nginx、Apache、Caddy 是否冲突。
- 看
journalctl -u 服务名 -n 100确认应用报错。 - 必要时改端口、停冲突服务或改 Docker 映射。
- 不要把 TIME_WAIT 当成监听端口冲突主因。
- 不要盲目
kill -9,先确认进程归属。
端口被占用的排查核心很简单:先找谁在 LISTEN,再决定怎么处理。
ss -lntp 是第一优先级,lsof -i :端口 用来确认进程,fuser 可以快速定位和释放。Nginx/Apache/Caddy 抢 80/443、Docker 端口映射冲突、Node/Python 服务开了两份,是 VPS 上最常见的几类原因。
不要把 TIME_WAIT、防火墙、安全组和端口监听混在一起。Address already in use 说的是本机端口绑定冲突;防火墙和安全组影响的是外部能不能访问,这是另一类问题。
