Home DevOps OCI Runtime Create Failed: Fix runc Errors
Intermediate 6 min · September 23, 2026

OCI Runtime Create Failed: Fix runc Errors

Fix the bad mount path or entrypoint named in the runc error, and re-pull the image if a layer is corrupt.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Production
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
Before you start⏱ 12 min
  • Docker installed with a working daemon (docker info succeeds)
  • A terminal where you can run docker commands and read error output
  • Basic familiarity with docker run flags like -v and --entrypoint
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • This error means runc couldn't assemble the container before your app ever started — read past the first line, the real cause sits at the end of the message
  • Bad bind mounts are cause number one: the host path in -v or --mount doesn't exist or has wrong permissions, so create the directory first
  • A wrong entrypoint or command is cause two: override it with --entrypoint sh to test, then fix the Dockerfile or run flags
  • If mounts and entrypoint look right, the image layer is likely corrupt: docker rmi the image and docker pull it fresh
✦ Definition~90s read
What is OCI Runtime Create Failed Fix?

Container startup is a relay race with four runners. dockerd validates your request and hands it to containerd, which unpacks the image layers into a root filesystem and calls the OCI runtime — runc on standard Linux hosts. runc does the low-level assembly: it creates namespaces (PID, mount, network), applies cgroups (CPU and memory limits), mounts the rootfs plus every -v and --mount bind, then execs your entrypoint as PID 1. Only if all of that succeeds does your application start. "OCI runtime create failed" means runc dropped the baton during assembly — namespaces, mounts, cgroups, or exec — so no process ever ran.

Picture a theater crew building a stage before the actors arrive.

Each sub-message names its layer: "mounting /data/uploads to rootfs caused: no such file" is a bind problem; "exec: ./start.sh: no such file or directory" is an entrypoint problem; cgroup complaints point at driver mismatch; checksum or "failed to register layer" points at corruption. The fix always lives in the inputs to runc — your flags, your host paths, your daemon config, your image bytes — never in code runc never ran.

What it is NOT: it isn't your application crashing (crashes produce exit codes and logs, not create failures), it isn't a registry or pull problem (pulls fail earlier with their own errors), and it isn't a resource shortage (OOM kills running containers; it doesn't block creation). Don't add retry loops around docker run or rebuild your app — re-read the tail of the error and fix the assembly input it names.

Plain-English First

Picture a theater crew building a stage before the actors arrive. The blueprint says: put a door here, then send in the lead actor. If the door was never delivered, the crew stops cold — the play never starts. runc is that crew, your image and run flags are the blueprint, and your app is the actor who never got on stage. Don't coach the actor. Fix the blueprint: deliver the missing door (the mount path), correct the actor's name (the entrypoint), or replace a smudged blueprint (a corrupt layer).

You run docker run, and instead of logs you get a wall of text ending in "OCI runtime create failed: runc create failed: unable to start container process." Your app never printed a line. Your code never executed. The failure happened in the few hundred milliseconds between the daemon accepting your request and your process starting — inside the runtime layer most engineers never look at. The instinct is to debug the application. That's the wrong layer entirely.

This error is Docker telling you the container couldn't be assembled: a mount source that doesn't exist, an entrypoint binary that isn't there, a cgroup setting the kernel rejected, or an image layer that fails its checksum. The message is verbose because three layers (daemon, containerd shim, runc) each add their complaint, but the actual cause is almost always in the last two lines.

This guide teaches you to read the error tail first, then work the four usual suspects in order: bind paths, entrypoint, cgroup driver, and corrupt layers. You'll get the inspect commands that reveal each one and the fixes that hold up in production.

Read the Tail: Three Layers, One Real Complaint

An OCI create error reads like three errors stacked in a trench coat. dockerd wraps containerd's message, containerd wraps the shim's, and the shim wraps runc's — so the first line you see ("docker: Error response from daemon") carries zero information. Scroll to the last two lines. That's runc speaking, and runc is specific: it names the syscall, the path, and the errno. "mounting /data/uploads to rootfs at /app/uploads caused: stat /data/uploads: no such file or directory" is a complete diagnosis in one sentence, if you read it instead of the wrapping.

Build the habit of capturing the full text: docker run ... 2>&1 | tail -5 preserves the tail while the terminal scrollback eats it. Then classify the tail into one of four buckets — mount, exec, cgroup, or layer — because each bucket has its own section below with its own commands. Guessing across buckets is how a 5-minute mount fix becomes a 2-hour image rebuild saga.

The second habit: reproduce minimally. Strip your 200-line compose service down to docker run with just the suspect flag. If docker run -v /data/uploads:/app/uploads:ro myapp:latest true fails the same way, you've isolated the mount from every other variable. Minimal reproduction is the difference between debugging one thing and debugging twelve things that share an error message.

🔥The Tail Is the Error
Everything above the last two lines is wrapping added by daemon, containerd, and shim. Train yourself to read bottom-up: the final runc line names the syscall, the path, and the errno. If you only remember one habit from this guide, make it this one.
📊 Production Insight
Engineers routinely paste only the first line (Error response from daemon) into chat and get generic advice. Teams that mandate the full tail in incident threads cut their container-start MTTR roughly in half.
🎯 Key Takeaway
Read bottom-up: the runc tail names the failed step. Classify it as mount, exec, cgroup, or layer, then reproduce with a minimal docker run.

Bad Bind Mounts: the Missing Host Path

Bind mounts graft a host path into the container, and runc stats the source before mounting. If the directory doesn't exist, the create fails — modern Docker refuses to auto-create host paths for explicit --mount binds, and even -v behavior varies by driver. The path might be a typo (/data/upload vs /data/uploads), a volume that was never mounted on this host, an NFS share that didn't come back after reboot, or a directory the migration script skipped. The error names the exact source path, so there's no detective work — just go look at it.

Check with ls -ld on the literal path from the error, on the host where it failed. Partial-fleet failures are the signature here: the path exists on 9 nodes and not on 3, because storage, migrations, and manual fixes never apply evenly. Also check permissions: runc runs the mount as root, but a :ro bind onto a directory the container user can't read fails later at access time, and SELinux hosts need the :z or :Z relabel flag or the mount is denied outright.

Prefer named volumes over host binds for anything the app writes — volumes are daemon-managed and exist on every host identically. When you must bind (configs, device nodes, legacy data), declare the path in config management and add a pre-deploy stat check on every target node. A one-line test -d in the deploy script would have caught the Friday incident before a single container failed.

bind-mount-diagnosis.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Reproduce with the suspect flag only
docker run --rm -v /data/uploads:/app/uploads:ro myapp:latest true 2>&1 | tail -3

# Inspect the host source exactly as runc sees it
ls -ld /data/uploads
stat -c 'owner=%U perms=%a' /data/uploads
mount | grep -E 'uploads|nfs ' || echo 'no matching mount'

# SELinux host denying the mount? Check the audit trail
sestatus 2>/dev/null | head -2
sudo ausearch -m avc -ts recent 2>/dev/null | tail -5

# Fix: create with the UID your container user expects
docker inspect --format '{{.Config.User}}' myapp:latest
sudo mkdir -p /data/uploads && sudo chown 1000:1000 /data/uploads

# Retry with shared-content relabel on SELinux hosts
docker run --rm -v /data/uploads:/app/uploads:ro,Z myapp:latest true
📊 Production Insight
Bind paths are host facts, and hosts drift. One fleet had /data/uploads on 9 nodes and bare metal on 3 after a skipped migration — the mount error named the path the whole time.
🎯 Key Takeaway
ls -ld the exact source path on the failing host. Create it with the right UID, handle SELinux relabels, and prefer named volumes for portable data.

Invalid Entrypoint: the Binary That Isn't There

