403

I am trying to mount a host directory in Docker, but then I cannot access it from within the container, even if the access permissions look good.

I am doing

sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash

and then

ls -al

It gives me:

total 8892
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 .
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 ..
-rwxr-xr-x.   1 root root       0 Jun 18 14:34 .dockerenv
-rwx------.   1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x.  18 1000 1000   12288 Jun 16 11:40 Downloads
drwxr-xr-x.   2 root root    4096 Jan 29 18:10 bin
drwxr-xr-x.   2 root root    4096 Apr 19  2012 boot
drwxr-xr-x.   4 root root     340 Jun 18 14:34 dev
drwxr-xr-x.  56 root root    4096 Jun 18 14:34 etc
drwxr-xr-x.   2 root root    4096 Apr 19  2012 home

and a lot more lines like that (I think this is the relevant portion).

If I do

cd /Downloads
ls

the result is

ls: cannot open directory .: Permission denied

The host is Fedora 20, with Docker 1.0.0 and go1.2.2.

What is going wrong?

Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
user3753011
  • 4,131
  • 3
  • 12
  • 3

11 Answers11

354

See this Project Atomic blog post about Volumes and SELinux for the full story.

Specifically:

This got easier recently since Docker finally merged a patch which will be showing up in docker-1.7 (We have been carrying the patch in docker-1.6 on RHEL, CentOS, and Fedora).

This patch adds support for "z" and "Z" as options on the volume mounts (-v).

For example:

docker run -v /var/db:/var/db:z rhel7 /bin/sh

Will automatically do the chcon -Rt svirt_sandbox_file_t /var/db described in the man page.

Even better, you can use Z.

docker run -v /var/db:/var/db:Z rhel7 /bin/sh

This will label the content inside the container with the exact MCS label that the container will run with, basically it runs chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/db where s0:c1,c2 differs for each container.

Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49
gregswift
  • 4,568
  • 1
  • 20
  • 8
  • 1
    Upstream has it as the last paragraph in this section https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container---volumes-from – gregswift Oct 17 '16 at 19:08
  • The last sentence of the quoted content is that it basically runs a chcon on the volume... so it makes sense that this would not work on a RO filesystem as applying SELinux context would require writing context somewhere. But that isn't kewl... and it stinks that searching about this doesn't give any answers. – gregswift Apr 20 '17 at 20:38
  • @gregswift but this by default opens container in root user. How can I run the container and mount the volume for any other user? – Geek May 20 '18 at 18:45
  • 2
    It is possible to fix the permissions under SELinux while mounting the volume as readonly at the same time by using both options at the same time separated by a comma: `-v $(pwd):/app:ro,Z`. This should be marked as the correct answer. – danirod Apr 22 '19 at 10:09
  • Does the `z/Z` option get ignored on non-SELinux systems - e.g. Mac? Or Do two separate commands need to be run? – icc97 Nov 18 '20 at 18:40
  • I add `:Z` to every docker command on tooling or devs run on their macbooks and no one has complained yet :D – gregswift Nov 20 '20 at 02:01
  • It may work only you don't need to do any hot deployments to those volumes. Other way new files will not carry over proper permissions to the container. – JackTheKnife Dec 02 '20 at 18:01
290

It is an SELinux issue.

You can temporarily issue

su -c "setenforce 0"

on the host to access or else add an SELinux rule by running

