Docker 容器启动就退出,日志提示 exec /bin/sh: exec format error?如果镜像刚从 Apple 芯片 Mac、ARM 开发机或另一台服务器迁过来,先核对 VPS、镜像和镜像内程序的架构是否一致,别先重装 Docker。
但同一句 exec format error 也可能来自入口脚本格式。本文按“失败的文件是谁 → 运行目标是什么 → 镜像包含什么”的顺序定位,适用于 Linux VPS 上的 Docker 与 Compose 部署。示例仓库名 yourname/myapp、版本标签和 app 服务名都要换成自己的值。
| 现象 | 优先检查 | 不要急着做什么 |
|---|---|---|
| 拉取时报 no matching manifest | 该标签有没有目标平台版本 | 反复重启 Docker |
运行 /bin/sh 就报 exec format error | 镜像平台、模拟器是否可用 | 给入口脚本 chmod 777 |
自己的 /app/server 无法执行 | 二进制系统与 CPU 架构 | 只改镜像标签或 platform |
entrypoint.sh 执行失败 | 首行解释器、换行和文件头 | 一律判断为 ARM 问题 |
| permission denied | 执行权限、挂载与安全策略 | 当作同一种格式错误 |
先保留原始错误和失败文件路径:
docker compose ps -a
docker compose logs --tail=80 app
进程尚未成功启动时,应用日志可能为空,还要看启动命令的错误输出。如果只是一直重启、尚未明确错误,可先对照 容器 Restarting 排查。
在目标 VPS 上执行,而不是在自己的 Mac 上执行:
uname -m
docker context show
docker info --format '{{.OSType}}/{{.Architecture}}'
docker image inspect yourname/myapp:2026-10-23 --format '{{.Os}}/{{.Architecture}}'
image inspect 检查本地已有镜像,镜像未拉取时会报不存在;它不会替你查询仓库中的所有平台。Docker inspect 文档说明了本地镜像元数据的查看方式。
常见对应关系:
| 系统报告 | 镜像平台中的常用写法 | 含义 |
|---|---|---|
| x86_64 | linux/amd64 | 64 位 x86,Intel 和 AMD 都可能使用 |
| aarch64 | linux/arm64 | 64 位 ARM |
| armv7l | linux/arm/v7 | 32 位 ARM,不能当作 arm64 |
还要看 Docker context:客户端可能连着另一台机器,uname -m 与实际 Docker 服务端不一定在同一主机。发现不一致时,先确认操作的是哪台 Docker,而不是立刻改服务器系统。
Apple 芯片 Mac 的默认本机构建容易产出 ARM64 镜像,常见 x86 VPS 则需要 AMD64;但使用了远程构建器或指定了平台的项目可能不同。以元数据为准,不能仅凭“在 Mac 上构建”就下结论。
使用 Buildx 查看远端标签:
docker buildx imagetools inspect yourname/myapp:2026-10-23
检查输出是否包含目标平台,例如 linux/amd64 或 linux/arm64。这是查询远端 manifest,与本地 docker image inspect 的用途不同;私有仓库需先按自己的权限登录。命令不可用时先检查 Buildx 是否安装。官方 imagetools inspect 文档提供了输出结构。
若标签根本没有目标平台,no matching manifest 是平台缺失,不能当成下载网络超时处理。要使用官方提供的兼容版本,或重新构建自己的应用镜像。单架构镜像的输出形式可能与多平台索引不同,也不能因为没看见长长的列表就判断镜像损坏。
只有一台 AMD64 VPS 时,可以在自己的构建环境指定一个目标平台:
docker buildx build \
--platform linux/amd64 \
-t yourname/myapp:2026-10-23-amd64 \
--push .
这是把构建结果推送到你拥有写权限的仓库,执行前替换仓库名并完成登录;ARM64 VPS 应改用对应平台。保留旧版本标签,先在测试实例验证新版本,不要覆盖唯一的回滚入口。Docker buildx build 文档说明了平台与输出选项。
如果只想把单平台构建结果放进当前 Docker 的本地镜像库,可以把 --push 换成 --load。不同构建驱动、镜像存储对多平台本地加载的支持不同,不能把单平台示例直接套成“多平台必定能 load”。
需要同时提供 AMD64 和 ARM64 时:
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t yourname/myapp:2026-10-23 \
--push .
先用 docker buildx ls 检查构建器能力。原生节点、交叉编译、QEMU 模拟都可能参与跨平台构建,不能认为安装 Buildx 就能运行任意目标程序。Docker 多平台构建指南也提醒,模拟下的编译和压缩可能明显慢于原生执行。
尤其留意 Dockerfile:如果只是 COPY 一个已在 Mac 上编译的可执行文件进 Linux 镜像,--platform linux/amd64 不会把这个文件转换成 Linux AMD64 程序。Go、Rust、含原生扩展的 Python/Node 依赖都要按各自工具链为目标平台构建。镜像元数据正确,不代表里面每个二进制都正确。
在确认镜像确实有对应版本后,可以显式固定服务平台:
services:
app:
image: yourname/myapp:2026-10-23-amd64
platform: linux/amd64
restart: unless-stopped
Compose platform 定义用于指定服务运行平台,并参与镜像版本选择。它不会转换一个 ARM 文件,也不会自动保证宿主机已经安装模拟能力。在 ARM VPS 上强制选择 AMD64,本质上可能仍依赖模拟;生产服务通常应优先匹配原生平台。
确认原项目的配置文件、环境参数和数据挂载后,在维护窗口执行:
docker compose config --quiet
docker compose pull app
docker compose up -d --no-deps app
docker compose ps app
这些命令假设在真实项目目录,且依赖服务已正常运行。自定义了 -f、--env-file 或项目名时,保持与原部署一致。只执行 restart 不会把服务切到新的镜像配置,详见 Compose 配置修改不生效排查。重建前确认持久数据已挂载并有备份,不需要删除 Volume。
在源码目录检查入口文件:
file entrypoint.sh
head -n 1 entrypoint.sh
od -An -tx1 -N32 entrypoint.sh
一个需要直接执行的 Shell 脚本应有正确解释器声明,例如第一行 #!/bin/sh,且该解释器确实存在于镜像。Bash 语法脚本不能仅把首行改成 sh 就算兼容。Linux execve 手册说明了解释器脚本的文件格式,以及错误架构等情况下的执行失败。
检查 UTF-8 BOM、Windows CRLF 和执行位。CRLF 可能把回车混进解释器路径,表现为解释器找不到等错误,不一定与错误架构完全相同;permission denied 则还要单独看权限。先备份,在编辑器中将文件保存为无 BOM 的 LF 格式,再用项目需要的解释器检查语法。不要直接对二进制文件批量替换换行。
如果容器一启动就退出,docker exec 通常进不去。应先检查构建源码;需要取出镜像内文件时,使用专门的临时容器和 docker cp,不要在正在运行的生产容器里反复改文件,形成无法复现的修补。
确认目标 Docker 服务端架构、远端标签平台和镜像内程序一致。更新后不只看 running,还要测试一个真实业务请求,并检查日志是否仍有格式错误。涉及持久数据的服务,确认挂载和数据都正常;保留旧标签及配置,便于回滚。
本文的示例命令用于定位问题,不代表已经在两种 CPU 的 VPS 上完成实机部署。修复顺序是先看失败文件、再对照平台、最后检查脚本与构建链,不能只靠在 Compose 里补一行 platform。
