83

I have a docker with a php application on it

I have a share volume, for example

/home/me/dev/site <=> /var/www/site

I can write something in my host, it will be sync with the container

if I launch

sudo docker exec test touch /var/www/site/test.txt

It works

But if my server is trying to create a file as www-data this is not working because of the rights.

Is there a way to give access to my shared volumes to www-data ?

I am using boot2docker

Ajouve
  • 9,735
  • 26
  • 90
  • 137
  • Does that user need write permission to the volume, or does it need the ability to execute docker (as a wrapper for managing the volume)? – scrowler Mar 25 '15 at 00:09
  • the user needs to create files in the volume, not just execute files from the volume – Ajouve Mar 25 '15 at 00:13
  • And does that creation and execution happen through the shell or do you have to use the `docker exec` command to do that? – scrowler Mar 25 '15 at 00:18
  • No, this is a website, it can create a file when I load a page for example – Ajouve Mar 25 '15 at 00:20
  • So what are the current permissions (i.e. user and group) for that folder you write to? If it's root you will probably need to `chown` it to a generic user, or create a user group that can contain both the generic/root user and the www-user, and assign that group as the owner of the folder – scrowler Mar 25 '15 at 00:23
  • I have for example `drwxr-xr-x 1 1000 staff 340 Mar 24 21:52 web` in my shared folder when I execute `docker exec test ls -l /var/www/site` – Ajouve Mar 25 '15 at 00:44

3 Answers3

131

(Bind-mounted) volumes in Docker will maintain the permissions that are set on the Docker host itself. You can use this to set the permissions on those files and directories before using them in the container.

Some background;

Permissions in Linux are based on user and group ids ('uid' / 'gid'). Even though you see a user- and group name as owner, those names aren't actually important in Linux, they are only there to make it easier for you to see who's the owner of a file (they are looked up from the /etc/passwd file).

You can set any uid/gid on a file; a user doesn't have to exist when setting those permissions. For example;

touch foobar && sudo chown 1234:5678 foobar && ls -la foobar

# UID and GID are set to 1234 / 5678, even though there's no such user
-rw-rw-r-- 1 1234 5678 0 Mar 25 19:14 foobar

Checking permissions (inside and outside a container)

As mentioned above, Docker maintains ownership of the host when using a volume. This example shows that permissions and ownership in the volume are the same outside and inside a container;

# (First create a dummy site)

mkdir -p volume-example/site && cd volume-example
echo "<html><body>hello world</body></html>" > site/index.html

# Permissions on the Docker host;

ls -n site

# total 4
# -rw-rw-r-- 1 1002 1002 38 Mar 25 19:15 index.html

# And, permissions inside a nginx container, using it as volume;

sudo docker run --rm -v $(pwd)/site:/var/www nginx ls -n /var/www

# total 4
# -rw-rw-r-- 1 1002 1002 38 Mar 25 19:15 index.html

Setting the permissions

As explained, a user doesn't have to exist in order to use them, so even if we don't have a www-data user on the Docker host, we can still set the correct permissions if we know the "uid" and "gid" of that user inside the container;

Let's see what the uid and gid of the www-data user is inside the container;

sudo docker run --rm nginx id www-data

# uid=33(www-data) gid=33(www-data) groups=33(www-data)

First check the state before changing the permissions. This time we run the nginx container as user www-data;

sudo docker run \
  --rm \
  --volume $(pwd)/site:/var/www \
  --user www-data nginx touch /var/www/can-i-write.txt

# touch: cannot touch `/var/www/can-i-write.txt': Permission denied

Next, set the permissions on the local directory, and see if we are able to write;

sudo chown -R 33:33 site

sudo docker run \
   --rm \
   --volume $(pwd)/site:/var/www \
   --user www-data nginx touch /var/www/can-i-write.txt

Success!

Buurman
  • 1,914
  • 17
  • 26
