executable file not found:Docker 容器启动即退出的 4 种原因(含 exec format error)
现象:容器起来一瞬就退出
# 容器启动时
standard_init_linux.go: exec: "./start.sh": stat ./start.sh: no such file or directory
# 或 docker exec 时
OCI runtime exec failed: exec: "sh": executable file not found in $PATH: unknown
# 或另一种形态
exec ./start.sh: no such file or directory
一行结论:容器里找不到你让它执行的那个文件/命令。最大的陷阱在于:报 no such file or directory 时,文件往往就在那里——真正缺失的是脚本的解释器(shebang 指向的程序不存在)。这是 Docker 报错里最误导人的一条。
先别急着改 Dockerfile。把错误抄全(含 standard_init_linux.go / OCI runtime / exec format error 原文),再按下面四类对照——多数工单在第一类(CRLF)或第四类(架构)就结束。
四种原因,按概率排序
1. Windows 的 CRLF 行尾毁掉了 shebang(最阴险)
在 Windows 上编辑的 shell 脚本行尾是 \r\n,第一行实际变成了:
#!/bin/sh\r
Linux 找的是 sh\r 这个程序——不存在,于是报 no such file or directory,而你 ls 看到脚本明明在。验证与修复:
docker run --rm --entrypoint sh myapp -c "head -1 /app/start.sh | od -c | head -2"
# 看到 \r \n 就是它了
# 修复:仓库加 .gitattributes
*.sh text eol=lf
编辑器右下角把 CRLF 切换成 LF 重存一遍也行。CI 里若有 Windows runner 检出脚本,记得统一 core.autocrlf 或强制 eol=lf,否则本地修好、流水线再弄坏。
2. ENTRYPOINT 的 exec 形式里用了环境变量拼接
# ❌ exec 形式不会经过 shell,$JAVA_OPTS 不展开,字面量去找
ENTRYPOINT ["java", "$JAVA_OPTS", "-jar", "app.jar"]
# ✅ 要展开变量就用 shell 形式(注意会丢信号传递)
ENTRYPOINT java ${JAVA_OPTS} -jar app.jar
exec 形式 ["a","b"] 不启动 shell,$VAR、~、通配符都是字面量;shell 形式 a b 经过 /bin/sh -c,变量会展开。分不清这两种形式是这个错误的高发源头。同理,ENTRYPOINT ["npm", "start"] 找不到 npm,多半是基础镜像里 npm 不在 PATH 或装在别的用户目录下。
折中写法:用小型 entrypoint 脚本接收参数,脚本末尾 exec 真正进程,既保留展开能力又保住信号。可用 Dockerfile 生成器 先搭骨架,再手工补 entrypoint。
3. 路径问题:相对路径与 WORKDIR
WORKDIR /app
COPY start.sh . # 文件在 /app/start.sh
ENTRYPOINT ["./start.sh"] # ✅ 相对 WORKDIR
ENTRYPOINT ["/start.sh"] # ❌ 根目录下没有
docker exec 进容器手动跑一遍最快:
docker run --rm --entrypoint sh myapp -c "ls -la /app && which npm"
多阶段构建时,上一阶段 COPY --from=builder 漏了可执行位或路径写错,也会表现为「文件在构建上下文里明明有」。进最终镜像用 ls -la 看权限位,chmod +x 要写进 Dockerfile 而不是只在本机改一次。
4. 架构不匹配:exec format error
exec /app/start.sh: exec format error
M 系列 Mac 构建的 arm64 镜像跑在 x86 服务器上(或反之)。这不是文件找不到,是二进制格式不对。构建时锁平台:
docker buildx build --platform linux/amd64 -t myapp --push .
docker inspect --format '{{.Architecture}}' myapp 确认镜像架构。
调试三板斧
# 1. 看镜像实际配置的入口
docker inspect myapp --format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}'
# 2. 绕过入口直接进容器看现场
docker run --rm --entrypoint sh myapp
# 进去后:ls 确认文件、head -1 xxx.sh | od -c 查 CRLF、which xxx 查 PATH
# 3. 精简 Dockerfile 里逐步执行,哪一步炸的一目了然
docker run --rm myapp ls /app
精简基础镜像(scratch / distroless)里往往没有 sh——此时 --entrypoint sh 本身就会报 not found。换带 shell 的调试标签镜像,或用 docker create + docker cp 把二进制拷出来用宿主机 file 检查。
ENTRYPOINT 与 CMD:shell 是怎么丢的
shell 形式(CMD npm start)跑 /bin/sh -c——PATH 查找正常但信号到不了 npm(PID 1 是 sh);exec 形式(CMD ["npm","start"])完全跳过 shell——绝对路径没问题,依赖 PATH 或 shell 展开的就坏。Windows 路径陷阱:在 Windows 上编辑的 Dockerfile 带尾随 \r——容器找名为 npm\r 的二进制。稳妥模式:exec 形式 + 绝对路径,.gitattributes 给 Dockerfile 标 LF。
把历史 docker run 迁到 Compose 时,entrypoint/command 字段同样要分清数组与字符串两种写法;可用 docker run 转 compose 对照,再人工确认是否误用了 shell 展开。
架构静默失败
错架构的二进制不总报 exec format error——加载器缺失时有时表现为 not found。容器内一条 file /app/binary 说真话:x86-64 还是 aarch64。交叉构建从 CI 的 arm 原生跑默认宿主、从 Mac 拷贝二进制进 linux/amd64 镜像,都是这类案的常客。Go/Rust 静态二进制记得在构建参数里锁 GOARCH / --target,不要「本机编完 COPY 进镜像」就当跨平台完成。
entrypoint 脚本:exec 或孤儿
自定义 entrypoint.sh 最后一行是 node server.js(没有 exec)时,sh 成了 PID 1——信号断路,docker stop 超时后升级 SIGKILL,优雅关闭没了。最后一行必须是 exec "$@"。它不直接造成 not found,但调试弯路通常两个症状一起过。
和「镜像里根本没有 shell」的差集
报错原文若是 exec: "bash": executable file not found,先看基础镜像:Alpine 默认只有 sh(BusyBox),Debian slim 才有 bash。ENTRYPOINT 写 ["/bin/bash", …] 对 Alpine 必炸。改写成 ["/bin/sh", …],或显式安装 bash——不要假设「Linux 都有 bash」。
排查清单
- 文件真的在吗?
--entrypoint sh进容器ls(精简镜像没有 sh 时换调试镜像) - 在但报 not found?
od -c查 shebang 的 CRLF(Windows 编辑的脚本九成是它) - ENTRYPOINT 是 exec 形式还想用
$VAR?改 shell 形式或用 entrypoint 脚本 - 路径相对的是 WORKDIR,不是你以为的目录;检查可执行位
exec format error→ 架构不匹配,buildx--platform重构建- 找不到
bash/npm→ 基础镜像 PATH 与包是否存在,不要抄别的发行版的绝对路径
排查注记
四类原因按概率排序,但 CRLF 单独标「最阴险」——它利用的是报错本身的误导性:报 no such file or directory 时文件往往就在那里,缺的是 shebang 指向的解释器,ls 确认文件存在只会让人更晕。文中明说多数工单在第一类(CRLF)或第四类(架构)就结束,所以验证命令选 od -c 看 shebang 的字节而不是 cat 看内容,就是为了绕开「肉眼看脚本明明在」这个陷阱。
本文由 ToolVault 工具匣 提供。相关工具:Dockerfile 生成器、docker run 转 compose、Linux 命令速查。相关阅读:Docker daemon 连不上排查、容器名冲突排查、磁盘不足排查。访问 首页 查看更多开发者工具。
相关工具
相关文章
Linux crontab 定时任务实战:命令、日志与排查
crontab 不只是写表达式:三个必会命令、/etc/cron.d 与用户 crontab 的区别、任务没跑的五步排查法、PATH/时区/% 转义、MAILTO,以及秒级任务的正确解法。
docker run 转 docker-compose 完全指南:为什么转、怎么转、参数对照表
把 docker run 命令迁移到 docker-compose.yml 的完整指南:compose 相比裸 run 的五大优势、参数逐项对照表(端口/卷/环境变量/重启策略/网络)、常见坑(裸 -e 透传、匿名卷、--rm 语义),附在线转换器用法。
Permission denied (publickey) 怎么解决?Git 推送失败的 6 种原因与修复
git clone/push 报 Permission denied (publickey) fatal: Could not read from remote repository?覆盖公钥没生成、没加载进 agent、没添加到 GitHub/Gitee、多账号配错密钥、deploy key 权限、remote URL 写错六种根因,附 ssh -v 诊断方法。