「MシリーズのMacでビルドしたイメージをサーバに持っていったら起動しない」「CIでは通るのに本番だけ落ちる」「node_modules をコピーしたら壊れた」——そしてログに出るのはたった一行、exec format error。
このエラーは情報量が極端に少ないので慌いますが、原因はほぼ4つに限られます。本記事では3行のコマンドで原因を特定する手順と、ケース別の直し方、そしてよく混同される別のエラーとの見分け方までを整理します。
・exec format error の正体は ENOEXEC。カーネルが「このファイルを実行形式として認識できない」と言っている
・実務では9割がCPUアーキテクチャの不一致(arm64 ↔ x86_64)
・切り分けは3行。uname -m で実行環境、file でバイナリ、docker image inspect でイメージのアーキを見る
・頑固な原因は node_modules や venv をアーキ違いのままコピーしていること。.dockerignore で防ぐ
・アーキと無関係な原因もある。shebangがないスクリプトを直接execveした場合
・CRLFはこのエラーではない。bad interpreter: No such file or directory になるので区別できる
ご環境に合わせた構成をご提案します。
お問合せはこちら
[目次を開く]
1. exec format error とは何か
このメッセージは、Linuxカーネルが返す ENOEXEC というエラー番号の表示です。意味はシンプルで、
「実行しようとしたファイルを、実行可能な形式として認識できなかった」
ということです。ファイルが存在しないわけでも、権限がないわけでもありません。ファイルはそこにあって実行権もあるのに、中身がカーネルの知っている形式ではない、という状態です。
Dockerでは次のような形で現れます。
exec /app/server: exec format error 古めのDockerだとこういう表示になることもあります。
standard_init_linux.go:228: exec user process caused: exec format error 2. まず3行で切り分ける
推測で直し始める前に、実行環境とバイナリのアーキテクチャを並べて見ます。これでほぼ決着します。
# ① 今動いている環境のアーキ
uname -m
# x86_64 → Intel/AMD 64bit
# aarch64 → ARM 64bit
# ② 問題のバイナリ/スクリプトの正体
file ./server
# ELF 64-bit LSB executable, x86-64 ... → x64 用
# ELF 64-bit LSB executable, ARM aarch64 ... → arm64 用
# ③ Docker イメージのアーキ
docker image inspect myapp:latest --format '{{.Os}}/{{.Architecture}}'
# linux/amd64 または linux/arm64 ①と②(もしくは③)が食い違っていたら、原因はアーキ不一致で確定です。
flowchart TB
A["exec format error"] --> B{"file の出力は<br>ELF バイナリか"}
B -->|"ELF だが<br>uname -m と違う"| C["アーキ不一致<br>ケース 1〜3"]
B -->|"ASCII text など<br>スクリプト"| D["shebang 欠落<br>ケース 4"]
C --> E{"どこで混ざったか"}
E -->|"docker image inspect が違う"| F["ケース 1<br>イメージのアーキ違い"]
E -->|"node_modules / venv をコピーした"| G["ケース 2<br>ネイティブモジュール"]
E -->|"CI だけで発生"| H["ケース 3<br>runner のアーキ"] 3. ケース1:Dockerイメージのアーキ違い
最も多いパターンです。Apple SiliconのMac(arm64)でビルドしたイメージを、x64のサーバやEC2に持っていって起動しない、というやつです。
応急処置:実行時にプラットフォームを指定する
docker run --platform linux/amd64 myapp:latest ただしこれはQEMUによるエミュレーションなので大幅に遅くなります。開発中の応急処置と割り切り、本番には使わないでください。
エミュレータが入っていないLinuxホストでは、先にbinfmtを入れる必要があります。
docker run --privileged --rm tonistiigi/binfmt --install all 根本対応:マルチアーキでビルドする
本番のアーキに合わせてビルドするのが正解です。buildx を使います。
# 両アーキ向けにビルドしてレジストリにpush
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myrepo/myapp:latest \
--push . こうしておけば、pullする側のアーキに合ったイメージが自動で選ばれます。x64サーバにしかデプロイしないのであれば、--platform linux/amd64 だけでビルドしても構いません。
4. ケース2:node_modules をアーキ違いで持ち込んだ
これが一番気づきにくいパターンです。イメージのアーキは正しいのに、中身のネイティブモジュールだけが違うアーキという状態です。
原因はほぼこれです。
# Dockerfile
COPY . . # ← ホストの node_modules ごとコピーされている Mac(arm64)で npm install してできた node_modules には、arm64用にビルドされたバイナリ(esbuild、sharp、rollup、node-gyp経由の.nodeファイルなど)が入っています。これをx64のイメージに持ち込むと壊れます。
直し方
# ホスト側の残骸を消して入れ直す
rm -rf node_modules package-lock.json
npm install
# ネイティブモジュールだけ再ビルドするなら
npm rebuild 二度と起こさない:.dockerignore
根本対策はホストの node_modules をイメージに入れないことです。
# .dockerignore
node_modules
npm-debug.log
.venv
__pycache__
*.pyc
.git コンテナ内で npm ci を走らせれば、そのアーキに合ったバイナリが入ります。
.venv や site-packages をそのままコピーすると、ビルド済みのwheel(.so)がアーキ違いで壊れます。numpyやPillowなどネイティブ拡張を持つパッケージで顕著です。こちらも .dockerignore で除外し、コンテナ内で pip install してください。 5. ケース3:CIとローカルでアーキが違う
「ローカルでは動くのにCIで落ちる」「CIでは通るのに本番で落ちる」ケースです。
GitHub Actionsでは、runnerのラベルでアーキが決まります。
| runner | アーキテクチャ |
|---|---|
ubuntu-latest / ubuntu-24.04 | x86_64 |
ubuntu-24.04-arm | arm64 |
macos-latest | arm64(Apple Silicon) |
なおarm64のホストrunnerは、パブリックリポジトリ向けが2025年8月にGA、プライベートリポジトリ向けも2026年1月に利用可能になっています。選択肢が増えた分、意図せず混在させてしまう事故も増えています。
本番がx64なのにMacでしか確認していない、という状態を避けるには、CIで本番と同じアーキをビルドします。
# .github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest # x64
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build for production arch
run: |
docker buildx build --platform linux/amd64 -t myapp:ci --load . 6. ケース4:shebangがない(アーキは無関係)
ここが見落とされがちです。アーキテクチャが完全に一致していても exec format error は出ます。
シェルスクリプトの1行目にshebang(#!/bin/bash など)がない場合、カーネルは「このファイルを何で実行すればいいのか」が判断できず、ENOEXECを返します。
# 1行目を確認する
head -1 entrypoint.sh
# なければ追加する
#!/bin/bash Dockerで ENTRYPOINT ["/app/entrypoint.sh"] のようにexec形式で書いていると、シェルを介さずに直接execveされるため、この問題が顔を出します。
混同されやすい別のエラー:CRLF
「Windowsで編集したスクリプトが動かない」とき、改行コード(CRLF)を疑うのは正しいのですが、CRLFは exec format error にはなりません。ここを混同すると切り分けを間違えます。
| 原因 | 出るメッセージ |
|---|---|
| shebangがない | exec format error(ENOEXEC) |
| アーキ不一致 | exec format error(ENOEXEC) |
| 改行がCRLF | /bin/bash^M: bad interpreter: No such file or directory |
| shebangのパスが間違っている | bad interpreter: No such file or directory |
つまり bad interpreter と出ているならアーキを疑う必要はありません。CRLFならこう直します。
# 確認(CRLF なら \r\n と出る)
file entrypoint.sh
# 変換
dos2unix entrypoint.sh
# dos2unix がなければ
sed -i 's/\r$//' entrypoint.sh 7. 予防:混在を起こさない仕組み
一度直しても、仕組みがなけれまた同じことが起きます。最低限この3つを入れておくと安全です。
① .dockerignore を必ず置く
ケース2の根絶です。リポジトリ作成時のテンプレートに入れてしまいましょう。
② Dockerfileでベースイメージのアーキを意識する
クロスビルドするなら、buildxが渡す変数を使えます。
FROM node:22 AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "building on $BUILDPLATFORM for $TARGETPLATFORM" ③ CIで本番と同じアーキをビルドする
ローカルのMacでしか確認しない運用をやめ、CIを「本番アーキでの検証の場」にします。
まとめ
-
exec format errorの正体は ENOEXEC。「ファイルを実行形式として認識できない」という意味で、ファイルの有無や権限の問題ではない - 切り分けは3行。
uname -m/file/docker image inspectを並べて見ればほぼ決着する - ケース1(イメージのアーキ違い)は
docker buildx build --platformで根本対応する。--platformでの実行は遅いので応急処置に留める - ケース2(
node_modules/.venvの持ち込み)が最も気づきにくい。.dockerignoreで除外してコンテナ内で入れ直す - ケース4の shebang欠落はアーキと無関係。
head -1で即座に確認できる - CRLFはこのエラーではなく
bad interpreterになる。メッセージで見分けられる
エラーメッセージが短いだけで、原因の範囲は狭いエラーです。まず3行切り分けを回してから手を動かしてください。
お問合せはこちら
よくある質問(FAQ)
Q. --platform linux/amd64 を付ければ解決では?
動くようにはなりますが、QEMUによるエミュレーションなので大幅に遅くなります。ビルドに数倍の時間がかかることもあります。開発中に一時的に使うのは良いですが、本番はターゲットアーキでネイティブにビルドしてください。
Q. Apple SiliconのMacでx64イメージを快適に動かす方法は?
Docker Desktopには、x86エミュレーションにRosettaを使うオプションがあります。QEMU単体よりは速くなりますが、それでもネイティブには及びません。Rosettaの仕組みはRosetta 2とは?で解説しています。
Q. イメージは正しいのにまだ出ます
ケース2を疑ってください。 イメージ自体のアーキが合っていても、中の node_modules や .venv が別アーキだと同じエラーになります。コンテナに入って file node_modules/**/*.node や file .venv/**/*.so を見ると一発でわかります。
Q. qemu-user-static と tonistiigi/binfmt はどちらを使う?
どちらもbinfmt_miscにエミュレータを登録するもので目的は同じです。Docker環境では tonistiigi/binfmt が手軽で、buildxのドキュメントでもこちらが案内されています。Docker Desktopを使っている場合は既定で有効なため、通常はどちらも不要です。
関連記事
📄ARM64とx64の違い・確認方法を解説|Windows/Mac/Linux対応【2026年版】
📄Rosetta 2とは?Apple SiliconでIntelアプリを動かす仕組みと終了時期|確認・有効化方法【2026年最新】
📄WSLが重い・メモリを食う問題を.wslconfigで直す完全ガイド【2026年版】