chcon -Rt svirt_sandbox_file_t /path/to/volume
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3761313
  • 2,949
  • 1
  • 10
  • 2
  • 3
    is /path/to/volume the host's path? If so it doesn't seem that this solution would work with data containers? – Roy Truelove Nov 17 '14 at 19:02
  • 6
    don't forget to do su -c "setenforce 1" ... otherwise it will work only because SELinux is still deactivated – vcarel Dec 10 '14 at 13:51
  • this solved my problem. thank you, i hope they will have a fix for this. – Hokutosei Mar 29 '15 at 01:26
  • 21
    Adding the selinux rule is the best way, as it is not a good idea in most cases to run containers with privileged mode. – Zoro_77 Apr 08 '15 at 17:22
  • 10
    As Zoro_77 said, add a rule and [stopdisablingselinux.com](https://stopdisablingselinux.com/) ;) – GabLeRoux May 15 '17 at 05:38
  • This worked great! I like the practice that the setenforce is "temporary." Meanwhile, I was confused that the audit.log said "res=SUCCESS" when it was in fact preventing the action. Why was it "success" if selinux was actually causing non-success? – macetw Sep 27 '17 at 21:55
  • Perfect, thanks. On my system, I even got the error in se permissive mode. – sebkraemer Nov 24 '17 at 09:24
  • 1
    For some reason I get `chcon: can't apply partial context to unlabeled file /var/xxx/yyy` – Maksim Luzik Feb 18 '19 at 13:59
  • I was searching trying to find an answer to my problem, finding all kinds of other incorrect solutions, until I stumbled across this, the correct one for my case. I work with selinux so little I keep forgetting that I should always suspect it first whenever weird permissions with zero error messages happens. – Michael Martinez May 21 '19 at 20:22
  • When running random linux commands I think its helpful to include the more verbose parameters for the sake of learning/understanding. From the [`chcon` docs](https://linux.die.net/man/1/chcon) the equivalent long version is `chcon --recursive --type=svirt_sandbox_file_t /path/to/volume`. What the "type" is is explained a bit more here: http://www.selinuxproject.org/page/SELinux_contexts – icc97 Nov 18 '20 at 18:30
  • I also had to prefix this command with `sudo` - but maybe that's because the volume I'm modifying is owned by root – icc97 Nov 18 '20 at 18:35
  • 1
    Addig SELinux rule did not solved problem in case of hot deployments to mounted volumes. Container still will not be able to access those files a :z flag only will patch them during the volume mount process. – JackTheKnife Dec 02 '20 at 18:03
  • So what's the best handling in the case Jack mentioned? I currently create volumes which I wouldn't need. Just to set the `z|Z` volume mount option. – arminfro Nov 08 '21 at 05:56
90

Typically, permissions issues with a host volume mount are because the UID/GID inside the container does not have access to the file according to the UID/GID permissions of the file on the host. However, this specific case is different.

The dot at the end of the permission string, drwxr-xr-x., indicates SELinux is configured. When using a host mount with SELinux, you need to pass an extra option to the end of the volume definition:

  • The z option indicates that the bind mount content is shared among multiple containers.
  • The Z option indicates that the bind mount content is private and unshared.

Your volume mount command would then look like:

sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash

See more about host mounts with SELinux at Configure the selinux label.


For others that see this issue with containers running as a different user, you need to ensure the UID/GID of the user inside the container has permissions to the file on the host. On production servers, this is often done by controlling the UID/GID in the image build process to match a UID/GID on the host that has access to the files (or even better, do not use host mounts in production).

A named volume is often preferred to host mounts because it will initialize the volume directory from the image directory, including any file ownership and permissions. This happens when the volume is empty and the container is created with the named volume.

macOS users now have OSXFS which handles UID/GIDs automatically between the Mac host and containers. One place it doesn't help with are files from inside the embedded VM that get mounted into the container, like /var/lib/docker.sock.

For development environments where the host UID/GID may change per developer, my preferred solution is to start the container with an entrypoint running as root, fix the UID/GID of the user inside the container to match the host volume UID/GID, and then use gosu to drop from root to the container user to run the application inside the container. The important script for this is fix-perms in my base image scripts, which can be found at: Docker Base Images from Brandon Mitchell

The important bit from the fix-perms script is:

# Update the UID
if [ -n "$opt_u" ]; then
  OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
  NEW_UID=$(stat -c "%u" "$1")
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

That gets the UID of the user inside the container, and the UID of the file, and if they do not match, calls usermod to adjust the UID. Lastly it does a recursive find to fix any files which have not changed UIDs. I like this better than running a container with a -u $(id -u):$(id -g) flag because the above entrypoint code doesn't require each developer to run a script to start the container, and any files outside of the volume that are owned by the user will have their permissions corrected.


You can also have Docker initialize a host directory from an image by using a named volume that performs a bind mount. This directory must already exist, and you need to provide an absolute path to the host directory, unlike host volumes in a compose file which can be relative paths. The directory must also be empty for Docker to initialize it. Three different options for defining a named volume to a bind mount look like:

  # create the volume in advance
  $ docker volume create --driver local \
      --opt type=none \
      --opt device=/home/user/test \
      --opt o=bind \
      test_vol

  # create on the fly with --mount
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

  # inside a docker-compose file
  ...
  volumes:
    bind-test:
      driver: local
      driver_opts:
        type: none
        o: bind
        device: /home/user/test
  ...

Lastly, if you try using user namespaces, you'll find that host volumes have permission issues because UID/GIDs of the containers are shifted. In that scenario, it's probably easiest to avoid host volumes and only use named volumes.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BMitch
  • 231,797
  • 42
  • 475
  • 450
72

WARNING: This solution has security risks.

Try running the container as privileged:

sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash

Another option (that I have not tried) would be to create a privileged container and then create non-privileged containers inside of it.

John Phillips
  • 896
  • 6
  • 11
40

From access.redhat.com:Sharing_Data_Across_Containers:

Host volume settings are not portable, since they are host-dependent and might not work on any other machine. For this reason, there is no Dockerfile equivalent for mounting host directories to the container. Also, be aware that the host system has no knowledge of container SELinux policy. Therefore, if SELinux policy is enforced, the mounted host directory is not writable to the container, regardless of the rw setting. Currently, you can work around this by assigning the proper SELinux policy type to the host directory":

chcon -Rt svirt_sandbox_file_t host_dir

Where host_dir is a path to the directory on host system that is mounted to the container.

It's seems to be only a workaround, but I tried and it works.

Community
  • 1
  • 1
Thomas8
  • 1,117
  • 1
  • 11
  • 22
21

I verified that chcon -Rt svirt_sandbox_file_t /path/to/volume does work and you don't have to run as a privileged container.

This is on:

  • Docker version 0.11.1-dev, build 02d20af/0.11.1
  • CentOS 7 as the host and container with SELinux enabled.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jeff mccormick
  • 321
  • 3
  • 5
  • 2
    See https://github.com/docker/docker/pull/5910 for official support for relabeling within Docker. – cpuguy83 Oct 20 '14 at 14:51
15

Try docker volume create.

mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash

Take a look at the document docker volume create.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cupen
  • 157
  • 1
  • 7
  • 3
    Tried a lot of answers about this issue on SO, but actually this one helped. Thanks! – Paul Jul 14 '17 at 09:38
  • It solved the error of permission .But now if i am trying to mount physical location it mounts voulme????@cupen – kunal verma Jan 13 '20 at 10:57
  • 1
    @kunalverma Yes. If you don't like it, here is the easier answer. https://stackoverflow.com/a/31334443/4909388 – cupen Jun 03 '20 at 09:45
8

I had a similar issue. Mine was caused by a mismatch between the UID of the host and the UID of the container's user. The fix was to pass the UID of the user as an argument to the docker build command and create the container's user with the same UID.

In the DockerFile:

ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER

In the build step:

docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID

After that, running the container and commands as per the OP gave me the expected result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RoboCop87
  • 825
  • 1
  • 8
  • 21
  • 3
    What if you don't know the UID until run-time? (I'm building an image for coleagues, to package up some tools which write back to their filesystem, but they have different UIDs). I guess I could keep it root and only adduser on run? – inger May 29 '18 at 17:06
  • I don't have a good answer to that, unfortunately. If anyone else has a solution I would be interested in it as well. I suspect the Docker entrypoint functionality might provide a solution. – RoboCop87 Jul 16 '18 at 17:40
  • I had a similar issue but I was running the container using docker run; this is how I adapted this helpful answer: `docker run --user=$UID -v /host/path:/container/path ` – Taoufik Mohdit Jan 01 '22 at 17:40
7

This issue is because SELinux is enabled on your machine. Check the below and disabled it.

[root@nfs-server ~]# getenforce
Enforcing
[root@nfs-server ~]# setenforce 0
[root@nfs-server ~]# getenforce
Permissive

If you want to just disable SELinux you can do this by using the --security-opt label:disable flag.

docker run --security-opt label:disable -v /run/docker.sock:/run/docker.sock POWERFULLCONTAINER
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Akshay Bobade
  • 79
  • 1
  • 1
  • 1
    Is there a less blunt why for enabling containers to read/write to host directories? – harm Feb 23 '23 at 09:28
0

I resolved that issue by using a data container. This also has the advantage of isolating the data from the application layer. You could run it like this:

docker run --volumes-from=<container-data-name> ubuntu

This tutorial provides a good explanation on the use of data containers.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tmsss
  • 1,979
  • 19
  • 23
-7

In my situation the problem was different. I don't know why, but even if a directory on the host had chmod 777 run on it, inside the Docker container it was visible as 755.

Running inside container sudo chmod 777 my_volume_dir fixed it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
CodeSandwich
  • 1,601
  • 13
  • 23