Quick fix
# One-liner: chown the host directory to the in-container uid (example: container uid 1000)
sudo chown -R 1000:1000 ./data && docker run --rm -u 1000:1000 -v "$PWD/data:/data" <image> ls /data
What you will run into
The container starts fine and the mount is visible (ls shows files), but any process read/write fails with Permission denied.
You think the mount didn't work — it did; the real issue is the uid of the process inside the container doesn't match the owner of the host directory.
Common trigger: switching to a non-root user per security advice (-u 1000:1000, compose user:, or an image built-in USER). Everything works when the image runs as root by default; the problem appears immediately after the switch.
Why this happens
Linux permissions match by uid/gid, and bind mounts don't translate ownership: the container process's uid is judged directly against the host directory file's owner/permissions.
Typical mismatch: host files owned by root (uid 0), container process runs with a non-zero uid → reading 600 files is denied, writes fail.
Common misconception: switching to a named volume doesn't auto-fix it either — docker initializes a new named volume's directory as root:root 755, and a non-root container still gets Permission denied (measured reproduction).
Linux permission checks match "process uid/gid × file owner/mode bits".
A bind mount just attaches the host directory into the container — no uid mapping and no ownership translation: a uid-1000 process inside the container reading a host 0600 file owned by uid 0 is judged exactly like a normal host user reading a root file.
On first creation, a named volume is initialized to root:root 755, so non-root containers hit Permission denied with named volumes too.
Deterministic takeaway: do not guess "it should be writable inside the container" — first run docker run --rm -v <dir>:/data <image> ls -ln /data to see the real owner;
then pick one of three fixes: chown -R 1000:1000 on the host to align uids, run the container as root (temp/dev), or user-namespace remapping (production).
chown changes the host files, so both sides take effect at once.
Step by step
1. Confirm it's permission, not the mount — Required
docker run --rm -u 0:0 -v "$PWD/data:/data" <image> ls -l /data
A root container can read it, a non-root container can't → permission problem (measured comparison).
2. Adjust the host directory owner — Required
sudo chown -R 1000:1000 ./data
chown to the in-container uid — a one-time fix, suitable when the data directory's owner is stable (measured: after chown, the non-root container reads successfully, exit 0).
3. With an image built-in user, check the official docs — Required
Many official images state "please run with -u 1000:1000" (e.g. blocky's internal user is uid 100); align with the docs, don't guess.
4. Named volume permission initialization — Required
If the image's target directory owner doesn't match, chown in the entrypoint / an init container; only when the image directory owner is already correct (e.g. the image creates /data owned by 1000) can you rely on volume auto-initialization.
5. Temporary emergency escape — Required
-u 0:0 runs the container as root and bypasses this, but it breaks least-privilege; local debugging only.
How to confirm it works
docker run --rm -u 1000:1000 -v "$PWD/data:/data" <image> touch /data/write-test && echo OK
A successful write means it's fixed.
Measured: -u 1000:1000 reading a root-owned 600 file → Permission denied (exit 1); root container reading the same mount → success; after host chown 1000:1000 the non-root container → success;
named volume initialized with owner root:root 755, non-root container touch → still Permission denied (misconception confirmed).
Other environments
| Environment | Notes |
|---|---|
| Bind mount (-v host:container) | Most typical; chown the host directory to align uids |
| Named volume | Doesn't auto-fix; works only if the image's directory owner is correct |
| Docker Desktop (Windows/macOS) | Also common; mounted directory owner mismatches the container uid |
| Image built-in USER | Check the official recommended -u, align with docs |
Symptom
The container starts fine and the mount is visible (ls shows files), but any read/write by the process fails:
cat: can't open '/data/rootfile': Permission denied
touch: /data/write-test: Permission denied
mkdir: cannot create directory '/data/xxx': Permission denied
cannot create temp cache file in /cache: open /cache/xxx.tmp: permission denied
Key traits:
- Container runs and the directory is visible — only read/write is denied, easily mistaken for "the mount failed";
- Typical with non-root containers: image
USER,docker run -u UID:GID, or composeuser: "1000:1000"; - Bind mounts (
-v /host/path:/container/path) are the most common case; named volumes can hit it too; - Docker Desktop on Windows/macOS hits it frequently (host directories whose owner doesn't match the container uid).
Cause
Linux permissions match by uid/gid, and bind mounts do no ownership translation: the container process's uid is checked directly against the host file's owner/permissions. The classic mismatch is host files owned by root (uid 0) or another uid (e.g. 1000) while the container process runs as a non-zero uid → can't read 600 files / can't write. Everything works while the image runs as root; the problem appears the moment you switch to a non-root user for security.
Common misconception: switching to a named volume does not fix it automatically — docker initializes a new named volume as root:root 755, so a non-root process still can't write (unless the target directory inside the image already belongs to that uid).
Solution
- Align the container uid with the host (recommended):
docker run -u "$(id -u):$(id -g)" -v "$PWD/data:/data" ...— the process runs as your host uid and can read/write its own files; in compose useuser: "${UID}:${GID}"(export UID/GID in the shell). - Fix the host directory ownership:
sudo chown -R 1000:1000 ./data(chown to the container's uid) — a one-time fix for directories with a fixed owner. - If the image has a built-in user, read its docs: official images usually state the exact run command (e.g. blocky runs as uid 100) — align with the documented uid instead of guessing.
- Named-volume permission init: if the image's target directory owner doesn't match, chown in the entrypoint / an init container; only if the image directory is already owned correctly can you rely on docker's automatic volume initialization.
- Quick workaround:
-u 0:0runs the container as root and bypasses the issue, but breaks least-privilege — local debugging only.
Verification Evidence
- Local repro (2026-08-29, WSL Ubuntu, docker 29.1.3, alpine:3.20):
- Setup: a host file owned by root (uid 0) with mode 600;
- Repro:
docker run --rm -u 1000:1000 -v /tmp/vol-test:/data alpine cat /data/rootfile→Permission denied(exit 1); - Control: root container (no
-u) reading the same mount → success (mount itself is fine); - Fix A: host
chown 1000:1000→ non-root container reads it → success (exit 0); - Fix B:
-u 0:0aligned with host → success (exit 0); - Misconception check: after
docker volume create, a-u 1000:1000container touching the named volume →Permission denied(exit 1);ls -ld /dataconfirms the volume initialized asroot:root 755→ named volumes do NOT auto-fix permissions; a root container writing the same named volume → success.
Evidence details kept locally (2026-08-29)
Related canonicals
Read in order: precondition -> same root / same domain / related:
- Precondition (rule out first):
- Cannot connect to the Docker daemon (precondition: container starts = daemon OK; permission error is at the mount layer)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
dockerBundled solutions (combos)
This problem is part of the following verified bundles.
Source & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk