Skip to Content
Docker Intermediate: Images and Compose That Survive Real Projects
Intermediate
0 likes
Courses
Docker Intermediate: Images and Compose That Survive Real Projects

Permissions, Ownership and Why the Container Can't Write

Reproduce the Permission denied error on a volume and a bind mount, read the owner numbers and fix it two ways. After this, you can tell a read-only mount from an ownership problem and fix the right one.

Course content

Sign in to save your progress.

Course Home

Skip ahead: If you only need the fix, skip to "Fix 1: change the owner once", then go on to the next lesson.

"Permission denied" in a container usually does not mean the container is broken. It means the process inside runs as one user, and the folder it wants to write to belongs to another. Docker does not translate users between the host and the container. Files carry numbers, the user ID and the group ID, and the names you see depend on whichever system is looking. Once you read the numbers, these errors are quick to solve.

Who am I inside the container?

Most images run as root, user ID 0, unless the Dockerfile says otherwise. You can pick another user for one run with --user.

docker run --rm alpine:3.24 id -u
docker run --rm --user 1000:1000 alpine:3.24 id -u
0
1000

Running as a non-root user is good practice. It limits what a bug or an attacker can do inside the container. It is also where the permission errors start.

A named volume that belongs to root

Create a new volume and write to it as user 1000.

docker volume create perm-vol | Out-Null
docker run --rm --user 1000:1000 -v perm-vol:/data alpine:3.24 sh -c "echo saved > /data/note.txt"
docker run --rm -v perm-vol:/data alpine:3.24 stat -c "%A %U:%G %n" /data
sh: can't create /data/note.txt: Permission denied
drwxr-xr-x root:root /data

The error is in the first line. The folder /data in the volume belongs to root and allows writing only by its owner (drwxr-xr-x). User 1000 can read it, not write to it. Nothing is broken. Root made the folder, and user 1000 is not root.

Fix 1: change the owner once

Run a throwaway container as root, change the owner of the volume's folder, and try again.

docker run --rm -v perm-vol:/data alpine:3.24 chown 1000:1000 /data
docker run --rm --user 1000:1000 -v perm-vol:/data alpine:3.24 sh -c "echo saved > /data/note.txt && stat -c '%u:%g %n' /data/note.txt"
1000:1000 /data/note.txt

This fixes one volume that already exists. It is the right tool when the data is already in it.

Fix 2: let the image prepare the folder

If the image creates the folder and gives it to the right user, a new volume mounted there inherits that owner. That is the same copy-in behaviour you saw in the last lesson, and it covers ownership too.

FROM alpine:3.24
RUN adduser -D -u 1000 app && mkdir /data && chown app:app /data
USER app
CMD ["sh", "-c", "echo saved > /data/note.txt && stat -c '%U:%G %n' /data/note.txt"]
docker build -q -t owned-demo:1 owned-demo | Out-Null
docker run --rm -v owned-vol:/data owned-demo:1
app:app /data/note.txt

The volume owned-vol did not exist, so Docker created it and copied the folder's owner from the image. The app can write without anyone running chown. This is the cleanest fix for your own images.

⚠ Warning: The inheritance happens only when a volume is new and empty. A volume that already exists with root as owner stays root-owned, even when you ship a corrected image later. Use Fix 1 on it once, or remove it if it holds nothing you need.

Bind mounts on Windows hide the problem

Try the same write to a folder from your Windows disk.

New-Item -ItemType Directory -Force win-dir | Out-Null
docker run --rm -v "${PWD}/win-dir:/data" alpine:3.24 stat -c "%A %U:%G %n" /data
docker run --rm --user 1000:1000 -v "${PWD}/win-dir:/data" alpine:3.24 sh -c "echo saved > /data/note.txt && echo write ok"
drwxrwxrwx root:root /data
write ok

Docker Desktop shows a Windows folder as owned by root with open permissions (drwxrwxrwx), so any user can write. That is convenient, and it is why a project can work on every Windows laptop and then fail in CI or on a Linux server with exactly the error above.

The Linux version, reproduced

On Linux, a bind mount is a real folder with a real owner. You can reproduce that on Windows by pointing a bind mount at a folder inside Docker's own Linux environment. A path that starts with /tmp does that. It is not your Windows C:\tmp.

docker run --rm -v /tmp/bind-demo:/data alpine:3.24 stat -c "%A %U:%G %n" /data
docker run --rm --user 1000:1000 -v /tmp/bind-demo:/data alpine:3.24 sh -c "echo saved > /data/note.txt"
docker run --rm -v /tmp/bind-demo:/data alpine:3.24 chown 1000:1000 /data
docker run --rm --user 1000:1000 -v /tmp/bind-demo:/data alpine:3.24 sh -c "echo saved > /data/note.txt && echo write ok"
drwxr-xr-x root:root /data
sh: can't create /data/note.txt: Permission denied
write ok

Docker created the missing folder as root, user 1000 was refused, and one chown from a root container fixed it. On a real Linux machine, the same job is sudo chown 1000:1000 ./folder, or you start the container with your own IDs: --user "$(id -u):$(id -g)". I ran this version only inside Docker Desktop's Linux environment, not on a separate Linux computer.

docker run --rm -v /tmp:/t alpine:3.24 rm -rf /t/bind-demo

Read-only is a different message

If the mount itself is read-only, the message changes.

docker run --rm -v "${PWD}/win-dir:/data:ro" alpine:3.24 sh -c "echo oops > /data/other.txt"
sh: can't create /data/other.txt: Read-only file system

"Read-only file system" means someone wrote :ro on the mount. "Permission denied" means the owner and mode do not allow this user. They need different fixes, so read the exact words before you change anything.

💡 Tip: Never "fix" permission errors with chmod 777 or by running the app as root. Find which user the process runs as, find who owns the folder, and change one of them on purpose.

Which of your containers still runs as root only because nobody asked why?

About

Reproduce the Permission denied error on a volume and a bind mount, read the owner numbers and fix it two ways. After this, you can tell a read-only mount from an ownership problem and fix the right one.

Duration
5 min
Last update
08/01/2026
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.