After mounts succeed, runc execs your entrypoint as PID 1. If that path doesn't exist inside the image, isn't executable, or names an interpreter that's missing, creation fails with an exec error — "no such file or directory" even when the file visibly exists, which confuses everyone once. The classic trap: the script exists but its shebang names /bin/bash in an Alpine image that only ships /bin/sh, or the file lost its execute bit in a COPY from Windows. The kernel reports the interpreter as missing, and the message points at your script.

Diagnose from outside the container. docker inspect --format prints the image's declared Entrypoint and Cmd without running anything — compare that against your run flags, because CLI args replace Cmd and --entrypoint replaces Entrypoint, and the merged result is what runc actually execs. Then test with docker run --rm --entrypoint sh to get a shell: ls -l the entrypoint path, read its shebang line, and try executing it by hand. If the shell override itself fails, stop — the image is corrupt, not misconfigured.

Fix at the source. Use exec-form ENTRYPOINT with absolute paths, pin the interpreter your base image actually ships, and keep ENTRYPOINT scripts to a POSIX sh subset unless you've verified bash exists. In CI, add a smoke step that runs the image with --entrypoint sh -c 'test -x /app/start.sh' so a broken entrypoint fails the build, not the deploy.

entrypoint-diagnosis.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# What will runc actually exec? (image defaults + your flags merged)
docker inspect --format 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}}' myapp:latest

# Get a shell in and interrogate the entrypoint by hand
docker run --rm --entrypoint sh myapp:latest -c 'ls -l /app/start.sh; head -1 /app/start.sh'

# Missing interpreter? Compare shebang vs what the image ships
docker run --rm --entrypoint sh myapp:latest -c 'ls -l /bin/sh /bin/bash 2>&1'

# Lost exec bit (common after COPY from Windows checkouts)?
docker run --rm --entrypoint sh myapp:latest -c 'test -x /app/start.sh && echo EXEC-OK || echo MISSING-X-BIT'

# Fix in the Dockerfile, not at runtime:
# ENTRYPOINT ["/app/start.sh"] with '#!/bin/sh' first line + chmod +x
📊 Production Insight
The weirdest entrypoint failures come from Windows checkouts: CRLF line endings turn the shebang into /bin/sh^M, which doesn't exist. A .gitattributes rule forcing LF on *.sh ends the whole class.
🎯 Key Takeaway
Inspect the declared entrypoint, test with an sh override, verify interpreter and exec bit — then fix the Dockerfile so the image is correct by construction.

Cgroup Driver Mismatch: systemd vs cgroupfs

On a plain docker run host, cgroup settings rarely bite. Under Kubernetes they bite hard. The kubelet and the container runtime must use the same cgroup driver — systemd on every modern distro — or container creation fails with cgroup-flavored errors about paths, parents, or permissions under /sys/fs/cgroup. The mismatch usually arrives via an old daemon.json carrying native.cgroupdriver=cgroupfs, or a kubelet installed from a guide written for the cgroupfs era. Docker Desktop and fresh installs default correctly, which is why the bug only appears on hand-configured nodes.

Confirm both sides independently. docker info --format '{{.CgroupDriver}}' prints the daemon's driver; on the node, check the kubelet config or ps aux | grep kubelet for --cgroup-driver. If they disagree, change the daemon side — Kubernetes standardized on systemd years ago, so the daemon should follow. Set exec-opts to native.cgroupdriver=systemd in /etc/docker/daemon.json, validate the JSON, restart dockerd, and re-check docker info.

Treat driver config as fleet state, not per-node folklore. Manage daemon.json through config management, assert the driver in node conformance checks, and be suspicious of any tutorial that sets cgroupfs — it's a time traveler from 2019. After the fix, redeploy a test pod on each touched node; cgroup changes only affect containers created after the restart.

cgroup-driver-fix.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Both sides must agree (want: systemd on each)
docker info --format 'docker cgroup driver: {{.CgroupDriver}}'
ps aux | grep '[k]ubelet' | grep -o '\-\-cgroup-driver=[a-z]*'

