快速修复
# 一键:先看是不是 docker 容器占的,再看宿主机进程,最后换端口
docker ps | grep <端口> ; ss -tlnp | grep <端口>
你会遇到什么
docker run -p <宿主端口>:<容器端口> 或 docker compose up 时容器创建失败,报 failed to bind host port 0.0.0.0:8080/tcp: address already in use 或 Bind for :::8090 failed: port is already allocated。
同一个 -p 命令之前能跑通,某天突然报错——宿主端口被占了。
本文路径:快速修复(30 秒一段命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。
为什么会这样
-p 宿主端口:容器端口 会在宿主机上创建端口转发监听(docker-proxy 监听 0.0.0.0:宿主端口)。宿主端口同一时间只能被一个进程/容器占用。两大类占用来源:
| 占用来源 | 例子 |
|---|---|
| 宿主机其它进程 | python http.server、nginx、VSCode 端口转发 |
| 其它 docker 容器 | 另一个容器映射同一宿主端口(compose 两个服务撞端口、旧容器没删干净) |
-p 宿主端口:容器端口 会在宿主机创建一个转发监听(docker-proxy 监听 0.0.0.0:宿主端口),语义是「宿主端口 → 容器端口」的映射。宿主端口是宿主机全局资源,同一时间只能有一个监听者——不管占用者是宿主进程还是另一个容器的转发规则。
确定性推论:报 port is already allocated 先 ss -lntp | grep <端口> 看监听者是谁;监听进程是 docker-proxy = 另一个容器占着,docker ps -a 找旧容器——已退出的容器保留端口映射,docker ps 看不到不代表端口空闲;监听者是宿主进程 = 换端口或停进程。
一步步来
1. 先看是不是 docker 容器占的 — 必做
docker ps
看 Ports 列有没有该端口。有就 docker rm -f <旧容器>(或 stop + rm),确认没有别的服务需要它再删。
2. 再看宿主机进程 — 必做
ss -tlnp | grep <端口>
(或 lsof -i:<端口> / netstat -tulpn | grep <端口>)看到 PID/进程名就能定位是谁占的——是临时进程就停掉,是长期服务就换端口。
3. 换宿主端口(最省事) — 必做
docker run -p 8081:80 <镜像>
compose 里改 ports: 左侧的宿主端口(如 "8081:80"),或写成环境变量统一管理(${LOBE_PORT}:3210 这类)。
4. 排查 compose 内部冲突 — 排查
同一个 compose 文件里两个服务映射同一宿主端口会互相打架,逐个检查 ports: 左侧值是否唯一。
怎么确认修好了
换端口后正常(实测输出形态):
docker run -p 8081:80 → 成功
docker port <容器> → 80/tcp -> 0.0.0.0:8081其它环境的表现
| 环境 | 说明 |
|---|---|
| 宿主进程占用 | ss -tlnp 定位 PID,停进程或换端口(实测链 A) |
| 容器占用 | docker ps Ports 列定位,rm -f 旧容器(实测链 B) |
| compose 内部 | ports 左侧值唯一 |
| IPv6 侧 | Bind for :::8090 failed = 容器占用形态(实测) |
症状
docker run -p <宿主端口>:<容器端口> 或 docker compose up 时容器创建失败,两种典型报错:
# 形态 1:宿主上其它进程占了端口
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint <name>:
failed to bind host port 0.0.0.0:8080/tcp: address already in use
# 形态 2:另一个 docker 容器占了端口(常报 IPv6 侧)
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint <name>:
Bind for :::8090 failed: port is already allocated
特征:报错里一定带 port is already allocated 或 address already in use,且明确指向宿主端口(0.0.0.0:PORT / :::PORT);同一个 -p 命令之前能跑通,某天突然报错;或照教程/别人的 compose 起服务时报错(端口撞车)。
原因
-p 宿主端口:容器端口 会在宿主机上创建一个端口转发监听(docker-proxy 监听 0.0.0.0:宿主端口)。宿主端口同一时间只能被一个进程/容器占用,被占就报上面的错。两大类占用来源:
- 宿主机其它进程:你/系统/别的服务(如 python http.server、nginx、VSCode 端口转发)占着该端口——最常见;
- 其它 docker 容器:另一个容器的
-p映射了同一宿主端口(含 compose 里两个服务映射同一端口、或旧容器没删干净)。
解法
- 先看是不是 docker 容器占的:
docker ps(看 Ports 列有没有该端口)。有就docker rm -f <旧容器>(或docker stop+docker rm),确认没有别的服务需要它再删。 - 再看宿主机进程:
ss -tlnp | grep <端口>(或lsof -i:<端口>/netstat -tulpn | grep <端口>),看到 PID/进程名就能定位是谁占的——是临时进程就停掉,是长期服务就换端口。 - 换宿主端口(最省事):
-p 8081:80换成没被占的端口;compose 里改ports:左侧的宿主端口(如"8081:80"),或把端口写成环境变量统一管理(${LOBE_PORT}:3210这类)。 - 排查 compose 内部冲突:同一个 compose 文件里两个服务映射同一宿主端口会互相打架,逐个服务检查
ports:左侧值是否唯一。
验证证据
- 本地实测(2026-08-29,WSL Ubuntu,docker 29.1.3,alpine:3.20):
- 链 A(宿主进程占用):python3 http.server 占 8080 →
docker run -p 8080:80→failed to bind host port 0.0.0.0:8080/tcp: address already in use(exit 125);ss -tlnp定位到 python3 进程(pid 可见);停掉后换-p 8081:80→ 成功,docker port显示80/tcp -> 0.0.0.0:8081,docker-proxy 监听 0.0.0.0:8081 与 [::]:8081; - 链 B(容器占用):容器 1
-p 8090:80成功 → 容器 2 抢同一端口 →Bind for :::8090 failed: port is already allocated(exit 125);docker psPorts 列定位到旧容器;docker rm -f旧容器后同一端口再起成功。 - 实测复现日志(2026-08-29)
相关 canonical
按「前置 → 同根 / 同域 / 相关」读:
- 前置(先排除):
- Docker daemon 连不上(前置:先确认 daemon 活着,端口报错才有意义)
适用工具 (1)
本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。
docker · 外部工具成套方案 (组合包)
这个问题包含在下列成套安装方案中。
- docker-compose 开发环境 verified
来源与证据
← 编码代理与终端 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