Skip to content
DevOps AI ToolKit
Newsletter
All guides
Docker with AI By James Joyner IV · · 9 min read

Docker Error Guide: 'cannot create directory: Permission denied' — Volume & Bind-Mount Ownership

Fix Docker's 'cannot create directory: Permission denied' on volumes and bind mounts caused by UID/GID mismatches between the container user and host directory ownership.

  • #docker
  • #troubleshooting
  • #errors
  • #permissions

Overview

A container fails to write into a mounted directory when the process’s user does not have permission on the host-owned path. It appears in container logs and interactive runs:

mkdir: cannot create directory '/data/cache': Permission denied

The shell-builtin variant looks the same with a leading sh::

sh: cannot create directory '/data/cache': Permission denied

Bind mounts and named volumes carry host filesystem ownership (UID/GID) straight into the container. If the container runs as a user whose UID does not match the directory’s owner and the mode does not grant write access, every create/write returns EACCES. This is an ownership/permissions mismatch, not a Docker bug.

Symptoms

  • A container that writes to a mounted path crashes at startup with Permission denied.
  • The same image works with no mount but fails once a bind mount or volume is attached.
  • Running the image as root works, but running as a non-root user fails.
  • Files created inside the container appear on the host owned by an unexpected UID (or vice versa).
  • A fresh named volume works, but a bind mount to a host directory owned by a different user does not.

Common Root Causes

  • UID/GID mismatch — the container process runs as a user (e.g. UID 1000 or an app-specific UID) that does not own the host directory.
  • Non-root USER in the image writing to a root-owned mount.
  • Bind-mount source owned by the host user with a restrictive mode (e.g. 700) the container UID cannot enter.
  • Read-only mount (:ro) where the app expects to write.
  • SELinux context on the host blocking container access to the bind mount (needs :z/:Z).
  • Root-created files on a named volume that a later non-root container cannot modify.

Diagnostic Workflow

First, identify which user the container runs as and which UID that maps to:

docker run --rm myapp:1.4.2 id

Check the ownership and mode of the mount target inside a throwaway container:

docker run --rm -v /data/myapp:/data alpine ls -ld /data

Compare that against the host directory’s ownership:

ls -ld /data/myapp

If the container UID (from id) does not match the directory owner and the group/other bits do not grant write, that is the mismatch. On SELinux hosts, check whether the context is the blocker:

ls -Z /data/myapp

Confirm the fix works by aligning ownership and re-running. Chown the host directory to the container’s UID/GID:

sudo chown -R 1000:1000 /data/myapp
docker run --rm -v /data/myapp:/data myapp:1.4.2 sh -c 'mkdir -p /data/cache && echo ok'

Example Root Cause Analysis

An image runs its app as a non-root user baked in as USER app (UID 1000). The container mounts a host directory and crashes on start:

mkdir: cannot create directory '/data/cache': Permission denied

Checking the container user and the host directory reveals the mismatch:

docker run --rm myapp:1.4.2 id
# uid=1000(app) gid=1000(app)

ls -ld /data/myapp
# drwxr-xr-x 2 root root 4096 Jul  5 10:12 /data/myapp

The directory is owned by root:root with mode 755, so UID 1000 can read but not write. The container user cannot create /data/cache. The fix is to align the host directory ownership with the container’s UID/GID:

sudo chown -R 1000:1000 /data/myapp

Re-running the container now succeeds because the app user owns the mount. On an SELinux host, the mount would also need the :Z flag so Docker relabels the directory for the container:

docker run -d -v /data/myapp:/data:Z myapp:1.4.2

An alternative that avoids host chowning is to run the container with a matching --user "$(id -u):$(id -g)" so it writes as the host owner.

Prevention Best Practices

  • Decide on a fixed application UID/GID and provision host directories with matching ownership during setup.
  • Use --user "$(id -u):$(id -g)" for bind mounts when you want files owned by the host user.
  • Prefer named volumes for app data; initialize their ownership with a one-time chown in an entrypoint or init container.
  • On SELinux systems, mount bind sources with :z (shared) or :Z (private) so Docker applies the correct label.
  • Avoid running production containers as root just to sidestep permissions; fix ownership instead.
  • Validate Dockerfiles and their USER/WORKDIR choices with the Dockerfile validator before shipping.

Quick Command Reference

# Which user/UID does the container run as?
docker run --rm myapp:1.4.2 id

# Ownership/mode of the mount as the container sees it
docker run --rm -v /data/myapp:/data alpine ls -ld /data

# Host directory ownership
ls -ld /data/myapp

# Align host ownership to the container UID/GID
sudo chown -R 1000:1000 /data/myapp

# Run as the host user instead of chowning
docker run --user "$(id -u):$(id -g)" -v /data/myapp:/data myapp:1.4.2

# SELinux relabel on mount
docker run -v /data/myapp:/data:Z myapp:1.4.2

Conclusion

cannot create directory: Permission denied on a volume or bind mount is an ownership mismatch: the container’s process UID does not have write permission on the host-owned path. Diagnose it by comparing docker run --rm myapp id against ls -ld of the mount, then either chown the host directory to the container’s UID/GID, run the container as the host user with --user, or apply the SELinux :Z flag. Standardizing a fixed app UID and provisioning directory ownership up front prevents it. For more volume and permission fixes, see the Docker guides.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.