# Align the daemon to systemd via daemon.json
sudo python3 - <<'EOF'
import json
p = '/etc/docker/daemon.json'
try:
    cfg = json.load(open(p))
except FileNotFoundError:
    cfg = {}
cfg['exec-opts'] = ['native.cgroupdriver=systemd']
json.dump(cfg, open(p, 'w'), indent=2)
print(open(p).read())
EOF

sudo systemctl restart docker
docker info --format 'docker cgroup driver: {{.CgroupDriver}}'
📊 Production Insight
Cgroup mismatch is a fleet-config bug wearing a runtime-error costume. One cluster failed only on 4 hand-built nodes while 20 imaged nodes were fine — daemon.json had been copied from a 2019 blog post.
🎯 Key Takeaway
Kubelet and daemon must both say systemd. Set exec-opts in daemon.json, restart, verify — and manage the file centrally.

Corrupted Image Layers: Pull Fresh

When mounts, entrypoint, and cgroups all check out, suspect the bytes. Layers corrupt in transit (flaky registry mirror, interrupted pull resumed badly), at rest (dying disk, full filesystem mid-extract), or at build time (daemon crash during build leaving a bad layer in the local cache). The symptoms vary — checksum mismatches, failed to register layer, unexpected EOF during create — but the test is uniform: the same tag fails identically with a minimal run while a fresh pull elsewhere works. If the image fails on one host and runs on five others, the host's cached layers are guilty, not the registry.

The fix is a clean slate on the failing machine: remove the image, pull fresh, and watch the pull output for retries or checksum errors that confirm the theory. docker rmi needs the image unused — check docker ps -a for stopped containers pinning it before reaching for -f. After pulling, compare the RootFS layer digests against a healthy host; identical digests with different behavior means the problem was never the image, so go back to host facts.

If corruption recurs on the same host, stop pulling and check the disk: df -h for full filesystems, dmesg for I/O errors, SMART data for a dying drive. Recurring single-host corruption is hardware or filesystem trouble, and no amount of re-pulling fixes hardware. Pin production to digests rather than mutable tags while you're at it — a digest guarantees every host unpacks the same bytes.

image-layer-refresh.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Who pins the image? (rmi refuses while containers reference it)
docker ps -a --filter ancestor=myapp:latest --format '{{.Names}} {{.Status}}'

# Clean slate on the failing host, watching pull output for retries
docker rmi myapp:latest
docker pull myapp:latest 2>&1 | tee /tmp/pull.log
grep -iE 'retry|checksum|error' /tmp/pull.log || echo 'pull clean'

# Compare layer digests against a healthy host
docker inspect --format '{{.RootFS.Layers}}' myapp:latest

# Recurring on one host? Suspect disk, not registry
df -h /var/lib/docker
dmesg | tail -20 | grep -iE 'I/O error|Buffer|EXT4|nvme|sd[a-z]' || echo 'no disk errors'
📊 Production Insight
Single-host corruption that survives re-pulls is almost always disk trouble. One team's weekly corrupt layer traced to an SSD with 400+ reallocated sectors — found only after the third identical incident.
🎯 Key Takeaway
rmi, pull fresh, compare digests. If one host keeps corrupting, check df, dmesg, and SMART — then pin prod to digests.

Inspect, Logs, and the Lock-In

Two commands close every OCI investigation: docker inspect and the daemon logs. docker inspect --format renders the merged runtime config — mounts, entrypoint, user, cgroup-relevant limits — exactly as the daemon will hand it to runc. Reading it before running catches typos in -v sources, wrong entrypoint paths, and user IDs with no home. The daemon logs (journalctl -u docker, or containerd logs under Kubernetes) add the daemon's view: which API call failed and what the runtime reported back. Together they replace guessing with reading.

