Overview
The error “OCI runtime create failed: runc create failed: unable to start container process” is a wrapper around a more specific cause. The key is to read the trailing part of the message and then verify image entrypoints, mounts/permissions, cgroups, security profiles (SELinux/AppArmor/seccomp), and storage.
This guide provides quick diagnostics, reproducible examples, and targeted fixes for Docker hosts on Linux (and notes for WSL2/macOS).
Quickstart (fast triage)
- Reproduce and capture full error text.
- Run with debug logs enabled:
sudo dockerd --debug 2> /tmp/dockerd.debug & # or enable in daemon.json and restart docker run --rm alpine:3.20 echo OK - Note the final clause after “unable to start container process:” (e.g., exec not found, permission denied, invalid mount).
- Run with debug logs enabled:
- Check image entrypoint/cmd and availability.
docker inspect --format='Entrypoint={{.Config.Entrypoint}} Cmd={{.Config.Cmd}}' IMAGE - Validate volumes and permissions.
docker run --rm -v /host/path:/mnt alpine:3.20 sh -lc 'id && ls -l /mnt' - Verify cgroups/security/storage on the host.
docker info | sed -n '1,120p' df -h /var/lib/docker - If in doubt, temporarily relax security to confirm cause (then revert):
docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined alpine:3.20 true
Minimal working example (MWE)
Use this to confirm your host is healthy and to test entrypoints.
- Dockerfile:
FROM alpine:3.20
RUN adduser -D app
COPY run.sh /usr/local/bin/run.sh
RUN chmod +x /usr/local/bin/run.sh
USER app
ENTRYPOINT ["/usr/local/bin/run.sh"]
- run.sh:
#!/bin/sh
# Tiny sanity check
echo "Container OK as $(id -u):$(id -g)"
- Build and run:
docker build -t mwe:ok .
docker run --rm mwe:ok
If this succeeds, your runtime is generally fine. If your app image fails, compare its entrypoint, user, and mounts against this MWE.
Common causes and concrete fixes
1) Executable not found / wrong interpreter
Symptoms:
- Error ends with: exec: "app": executable file not found in $PATH
- Or: no such file or directory (missing interpreter) or exec format error (wrong architecture)
Fix:
- Ensure the binary/script exists and is executable.
docker run --rm -it IMAGE sh -lc 'which app || ls -l /path/to/app' - For scripts, check shebang and line endings (no CRLF):
docker run --rm -v "$PWD":/w -w /w alpine:3.20 sh -lc 'apk add --no-cache file && file -b run.sh' # If CRLF: convert locally dos2unix run.sh - Provide missing interpreter in image or change shebang (e.g., #!/usr/bin/env bash).
- Pull for correct architecture or set platform:
docker run --rm --platform=linux/amd64 IMAGE true
2) Permission denied on bind mounts
Symptoms:
- Error ends with: permission denied, operation not permitted
- A directory is mounted but container user cannot read/execute
Fix:
- Adjust ownership/permissions on host or run as a matching user:
sudo chown -R 1000:1000 /host/path chmod -R u+rx /host/path docker run --rm -u 1000:1000 -v /host/path:/data IMAGE - On SELinux hosts, label volumes:
docker run --rm -v /host/path:/data:Z IMAGE # or :z for shared among containers - Avoid mounting from noexec partitions. Check and remount without noexec:
mount | grep -E '/var/lib/docker|/tmp'
3) Invalid or mismatched volume sources
Symptoms:
- Error mentions: not a directory, file exists, or invalid argument
Fix:
- Ensure host paths exist and type matches the container expectation:
mkdir -p /host/dir docker run -v /host/dir:/app/dir IMAGE - Avoid trailing slashes that create dir vs file mismatches.
- Resolve symlinks to accessible real paths.
4) Security profiles (seccomp/AppArmor/SELinux)
Symptoms:
- Syscall denials; error may not show details.
Fix:
- Temporarily disable to confirm:
docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined IMAGE - For SELinux AVC denials, check:
Then relabel volumes (:Z/:z) or adjust policy.sudo journalctl -t setroubleshoot -t audit | tail -n 50
5) Cgroups driver or kernel support issues
Symptoms:
- Errors around cgroups, pids, devices, or freezer
Fix:
- Align Docker with systemd cgroups (recommended on modern distros):
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json >/dev/null <<'JSON' { "exec-opts": ["native.cgroupdriver=systemd"] } JSON sudo systemctl restart docker - On cgroup v2-only hosts, ensure recent Docker/Containerd/Runc versions.
- In WSL2:
wsl.exe --shutdownthen restart Docker Desktop.
6) Storage driver and disk pressure
Symptoms:
- Error may include overlayfs/aufs failures; low disk space
Fix:
- Ensure space and inodes:
df -h /var/lib/docker df -i /var/lib/docker - Prune safely:
docker system df docker system prune -af --volumes - Keep Storage Driver as overlay2 on Linux unless you have a reason otherwise.
7) Rootless Docker specifics
Symptoms:
- newuidmap/newgidmap errors, overlayfs permission denied
Fix:
- Install shadow-utils (subuid/subgid) and configure ranges:
grep "$(whoami)" /etc/subuid /etc/subgid || echo "$(whoami):100000:65536" | sudo tee -a /etc/subuid /etc/subgid - Use fuse-overlayfs if kernel lacks overlayfs for unprivileged users.
Diagnostics toolbox
- Get container create-time detail:
docker events --since 1m & docker run --name test IMAGE ... || true docker inspect test | sed -n '1,200p' docker logs test 2>&1 | tail -n +1 - Host logs:
sudo journalctl -u docker -n 200 --no-pager sudo journalctl -u containerd -n 200 --no-pager dmesg | tail -n 100
Symptom-to-fix quick map
| Symptom tail | Likely cause | Fast fix |
|---|---|---|
| executable file not found | Wrong entrypoint/path | Inspect, correct path, ensure executable bit |
| no such file or directory | Missing interpreter/CRLF | Install shell, fix shebang, dos2unix |
| permission denied | Mount perms/SELinux | chown/chmod, :Z, avoid noexec |
| invalid argument (mount) | Wrong host path/type | Create path, correct :ro/:rw, dir vs file |
| operation not permitted | Seccomp/AppArmor | Temporarily unconfine; update profile |
Pitfalls
- CRLF line endings in shell scripts cause “no such file or directory”.
- Mismatched CPU architecture triggers “exec format error”.
- Mounting from directories owned by root without proper permissions for a non-root container user.
- Relying on relative host paths with -v; use absolute paths.
- Running from a noexec filesystem (common in hardened hosts) blocks script execution.
Performance notes
- Keep images small and entrypoints simple to reduce cold-start overhead.
- Prefer overlay2 on ext4/xfs with ftype=1; slow storage increases container start latency.
- Avoid excessive bind mounts; deep directory trees can slow mount setup.
- Using seccomp=unconfined or privileged may mask issues but has security costs; use only for debugging.
Tiny FAQ
- How do I see the real root cause? Read the last clause of the error and check daemon logs with journalctl; runc’s message is appended there.
- Why does a file “exist” but still says not found? Often missing interpreter or CRLF endings; the kernel can’t load the script/binary.
- Can I fix this by using --privileged? It may mask security denials but is not a fix; identify and address the specific policy or permission.
- Does restarting Docker help? It can clear transient state:
sudo systemctl restart docker(and containerd). Useful after daemon.json changes. - What about Docker Desktop/WSL2? Restart the VM/WSL2 (
wsl.exe --shutdown), ensure enough disk, and check the Linux kernel version inside the VM.