KhueApps
Home/DevOps/How to fix 'OCI runtime create failed: permission denied' in Docker

How to fix 'OCI runtime create failed: permission denied' in Docker

Last updated: October 06, 2025

What this error means

The error “OCI runtime create failed: permission denied” occurs when the container runtime (runc/containerd via Docker) cannot start the container due to a host-level access control denial. Common culprits:

  • SELinux/AppArmor policy blocks mounts or process actions
  • Bind-mounted directory lacks proper permissions or labels
  • Missing Linux capabilities or restrictive seccomp profile
  • Devices/cgroups/network not permitted (often in rootless Docker)
  • Incorrect ownership/permissions under /var/lib/docker or data-root

This guide targets Linux hosts using Docker (DevOps/Docker context).

Quickstart: most common fixes (Linux)

  1. Verify Docker works at all
  • Test a basic container:
docker run --rm alpine:3.20 echo OK

If this fails with the same error, jump to Diagnostics.

  1. Fix bind-mount + SELinux (Fedora/RHEL/CentOS)
  • Add SELinux relabel option to mounts:
docker run --rm -v "$HOME/test:/data":Z alpine:3.20 sh -c 'touch /data/ok'
  • Or relabel once on host:
mkdir -p "$HOME/test"
sudo chcon -Rt svirt_sandbox_file_t "$HOME/test"
  1. AppArmor (Ubuntu/Debian)
  • Test with AppArmor unconfined (for debugging only):
docker run --rm --security-opt apparmor=unconfined alpine:3.20 true

If it works, adjust or choose an appropriate profile instead of leaving unconfined.

  1. Seccomp restrictions
  • Test with seccomp unconfined (debugging only):
docker run --rm --security-opt seccomp=unconfined alpine:3.20 true

If it works, switch to a profile that allows the needed syscalls.

  1. Capabilities
  • If the container needs privileged operations (e.g., mount), add capability or run privileged for testing:
docker run --rm --cap-add SYS_ADMIN alpine:3.20 mount | head -n1
# or, last resort for debugging only:
docker run --rm --privileged alpine:3.20 true
  1. Rootless Docker specifics
  • Ensure newuidmap/newgidmap are installed and subuid/subgid configured:
getent passwd "$USER" && grep "^$USER:" /etc/subuid /etc/subgid
  • Try with fuse-overlayfs driver or use a named volume instead of privileged bind mounts.
  1. File/dir permissions
  • Ensure host path exists and is accessible:
HOSTDIR="$HOME/test"; mkdir -p "$HOSTDIR"; chmod 755 "$HOSTDIR"
docker run --rm -v "$HOSTDIR:/data" alpine:3.20 sh -c 'touch /data/ok'
  1. Restart services and update
sudo systemctl restart docker
sudo docker system prune -f
sudo apt/yum update && sudo systemctl restart docker

Minimal working example (SELinux bind-mount fix)

Reproduce the error (on SELinux enforcing hosts):

mkdir -p "$HOME/seltest"
docker run --rm -v "$HOME/seltest:/data" alpine:3.20 sh -c 'touch /data/hello'
# Likely fails with: OCI runtime create failed: ... permission denied

Fix with automatic relabeling:

docker run --rm -v "$HOME/seltest:/data":Z alpine:3.20 sh -c 'touch /data/hello'
# Succeeds

Alternative persistent fix:

sudo chcon -Rt svirt_sandbox_file_t "$HOME/seltest"
docker run --rm -v "$HOME/seltest:/data" alpine:3.20 sh -c 'touch /data/hello'