Verification is a ladder: the minimal failing run now succeeds, docker inspect shows the corrected config, and the full compose service or pod starts and passes health checks. On Kubernetes, follow with kubectl describe pod and kubectl logs to confirm the runtime layer is green before declaring victory. Each rung tests a wider scope, so a pass at the top means the fix is real.

Lock it in with three guards. Pre-deploy checks that stat every bind source on every target node. CI smoke steps that boot the image with its real entrypoint. And digest-pinned production tags so every host assembles the same bytes. OCI create failures are assembly failures — make the inputs (paths, entrypoints, configs, bytes) declared and verified, and the assembly stops failing.

oci-final-verification.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
# Read the merged runtime config before you run
docker inspect --format 'mounts={{json .Mounts}} user={{.Config.User}}' myapp-container 2>/dev/null

# Daemon's view of the last failure
sudo journalctl -u docker --since '10 min ago' | grep -i -B2 -A5 'runc.*fail\|create.*fail' | tail -25

# Verification ladder: minimal run, then the real workload
docker run --rm myapp:latest true && echo MINIMAL-OK
docker compose up -d app && sleep 5 && docker compose ps app

# Kubernetes follow-up when the runtime sits under kubelet
kubectl describe pod -l app=myapp | grep -A5 Events | tail -8
kubectl logs -l app=myapp --tail=5
💡Inspect Before You Run
docker inspect --format shows the exact mounts, user, and entrypoint the daemon will hand to runc. Thirty seconds of reading catches the typo that costs thirty minutes of redeploys.
📊 Production Insight
Mature teams gate deploys on a bind-source check across all targets plus an entrypoint smoke in CI. After adding both, one org went from monthly OCI pages to zero in two quarters.
🎯 Key Takeaway
Inspect the merged config, read daemon logs, climb the verification ladder — then gate deploys on bind checks and entrypoint smokes.
● Production incidentPOST-MORTEMseverity: high

A Missing Host Path Blocked 41 Deploys Across 3 Nodes

Symptom
At 4:12 PM on a Friday, the deploy pipeline turned red — but only partially. Nine of 12 workers started the new release cleanly; 3 workers failed all 41 container starts with "OCI runtime create failed: mounting /data/uploads to rootfs caused: no such file or directory." The app team assumed a bad image and rebuilt it twice, burning 35 minutes while the same 3 hosts kept failing and the 9 healthy ones kept passing.
Assumption
Because the same image tag worked on most hosts, the team assumed the registry had served a corrupt layer to the failing minority and focused on re-pushing. Two engineers also suspected a kernel version skew, since the 3 hosts had been patched the previous night. Nobody checked the host path itself for 35 minutes, because the mount had worked for 8 months and /data/uploads felt like bedrock.
Root cause
A storage migration the prior weekend had moved /data/uploads to a new volume — on 9 hosts. The remaining 3 were skipped when the migration script hit an SSH timeout and nobody re-ran it for the stragglers. The new release was the first to mount with the strict :ro bind that surfaces a missing source as a create failure instead of silently creating an empty directory. The patched kernels were a coincidence.
Fix
Immediate: created /data/uploads on the 3 hosts and remounted the volume — all 41 starts succeeded within 6 minutes. Same evening: added a pre-deploy host check that stats every bind source on every target node and aborts the deploy naming the host and path. That quarter: moved the bind into a named Docker volume managed by config management, so host paths are declared state rather than tribal knowledge.
Key lesson
  • Partial-fleet failures are host facts, not image facts. When 3 of 12 hosts fail identically, diff the hosts — ls the mount source on a good and bad node — before rebuilding anything.
  • Silent directory auto-creation hides drift for months. Strict mounts that fail loud are better, but only paired with a pre-deploy check that validates every bind source on every target.
  • Migration scripts need completion accounting. An SSH timeout that skips 3 hosts must page, not just log — the stragglers become Friday's outage.
