CMD 在 Dockerfile 中定义默认启动命令,但会被 docker run 参数完全覆盖;仅 exec 形式 CMD 可被正确替换,shell 形式会整体替换为 /bin/sh -c 调用;ENTRYPOINT + CMD 组合时,run 参数仅替换 CMD 部分。

CMD 指令在 Dockerfile 中定义容器启动时默认执行的命令,但它不是一成不变的。当使用 docker run 传入命令参数时,这些参数会完全覆盖 Dockerfile 中的 CMD(包括其全部参数),而不是追加或合并。
CMD 的三种写法及其覆盖行为
CMD 支持 shell 形式、exec 形式和参数形式,但只有 exec 形式(CMD ["cmd", "arg1", "arg2"])能被 docker run 正确覆盖为新命令;shell 形式(CMD cmd arg1 arg2)会被包装进 /bin/sh -c,此时 docker run 传入的命令会替换整个 shell 调用。
-
CMD ["nginx", "-g", "daemon off;"]→docker run myimg sleep 5执行sleep 5 -
CMD nginx -g "daemon off;"→docker run myimg sleep 5也执行sleep 5,原命令被整体替换 -
CMD ["nginx"]+docker run myimg nginx -h→ 执行nginx -h,不是nginx nginx -h
ENTRYPOINT 与 CMD 协同时的覆盖逻辑
如果 Dockerfile 同时声明了 ENTRYPOINT(exec 形式) 和 CMD,那么 CMD 的内容会作为 ENTRYPOINT 命令的默认参数。此时 docker run 后跟的参数会替换 CMD,但不会影响 ENTRYPOINT。
-
ENTRYPOINT ["python", "app.py"],CMD ["--debug"]→ 默认运行python app.py --debug -
docker run myimg --verbose→ 运行python app.py --verbose -
docker run myimg bash→ 运行python app.py bash(除非用--entrypoint显式覆盖)
如何真正“追加”参数而不是覆盖?
Docker 不支持自动追加参数。若需灵活传参,推荐以下方式:
- 把可变部分设计为环境变量 + shell 脚本封装:CMD 执行一个 wrapper.sh,脚本中用
$@或$EXTRA_ARGS动态拼接 - 用
ENTRYPOINT固定主程序,CMD 设为空数组CMD [],强制用户每次用docker run ... args显式传参 - 避免在 CMD 中写死关键参数,改用配置文件或环境变量驱动行为
常见误区与验证方法
容易误以为 docker run myimg cmd arg 是在原有 CMD 后追加,实际是彻底替换。验证方式很简单:
- 构建镜像后运行
docker image inspect myimg --format='{{.Config.Cmd}}'查看默认 CMD - 运行容器时加
--rm -it和明确命令,用ps aux或cat /proc/1/cmdline看实际 PID 1 的命令行 - 注意 shell 形式 CMD 在
inspect中显示为["/bin/sh", "-c", "xxx"],这是覆盖发生的根本原因










