Skip to content

Bind Mount Permissions


Here’s a scenario that trips up almost everyone the first time: you bind mount a folder, the container writes a file into it just fine, and then back on your host, you try to delete or edit that same file — and get denied. Nothing looks broken from the container’s side. The confusion is entirely a host-side surprise, and it comes down to one thing: UID and GID numbers, not usernames, are what actually own a file.

Why This Happens

A username like root or appuser is just a label. What the filesystem actually stores against a file is a numeric User ID (UID) and Group ID (GID). When a process inside a container writes a file, it writes it as whatever UID that process is running as — often root, which is UID 0, unless the image or the run command says otherwise.

The bind mount doesn’t translate or remap these numbers at all — remember, it’s the exact same filesystem, just visible from two places. So a file created by UID 0 inside the container shows up on your host, still owned by UID 0. If your own host user isn’t UID 0 (and it almost never is), you don’t own that file — and depending on the permission bits, you may not even be able to read or edit it without sudo.

    flowchart LR
    subgraph Container["Container"]
        P["Process running as<br/>UID 0 (root)"]
    end
    subgraph Host["<br>Host Machine"]
        F["file.txt<br/>owned by UID 0"]
        U["Your host user<br/>UID 1000"]
    end
    P -->|"writes"| F
    U -.->|"Permission denied"| F
  

Seeing It for Yourself

Using the bind-mount-demo folder from earlier:

docker container run --rm \
  -v "$(pwd)":/data \
  busybox \
  sh -c "echo 'written by container' > /data/from-container.txt"

ls -l from-container.txt

On most Linux hosts, that ls -l output shows the file owned by root, not by you — because the container process wrote it as UID 0 by default, and the bind mount carried that ownership straight through to your host filesystem.

Fixing It: Run the Container as Your Own User

The cleanest fix is telling the container to run as a UID that matches your host user, using the --user option:

docker container run --rm \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd)":/data \
  busybox \
  sh -c "echo 'written as me' > /data/from-container-fixed.txt"

$(id -u) and $(id -g) grab your current host user’s UID and GID and pass them straight into the container, so anything it writes into the bind-mounted folder lands with your ownership, not root’s. This is exactly why the earlier Flask example didn’t run into this — Python’s dev server writes application state, not files into the bind-mounted folder itself — but the moment your container writes logs, uploads, or generated files into a bind-mounted directory, this becomes worth knowing.

Note

Some official images (like postgres or node) already run as a non-root user by default, in which case you may need to check that specific image’s expected UID rather than assuming root, since forcing the wrong --user can just trade one permission mismatch for another.

Fixing It After the Fact

If you’ve already got files with the “wrong” ownership sitting around, you can reclaim them from the host side, assuming you have sudo access:

sudo chown -R "$(id -u):$(id -g)" ./bind-mount-demo

Or, if you’d rather fix it from inside a container instead of using sudo on the host directly:

docker container run --rm \
  -v "$(pwd)":/data \
  busybox \
  chown -R 1000:1000 /data

(Swap 1000:1000 for your actual host UID:GID from id -u and id -g if they’re different.)

Tip

The docker container run --rm command automatically deletes the container and its temporary file system immediately after it stops executing. The --rm is useful here because we just want to run one fixup command and tear down the container.

Providing Readonly Permission

If you want container to read some kind of data but not write it you can set readonly or ro option whenever you start the container:

With --mount:

docker container run \
  --mount type=bind,source="$(pwd)",target=/data,readonly \
  busybox

With -v:

docker container run \
  -v "$(pwd)":/data:readonly \
  busybox

The Takeaway

This isn’t a Docker bug — it’s just bind mounts doing exactly what they promise: giving the container direct, unfiltered access to a real part of your host filesystem, ownership numbers included. Volumes mostly sidestep this headache since Docker manages their storage internally, which is one more small reason volumes tend to be the friendlier default — but whenever you do reach for a bind mount, keeping UID/GID in mind will save you a confusing few minutes the first time a “permission denied” shows up somewhere it has no business being.

Last updated on