Production debug guideFive symptoms, five exact command sequences — always read the tail of the error first.5 entries
Symptom · 01
The error is a wall of text and you can't tell which layer is complaining
Fix
Read the last 3 lines first — runc's own complaint sits at the tail. Run your failing command with the full error captured: docker run --rm myapp:latest 2>&1 | tail -5. Then split client from daemon with docker version, and pull the daemon side with sudo journalctl -u docker --since '10 min ago' | grep -i -A 3 error | tail -20. The tail names the failed assembly step; everything above it is wrapping.
Symptom · 02
Tail says mounting ... caused: no such file or directory, or permission denied
Fix
Stat the host source exactly as written: ls -ld /data/uploads and check ownership with stat -c '%U %a' /data/uploads. If it's missing, create it with sudo mkdir -p /data/uploads && sudo chown 1000:1000 /data/uploads (match your container UID). If it exists but denies, compare the host UID with the container user from docker inspect --format '{{.Config.User}}' myapp:latest, then fix ownership or add :z/:Z on SELinux hosts.
Symptom · 03
Tail says exec: ... no such file or directory, or permission denied on start
Fix
Print what the image actually declares: docker inspect --format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}' myapp:latest. Test with an override — docker run --rm --entrypoint sh myapp:latest -c 'ls -l /app/start.sh' — to see if the file exists inside. If the override works, fix the Dockerfile ENTRYPOINT path or the run flags. If sh itself fails, the image is corrupt — re-pull it.
Symptom · 04
Tail mentions cgroup, devices, or permission on /sys/fs/cgroup
Fix
Compare drivers: docker info --format '{{.CgroupDriver}}' should agree with the kubelet's cgroup-driver on Kubernetes nodes (both systemd on modern setups). On mismatch, set dockerd via /etc/docker/daemon.json with {"exec-opts": ["native.cgroupdriver=systemd"]}, then sudo systemctl restart docker. Confirm with docker info | grep -i cgroup showing systemd on both sides.
Symptom · 05
Mounts, entrypoint, and cgroups all check out — suspect a corrupt layer
Fix
Force a clean slate: docker rmi myapp:latest (add -f only if a stopped container pins it — check docker ps -a first), then docker pull myapp:latest and watch for retry or checksum lines. Verify with docker inspect --format '{{.RootFS.Layers}}' myapp:latest and compare the digest against the registry. If corruption recurs on one host, check that host's disk with df -h and dmesg | tail for I/O errors — dying disks corrupt layers repeatedly.
OCI Create Failures — Confirm, Fix, Prevent
Root CauseHow to ConfirmFixPrevention
Missing host bind sourceTail says mounting ... no such file; ls -ld on the host path failsCreate the directory with the container UID's ownership; remount the volumePre-deploy stat of every bind source on every target; prefer named volumes
Wrong entrypoint or interpreterdocker inspect shows a path that sh -c 'ls -l' can't find; shebang names missing bashFix Dockerfile ENTRYPOINT path and shebang; restore exec bitCI smoke that boots the real entrypoint; enforce LF endings and exec bits
Cgroup driver mismatchdocker info driver disagrees with kubelet --cgroup-driver; cgroup path errorsSet native.cgroupdriver=systemd in daemon.json and restart dockerdManage daemon.json centrally; assert systemd in node conformance checks
Corrupt image layerSame tag fails on one host, runs elsewhere; pull shows retries or checksum errorsdocker rmi plus fresh docker pull; compare layer digests across hostsPin production to digests; monitor disk health on builders and nodes
Bad user, perms, or SELinux labelPermission denied on mount access; ausearch shows AVC denialschown to container UID; add :z/:Z relabel on SELinux hostsStandardize image UIDs; set SELinux flags in compose and manifests
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
bind-mount-diagnosis.shdocker run --rm -v /data/uploads:/app/uploads:ro myapp:latest true 2>&1 | tail -...Bad Bind Mounts
entrypoint-diagnosis.shdocker inspect --format 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Conf...Invalid Entrypoint
cgroup-driver-fix.shdocker info --format 'docker cgroup driver: {{.CgroupDriver}}'Cgroup Driver Mismatch
image-layer-refresh.shdocker ps -a --filter ancestor=myapp:latest --format '{{.Names}} {{.Status}}'Corrupted Image Layers
oci-final-verification.shdocker inspect --format 'mounts={{json .Mounts}} user={{.Config.User}}' myapp-co...Inspect, Logs, and the Lock-In