thaJeztah
  • 27,738
  • 9
  • 73
  • 92
  • 4
    Thanks, you saved my day, It works well on linux. But but it doesn't work on Osx with docker-machine. Any suggestion? – Hemerson Varela Mar 03 '16 at 21:20
  • 14
    That's a great explanation, thanks. But what if we want to use different UID/GID on a host machine and inside a container? For example `33` could be a legitimate user inside a container, but could be an "evil person" on a host machine. Also, I want for all files to belong to my user account on a host machine and not to some mysterious user `33`. – Slava Fomin II Feb 11 '19 at 22:25
  • That's not possible, unless you're using something like `shiftfs` (https://lwn.net/Articles/687354/). If you're bind-mounting files, files inside the container are _the same files_ as the files outside of the container, so changing permissions on those files would change the permissions on the host as well. – thaJeztah Feb 14 '19 at 00:29
  • 1
    What if the uid of the container clashes with the uid of an existing user in the host? You'll end up giving them access to it. Also, you're setting some dangling uids in your host system that only make sense for the container. For these reasons I think it's better to do it the other way around: setting the uid of the container to match the one of the host or make use of the "docker" group on linux – disklosr May 14 '19 at 13:49
  • 2
    > I think it's better to do it the other way around: setting the uid of the container to match the one of the host or make use of the "docker" group on linux The `docker` group should not be used as it provides access to the docker socket (API access). As to matching the container to run as the current user; The scenario described here is most useful for development situations (working on files outside of the container, but giving the container access), however, – thaJeztah May 22 '19 at 13:51
  • 2
    making the container use a different than it is designed to run on will make your development situation differ more from the production situation (you should not use bind-mounts in production), which may cause other headaches down the road. – thaJeztah May 22 '19 at 13:51
  • Great answer @thaJeztah, I'm curious why "you should not use bind-mounts in production", did not find documentation on that. – Nagev Sep 19 '22 at 10:52
  • 1
    @Nagev (I'll try to keep it short for the comments section); In production / deploy, it would be more common to build an image for your application (and if needed, use (managed) volumes for state). That way you're able to test the exact code you'll be running before deploying. Bind-mounting your application's source into a container means that your application depends on state on the host, which means that (e.g.) it won't be possible to run it on another machine. – thaJeztah Sep 21 '22 at 12:36
  • Thanks, I see your point. And then the `docker-compose.yml` would just reference those images in production. But I mean, for things like logging, there should be no issues if one prefers to use bind mounts in the host instead of managed volumes, aside from the fact that the development environment now must match the production environment... – Nagev Sep 21 '22 at 12:54
  • @Nagev you _can_ use bind-mounts for that, but storage of bind-mounts is not "managed" (something needs to clean them up). The standard approach for logs in containers is to print to `STDOUT` and `STDERR`; that output is captured by docker and send to the logging driver that's configured for the container, which allows for log-rotation to be configured, and to use `docker logs` to view them, but also to (for production) send them to a remote logging aggregator; see https://docs.docker.com/config/containers/logging/ and https://docs.docker.com/config/containers/logging/configure/ – thaJeztah Sep 29 '22 at 08:30
49

Add the following lines to your dockerfile and rebuild your image

RUN usermod -u 1000 www-data
RUN usermod -G staff www-data
Tunaki
  • 132,869
  • 46
  • 340
  • 423
István Döbrentei
  • 948
  • 10
  • 20
  • 4
    Perfect! That is what I've been looking for. http://linux.die.net/man/8/usermod docs for this command. Basically www-data's user id is changed to 1000 and it's added to staff group. Why 1000 or staff? Run `docker exec -it CONTAINER_NAME_OR_ID bash` Once in run `ls -la /var/www` you will see ex. `drwxr-xr-x 1 1000 staff 238 Aug 20 20:14 html` – Krzysztof Boduch Aug 20 '16 at 20:23
  • 1
    This works for me on my Ubuntu desktop because my uid is 1000. If you're not the first user account (which gets 1000 by default) you'll need to replace 1000 with your uid. – Ethan Hohensee Apr 08 '18 at 14:03
2

What I noticed from the answers above is that even though the container has write permission to the mounted volume, from outside, you cannot modify the files created by the container.

This is the solution that I found to be the best.

First, add this to your Dockerfile

ARG USER_ID
ARG GROUP_ID

RUN addgroup --gid $GROUP_ID user
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
USER user

Then use this when building your image

docker build -t myimage \
  --build-arg USER_ID=$(id -u) \
  --build-arg GROUP_ID=$(id -g) .

Basically, this synchronizes the user inside the container with the user outside so that they have the same permission to the mounted volume. No need to chown and stuff.

Note: this resembles VSCode's method to make files created inside devcontainer accessible to the user outside. You can checkout devcontainer's build log for more detail.