Fix Podman `exec format error`

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Linux hosts with Podman where a container fails to start because the runtime cannot execute the entrypoint binary or script
Privilege Rootful examples as root; architecture and shebang behavior is the same rootless
Scope Diagnosing exec format error from crun — host versus image Architecture, explicit --arch and --platform pulls, multi-architecture manifest inspection, missing shebang scripts, wrong interpreter paths, CRLF shebang lines, ENTRYPOINT and Cmd verification, and foreign-architecture binaries inside otherwise correct images. Does not cover multi-architecture image builds, QEMU installation, or general shell scripting.
Related guides Write a Containerfile
Build images with podman build

Podman starts a container by asking crun to execute the image ENTRYPOINT or Cmd. When the Linux kernel cannot run that file, you get exec format error. That message sounds generic, but it almost always narrows to one of two causes: the binary targets the wrong CPU architecture for this host, or a text script is being executed directly without a shebang interpreter line.

IMPORTANT
This article diagnoses startup failures where the executable format is wrong. It does not cover every ENTRYPOINT failure — missing files, permission bits, and exit codes belong in Fix container exits immediately. For building and publishing multi-architecture manifest lists, see Create multi-architecture images.

What exec format error means

These are the two failure shapes you will see most often on Podman 5.8.2 with crun. First, a text script executed without a shebang:

bash
podman run --rm -v /tmp/exec-lab/run.sh:/run.sh:Z docker.io/library/alpine:3.20 /run.sh
output
{"msg":"exec container process `/run.sh`: Exec format error","level":"error","time":"2026-08-23T09:42:02.230533Z"}

Second, an ARM64 binary on an amd64 host:

bash
podman run --rm localhost/podman-hello-arm64-test:latest
output
WARNING: image platform (linux/arm64) does not match the expected platform (linux/amd64)
{"msg":"exec container process `/usr/local/bin/podman_hello_world`: Exec format error","level":"error","time":"2026-08-23T09:42:00.675583Z"}

Podman 5.x may also surface a one-line wrapper such as Error: crun: executable file ...: Exec format error: OCI runtime error. The JSON msg and the crun wrapper describe the same kernel rejection — treat them as one symptom and split architecture mismatch from script interpreter problems before you rebuild the image.


Cause 1: image architecture does not match the host

The kernel executes native machine code for the host CPU. An arm64 ELF binary on an x86_64 host fails unless user-mode emulation is registered.

Check the host kernel architecture:

bash
uname -m

Sample output on the lab machine:

output
x86_64

Podman records the same value under a different label:

bash
podman info --format '{{.Host.Arch}}'
output
amd64

x86_64 and amd64 name the same 64-bit Intel/AMD platform. Compare that to the image you are trying to run.


Check image architecture with podman image inspect

Replace IMAGE with your local tag or ID:

bash
podman image inspect --format '{{.Architecture}}' IMAGE

On a healthy amd64 Alpine copy already on the lab host:

output
amd64

When host and image disagree, you have an architecture mismatch even if the image pulled successfully:

text
host:  amd64
image: arm64

Storing a foreign-architecture image locally does not make it runnable. The failure appears at container start, not during podman pull.


Reproduce architecture mismatch on amd64

Docker Hub rate limits blocked docker.io/library/alpine:3.20 pulls during parts of this lab, so the mismatch demo uses quay.io/podman/hello:latest, which publishes both amd64 and arm64 entries.

Pull the ARM64 variant explicitly and give it a dedicated local name so you do not overwrite your working amd64 tags:

bash
podman pull --arch arm64 quay.io/podman/hello:latest

Tag the foreign-architecture copy for testing:

bash
podman tag quay.io/podman/hello:latest localhost/podman-hello-arm64-test:latest

Confirm what you stored:

bash
podman image inspect --format '{{.Architecture}}' localhost/podman-hello-arm64-test:latest
output
arm64