Key takeaways

1
Read the error bottom-up
the runc tail is the diagnosis, the rest is wrapping.
2
Missing bind sources fail creation
stat the host path on the failing node first.
3
Exec failures mean interpreter or exec bit, not your app
test with an sh override.
4
Kubelet and daemon cgroup drivers must both be systemd.
5
One-host failures mean cached corrupt layers
rmi, pull fresh, compare digests.
6
Gate deploys on bind checks and entrypoint smokes so assembly inputs stay correct.

Common mistakes to avoid

6 patterns
×

Debugging the application instead of the assembly

Symptom
Hours reading app code and logs for a process that never started — no stack trace exists because PID 1 never exec'd.
Fix
Read the runc tail first. If it names a mount, entrypoint, cgroup, or layer, the app is innocent — fix the assembly input.
×

Pasting only the first line of the error

Symptom
Error response from daemon tells the team nothing, so advice stays generic and the thread runs 40 messages deep.
Fix
Always capture and share the full text: docker run ... 2>&1 | tail -5. The last two lines are the diagnosis.
×

Rebuilding the image for a host-path problem

Symptom
Fresh image, same failure on the same 3 hosts — because /data/uploads is still missing there regardless of bytes.
Fix
When failures cluster on specific hosts, ls the bind source on good vs bad nodes before touching the build.
×

Using shell-form ENTRYPOINT with an assumed shell

Symptom
Works on Debian-based images, fails on Alpine with exec errors — /bin/bash doesn't exist in the slim image.
Fix
Use exec-form ENTRYPOINT with absolute paths and a #!/bin/sh shebang unless you've verified the interpreter ships.
×

Copying daemon.json folklore from old guides

Symptom
cgroupfs driver on new nodes under a systemd kubelet; creation fails only on hand-configured hosts.
Fix
Set native.cgroupdriver=systemd, manage daemon.json in config management, and distrust any guide that sets cgroupfs.
×

Forcing rmi -f without checking stopped containers

Symptom
Deleted image returns on next start attempt confusion, or a running container loses its reference mid-debug.
Fix
List docker ps -a --filter ancestor= first. Remove the pinning containers deliberately, then rmi and pull clean.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
docker run fails with OCI runtime create failed but the app logs nothing...
Q02SENIOR
The tail says mounting /data/uploads caused no such file. The directory ...
Q03SENIOR
Entrypoint start.sh exists in the image but exec fails with no such file...
Q04SENIOR
When would you suspect the cgroup driver, and how do you confirm it?
Q05SENIOR
Same tag runs on five hosts and fails create on one. Walk me through it.
Q01 of 05JUNIOR

docker run fails with OCI runtime create failed but the app logs nothing. Where do you look first?

ANSWER
The tail of the full error — the last two lines are runc's own complaint naming the syscall, path, and errno. Then I classify it as mount, exec, cgroup, or layer and reproduce minimally. App logs can't exist because PID 1 never started.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Is OCI runtime create failed an application bug?
02
Why does the same image work on some hosts but not others?
03
What's the difference between OCI create failed and a container exiting immediately?
04
Can --privileged fix cgroup-related create failures?
05
Do I need to rebuild the image after fixing a mount path?
06
How do I stop this class of failure in CI?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
🔥

That's Docker. Mark it forged?

6 min read · try the examples if you haven't

Previous
Cannot Connect to Docker Daemon Fix
22 / 24 · Docker
Next
Docker No Space Left on Device Fix