Root causes and targeted fixes

  • Bind mounts without proper permissions/labels

    • Symptom: Fails when mounting or writing to host path.
    • Fix: Ensure path exists, correct owner/perm (e.g., 755/775). On SELinux, use :Z or chcon. Prefer named volumes if you don’t need host path.
  • SELinux denials (Fedora/RHEL-based)

    • Symptom: Error at container start; audit logs show denied mount/access.
    • Fix: Use :Z or :z on volumes; avoid disabling SELinux. For persistent paths, chcon -Rt svirt_sandbox_file_t.
  • AppArmor denials (Ubuntu/Debian)

    • Symptom: Default profile blocks some operations.
    • Fix: Select an appropriate profile or add rules. For debugging only, apparmor=unconfined.
  • Seccomp profile too restrictive

    • Symptom: Start-up syscalls denied.
    • Fix: Use a compatible seccomp profile or temporarily seccomp=unconfined to confirm the cause.
  • Missing capabilities

    • Symptom: Operations like mounting, raw network, or device access fail.
    • Fix: Add required capabilities (e.g., --cap-add NET_ADMIN or SYS_ADMIN). Use --privileged only to confirm and then tighten.
  • Devices and cgroups

    • Symptom: Access to /dev/* or cgroup creation denied.
    • Fix: Allow device explicitly: --device /dev/fuse; ensure cgroup v2 is supported by your Docker/runc stack; update if needed.
  • Rootless Docker specifics

    • Symptom: Cannot create cgroups, set up networking, or mount overlay.
    • Fix: Install newuidmap/newgidmap; configure /etc/subuid and /etc/subgid; use fuse-overlayfs; avoid privileged bind mounts.
  • Docker data-root permissions

    • Symptom: Denial when touching /var/lib/docker (or custom data-root).
    • Fix: Ensure root:root ownership and 711/755 perms on each path element.

Quick diagnostics

  • Get the exact failure details:
journalctl -u docker -n 200 --no-pager
sudo dmesg | tail -n 50
  • Show Docker info (cgroup driver, runtime):
docker info
  • Validate with a baseline container:
docker run --rm busybox:1.36 true

If baseline works but your app container fails, compare differences: volumes, capabilities, devices, security opts.

Mapping symptom to likely fix

Symptom contextLikely causeTry this first
Fedora/RHEL, bind mountSELinuxAdd :Z on the -v mount or chcon the path
Ubuntu, custom mountsAppArmorapparmor=unconfined (debug), then set a profile
Needs FUSE or mountCapabilities/Devices--cap-add SYS_ADMIN or --device /dev/fuse
Rootless modeSubuid/subgid/cgroupsInstall newuidmap/newgidmap; use fuse-overlayfs

Pitfalls to avoid

  • Disabling SELinux or AppArmor permanently; prefer targeted policy or labeling.
  • Leaving containers privileged/unconfined after debugging.
  • chmod -R 777 on host paths; use correct ownership and minimal perms.
  • Ignoring cgroup v2 compatibility; keep Docker, containerd, and runc up to date.
  • Mounting non-existent host paths; Docker will create files in unexpected places.

Performance notes

  • Prefer named volumes over bind mounts when possible; they avoid host labeling issues and are often faster on overlay2.
  • On rootless, fuse-overlayfs can be slightly slower; consider rootful Docker when safe and appropriate.
  • Excessive relabeling (:Z on large trees) can slow startup; relabel once with chcon when stable.
  • Avoid running with seccomp=unconfined or privileged in production; it may mask bugs and reduce kernel mitigations.

Tiny FAQ

  • Why does :Z fix this on Fedora/RHEL?

    • It sets an SELinux label on the mount so the container’s confined domain may access it.
  • Is using --privileged safe?

    • It’s for debugging. Replace it with the minimal set of capabilities and security opts your app needs.
  • Does this happen on macOS/Windows?

    • Less commonly. The error typically arises on Linux where SELinux/AppArmor/cgroups directly govern the runtime.
  • My container only fails with a specific host directory. Why?

    • That path’s permissions or labels likely differ. Compare owner/perm/xattrs with a working path.
  • How do I make the fix permanent?

    • Script chcon for stable paths, define volumes in compose with :Z, and encode needed caps/security opts in your compose/CI configs.

Series: Docker

DevOps