Run it on the amd64 host without QEMU user-static emulation (qemu-user-static is not installed on this lab machine, and binfmt_misc has no foreign-arch handlers):

bash
podman run --rm localhost/podman-hello-arm64-test:latest
output
WARNING: image platform (linux/arm64) does not match the expected platform (linux/amd64)
{"msg":"exec container process `/usr/local/bin/podman_hello_world`: Exec format error","level":"error","time":"2026-08-23T09:39:02.355892Z"}

That is the architecture branch — not a broken ENTRYPOINT path.


Inspect a multi-architecture manifest

Official images often ship as a manifest list. Inspect the index before you blame Podman for picking the wrong platform:

bash
podman manifest inspect quay.io/podman/hello:latest

Sample output (trimmed):

output
{
    "schemaVersion": 2,
    "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
    "manifests": [
        {
            "platform": {
                "architecture": "amd64",
                "os": "linux"
            }
        },
        {
            "platform": {
                "architecture": "arm64",
                "os": "linux"
            }
        },
        ...
    ]
}

Normally Podman selects the entry that matches the host. Problems appear when:

  • you explicitly pulled --arch arm64 (or another foreign architecture) on the host CPU
  • a private tag contains only one foreign architecture
  • CI built and pushed binaries for the wrong platform

Pull the correct architecture

Request the host-native platform explicitly:

bash
podman pull --arch amd64 quay.io/podman/hello:latest

Platform syntax selects OS and architecture together:

bash
podman pull --platform linux/amd64 quay.io/podman/hello:latest

Verify before you run:

bash
podman image inspect --format '{{.Architecture}}' quay.io/podman/hello:latest
output
amd64

A matching architecture should start cleanly:

bash
podman run --rm quay.io/podman/hello:latest

Podman prints the hello banner and exits 0 when the binary matches the host.


--arch versus --platform

Both flags choose which manifest entry Podman downloads. They do not convert binaries after the pull.

Flag Selects
--arch arm64 CPU architecture entry
--platform linux/arm64 OS and architecture pair (linux + arm64)

Use --platform when you need to pin OS as well as CPU — for example linux/arm64 versus windows/amd64. Building manifest lists and publishing multi-architecture tags is covered in Create multi-architecture images.


QEMU and binfmt emulation

Cross-architecture execution can work when the host registers QEMU user emulators through binfmt_misc and packages such as qemu-user-static. On the RHEL 10.2 lab host used here:

  • qemu-user-static is not installed
  • binfmt_misc exposes only register and status — no qemu-aarch64 handler

This article reproduces the wrong-architecture failure honestly. It does not demonstrate a successful emulated arm64 run because that would require separately configured emulation outside this lab. If you need foreign architectures on amd64, plan native builders, CI on matching hardware, or install and register QEMU before expecting podman run to succeed.


Cause 2: script has no shebang

