docker compose run 总是新建容器执行一次性命令,而 docker compose exec 只能进入已运行的容器;run 默认不映射服务端口,需显式加 --service-ports 或 --publish,且会覆盖服务定义中的 command。

docker compose run 是 Docker Desktop(以及所有支持 Compose v2 的环境)中执行一次性命令的唯一标准方式。它不依赖 GUI,直接在终端中调用,但行为和参数逻辑与 Docker Desktop 的底层引擎完全一致。
docker compose run 和 docker-compose exec 的核心区别在哪?
关键不是“能不能用”,而是“容器是否已运行”:
-
docker compose run总是新建一个容器:基于服务定义拉起全新实例,执行完即退出(除非加--rm显式控制清理);适合数据库迁移、初始化脚本、调试新代码等“尚未启动服务”的场景 -
docker compose exec只能进入已运行的容器:要求目标服务容器必须处于running状态;适合日志检查、临时诊断、交互式调试已有服务 - 常见错误现象:
docker compose exec web bash报错No container found for web_1→ 说明web根本没在跑,此时该用run而非exec
端口映射不生效?默认不暴露服务 ports 是设计使然
执行 docker compose run web python manage.py runserver 0.0.0.0:8000 后,在宿主机访问 localhost:8000 失败,不是 bug,是默认行为:
- Compose 不会自动应用
docker-compose.yml中为web定义的ports:配置(比如- "8000:8000") - 若需复用配置里的端口映射,必须显式加
--service-ports(短选项-P):docker compose run --service-ports web python manage.py runserver 0.0.0.0:8000 - 若只需映射某几个端口(比如只暴露 8000,不暴露其他),用
--publish(短选项-p)更精准:docker compose run -p 8000:8000 web python manage.py runserver 0.0.0.0:8000 - 注意:
--service-ports和--publish互斥,同时指定会报错
--rm 不是默认行为,但你应该几乎总是加上它
不加 --rm 时,每次 run 结束后都会留下一个 Exited 状态的容器,久而久之 docker ps -a 列表里全是历史记录:
- 加
--rm后容器退出即被自动删除,干净利落:docker compose run --rm web ls /app - 例外情况:需要保留容器用于后续
docker inspect或查日志(如崩溃后想看 stderr),才暂时不加--rm - Docker Desktop 的 Dashboard 里不会自动隐藏这些 Exited 容器,容易误点重启——这是最常被忽略的 UI 干扰点
环境变量和卷挂载按服务定义自动继承,但 command 会被覆盖
你不需要重复写 -e 或 -v,只要服务在 docker-compose.yml 里声明了,run 就全盘照搬:
-
environment:、volumes:、networks:、depends_on:全部生效 - 但
command:在服务定义中会被你传入的命令完全替换(这是它和up的关键差异);例如服务定义了command: tail -f /dev/null,执行docker compose run web bash就不会运行tail,而是直接进bash - 如果想保留原
command并追加动作(比如先运行服务再执行脚本),得手动在命令里组合:docker compose run web sh -c "npm run build && node server.js"
Docker Desktop 的图形界面本身不提供 run 的快捷入口,所有一次性任务都必须走终端。最容易被忽略的是:它不会提示你当前服务是否已运行,也不会帮你自动补全 --service-ports —— 这些决策全靠你对服务状态和端口意图的判断。


