Linux executes scripts by reading the shebang (#!) interpreter on the first line. Without it, the kernel treats the file as a binary and rejects the text with exec format error.

Create a lab directory and write a script with no shebang:

bash
mkdir -p /tmp/exec-lab

Write the script body without a #! line:

bash
printf 'echo "hello"\n' > /tmp/exec-lab/run.sh

Make it executable:

bash
chmod +x /tmp/exec-lab/run.sh

Run the script directly as the container command:

bash
podman run --rm -v /tmp/exec-lab/run.sh:/run.sh:Z docker.io/library/alpine:3.20 /run.sh
output
{"msg":"exec container process `/run.sh`: Exec format error","level":"error","time":"2026-08-23T09:36:21.878701Z"}

The mount path and chmod +x are correct — the missing interpreter line is the problem.


Fix the missing shebang

Add #!/bin/sh as the first line:

bash
printf '#!/bin/sh\necho "hello"\n' > /tmp/exec-lab/run.sh

Keep the executable bit and rerun the same publish path:

bash
chmod +x /tmp/exec-lab/run.sh

The shebang line is in place now, so the same podman run path should print hello:

bash
podman run --rm -v /tmp/exec-lab/run.sh:/run.sh:Z docker.io/library/alpine:3.20 /run.sh
output
hello

The same script content with a shebang is valid. Rebuild or recopy the script into your image ENTRYPOINT when the failure happens at deploy time, not only on a bind mount.


Wrong shebang interpreter

A shebang pointing at a binary that does not exist in the image produces a different error — usually no such file or directory, not exec format error:

Recreate the lab directory and point the shebang at a path that does not exist:

bash
mkdir -p /tmp/exec-lab

Write a script that names a nonexistent interpreter:

bash
printf '#!/bin/missing-interpreter\necho hello\n' > /tmp/exec-lab/run.sh

Mark the script executable:

bash
chmod +x /tmp/exec-lab/run.sh

Run it the same way as the no-shebang test:

bash
podman run --rm -v /tmp/exec-lab/run.sh:/run.sh:Z docker.io/library/alpine:3.20 /run.sh
output
{"msg":"exec container process (missing dynamic library?) `/run.sh`: No such file or directory","level":"error","time":"2026-08-23T09:41:04.363244Z"}

A #!/bin/bash line on an image without Bash follows the same pattern. Alpine normally provides /bin/sh through BusyBox ash; /bin/bash is not present unless Bash was installed separately. Therefore a script with #!/bin/bash can fail with no such file or directory even though /bin/sh exists. Verify interpreters inside the image you ship:

bash
podman run --rm docker.io/library/alpine:3.20 sh -c \
  'ls -l /bin/sh; command -v bash || echo "bash not installed"'

Typical output on Alpine 3.20:

output
lrwxrwxrwx    1 root     root            12 Jan 26  2024 /bin/sh -> /bin/busybox
bash not installed

Do not classify every ENTRYPOINT failure as exec format error when the transcript names a missing interpreter path.


Windows CRLF shebang lines

A shebang copied from Windows may embed a carriage return:

text
#!/bin/sh\r

That makes the kernel look for /bin/sh\r, which does not exist. The error is usually no such file or directory or a bad interpreter message — not always the literal exec format error string.

Inspect line endings on the host:

bash
file run-crlf.sh
output
run-crlf.sh: POSIX shell script, ASCII text executable, with CRLF line terminators

file flags CRLF terminators; sed -n l shows the hidden carriage returns:

bash
sed -n l run-crlf.sh
output
#!/bin/sh\r$
echo hello\r$

Inside a container, mount the CRLF script and run it as the entry command:

bash
podman run --rm -v /tmp/exec-lab/run-crlf.sh:/run.sh:Z docker.io/library/alpine:3.20 /run.sh
output
{"msg":"exec container process (missing dynamic library?) `/run.sh`: No such file or directory","level":"error","time":"2026-08-23T09:36:27.797419Z"}

Convert to Unix line endings (dos2unix, sed, or editor settings) before you bake the script into an image.


Check ENTRYPOINT and Cmd

When the architecture matches and the shebang looks correct, confirm what Podman is actually trying to execute:

bash
podman image inspect --format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}' IMAGE

On Alpine 3.20 in the lab:

output
null ["/bin/sh"]

A null entrypoint with Cmd set means Podman runs /bin/sh when you start the Alpine image with no command override. For a failing named container, inspect the resolved process:

bash
podman inspect CONTAINER --format '{{.Config.Entrypoint}} {{.Config.Cmd}}'

Compare that path to files that exist in the image (podman run --rm IMAGE ls -l /path/to/binary).


Wrong-architecture binary inside a correct base image

The base image architecture can be amd64 while a single copied application binary targets arm64 — common after Go or Rust cross-compilation mistakes. The whole image inspect reports amd64, but one ENTRYPOINT binary still triggers exec format error.

Check the binary on the build host or extract it from the image:

bash
file ./app

Example output for a mismatched build artifact:

output
./app: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, ...

Rebuild the application for the deployment architecture, or ship separate per-architecture images in a manifest list. Image-level Architecture metadata alone is not enough when you add foreign binaries in a later layer.


Diagnostic decision tree

text
exec format error
uname -m  and  podman image inspect .Architecture
   ├─ mismatch → pull/build correct platform; emulation only if QEMU/binfmt configured
   └─ same
inspect ENTRYPOINT/Cmd
text script executed directly?
   ├─ no shebang → add #!/bin/sh (or correct interpreter)
   ├─ CRLF shebang → convert to LF
   ├─ missing interpreter path → no such file (fix path or install binary)
   └─ native binary → file(1) on binary; verify build arch

Troubleshooting

Symptom Likely cause Fix
exec format error right after podman run arm64 image on amd64 host (or reverse) podman pull --arch amd64 or --platform linux/amd64; build on native CPU
WARNING: image platform ... does not match Foreign-architecture image on host Pull host-native manifest entry
exec format error on a .sh file No shebang Add #!/bin/sh or invoke sh /path/script.sh
no such file or directory on script Bad shebang path or CRLF ls -l interpreter; file / dos2unix the script
Worked in CI, fails on laptop Image built for CI CPU only Inspect .Architecture; check manifest list
Base amd64 but one binary fails Wrong-arch artifact copied in file on binary; rebuild for target arch
podman build --platform linux/arm64 RUN fails on amd64 No emulation during build Native ARM builder or QEMU — see multi-arch guide

References


Summary

exec format error means the Linux kernel refused to execute the file crun was asked to start. Split the problem into architecture mismatch versus script format before you rebuild unrelated layers. Compare uname -m and podman info host architecture to podman image inspect --format '{{.Architecture}}'. An explicit --arch arm64 pull on amd64 stores a valid image that still fails at podman run unless QEMU and binfmt_misc are configured — the lab reproduced that failure with quay.io/podman/hello and did not fake emulated success.

When architectures match, inspect ENTRYPOINT and Cmd, then read the target file. Text scripts executed without #!/bin/sh produce the same error string as a wrong CPU binary. Missing interpreters and CRLF shebangs usually surface as no such file or directory instead. A single foreign binary inside an otherwise amd64 image can still fail — use file on the artifact.

For manifest lists, --platform pulls, and assembly workflows, continue with Create multi-architecture images. For pull flags and registry behavior, see Pull images with podman pull.


Frequently Asked Questions

1. What causes Podman exec format error?

The Linux kernel cannot execute the file Podman asked crun to start. The two common causes are an architecture mismatch between host CPU and container binary, or a text script run as an executable without a shebang interpreter line. Compare uname -m and podman image inspect Architecture before you change ENTRYPOINT or rebuild unrelated layers.

2. Can I run an arm64 Podman image on an amd64 host?

Not without QEMU user-mode emulation and binfmt_misc registration. Pulling an arm64 image stores foreign-architecture bytes locally but does not make them runnable on amd64. Pull linux/amd64 or build on native hardware for the target CPU.

3. How do I fix exec format error on a shell script in a container?

Add a shebang such as #!/bin/sh as the first line, ensure the interpreter path exists in the image, and convert Windows CRLF line endings to Unix LF. A script with no shebang executed directly produces exec format error because the kernel tries to run the text file as a binary.

4. What is the difference between exec format error and no such file or directory?

Exec format error means the kernel rejected the file format, often wrong CPU architecture or a script without shebang. No such file or directory on a script usually means the shebang interpreter path does not exist, or CRLF line endings made the interpreter path invalid. Check ls -l on the interpreter and run file or sed -n l on the script.

5. Does podman pull --arch arm64 make the image runnable on amd64?

No. --arch and --platform select which manifest entry to download. They do not translate binaries. On an amd64 host without emulation, an arm64 image fails at container start with exec format error even though podman image inspect reports Architecture arm64.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)