427

I've been playing around with Docker for a while and keep on finding the same issue when dealing with persistent data.

I create my Dockerfile and expose a volume or use --volumes-from to mount a host folder inside my container.

What permissions should I apply to the shared volume on the host?

I can think of two options:

  • So far I've given everyone read/write access, so I can write to the folder from the Docker container.

  • Map the users from host into the container, so I can assign more granular permissions. Not sure this is possible though and haven't found much about it. So far, all I can do is run the container as some user: docker run -i -t -user="myuser" postgres, but this user has a different UID than my host myuser, so permissions do not work. Also, I'm unsure if mapping the users will pose some security risks.

Are there other alternatives?

How are you guys/gals dealing with this issue?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Xabs
  • 4,544
  • 3
  • 18
  • 22
  • Checkout this [answer from a similar question](http://stackoverflow.com/a/20652410/434961). – Batandwa Jun 19 '14 at 09:08
  • 1
    You might also be interested in this thread which discusses this topic in some detail: https://groups.google.com/forum/#!msg/docker-user/cVov44ZFg_c/DGqHU4TL8RkJ – btiernay Jun 23 '14 at 20:39
  • 1
    Did you see http://container42.com/2014/11/18/data-only-container-madness/? – Philippe May 07 '15 at 18:28
  • At the moment, the Docker team is not planning to implement a native solution to mounting host-directories as a volume with specified uid/gid. See my comment and replies on this issue: https://github.com/docker/docker/issues/7198#issuecomment-230636074 – Quinn Comendant Sep 01 '16 at 22:28

15 Answers15

174

UPDATE 2016-03-02: As of Docker 1.9.0, Docker has named volumes which replace data-only containers. The answer below, as well as my linked blog post, still has value in the sense of how to think about data inside docker but consider using named volumes to implement the pattern described below rather than data containers.


I believe the canonical way to solve this is by using data-only containers. With this approach, all access to the volume data is via containers that use -volumes-from the data container, so the host uid/gid doesn't matter.

For example, one use case given in the documentation is backing up a data volume. To do this another container is used to do the backup via tar, and it too uses -volumes-from in order to mount the volume. So I think the key point to grok is: rather than thinking about how to get access to the data on the host with the proper permissions, think about how to do whatever you need -- backups, browsing, etc. -- via another container. The containers themselves need to use consistent uid/gids, but they don't need to map to anything on the host, thereby remaining portable.

This is relatively new for me as well but if you have a particular use case feel free to comment and I'll try to expand on the answer.

UPDATE: For the given use case in the comments, you might have an image some/graphite to run graphite, and an image some/graphitedata as the data container. So, ignoring ports and such, the Dockerfile of image some/graphitedata is something like:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
RUN mkdir -p /data/graphite \
  && chown -R graphite:graphite /data/graphite
VOLUME /data/graphite
USER graphite
CMD ["echo", "Data container for graphite"]

Build and create the data container:

docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata

The some/graphite Dockerfile should also get the same uid/gids, therefore it might look something like this:

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
# ... graphite installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]

And it would be run as follows:

docker run --volumes-from=graphitedata some/graphite

Ok, now that gives us our graphite container and associated data-only container with the correct user/group (note you could re-use the some/graphite container for the data container as well, overriding the entrypoing/cmd when running it, but having them as separate images IMO is clearer).

Now, lets say you want to edit something in the data folder. So rather than bind mounting the volume to the host and editing it there, create a new container to do that job. Lets call it some/graphitetools. Lets also create the appropriate user/group, just like the some/graphite image.

FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
  && useradd -r -g graphite graphite
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]

You could make this DRY by inheriting from some/graphite or some/graphitedata in the Dockerfile, or instead of creating a new image just re-use one of the existing ones (overriding entrypoint/cmd as necessary).

Now, you simply run:

docker run -ti --rm --volumes-from=graphitedata some/graphitetools

and then vi /data/graphite/whatever.txt. This works perfectly because all the containers have the same graphite user with matching uid/gid.

Since you never mount /data/graphite from the host, you don't care how the host uid/gid maps to the uid/gid defined inside the graphite and graphitetools containers. Those containers can now be deployed to any host, and they will continue to work perfectly.

The neat thing about this is that graphitetools could have all sorts of useful utilities and scripts, that you can now also deploy in a portable manner.

UPDATE 2: After writing this answer, I decided to write a more complete blog post about this approach. I hope it helps.

UPDATE 3: I corrected this answer and added more specifics. It previously contained some incorrect assumptions about ownership and perms -- the ownership is usually assigned at volume creation time i.e. in the data container, because that is when the volume is created. See this blog. This is not a requirement though -- you can just use the data container as a "reference/handle" and set the ownership/perms in another container via chown in an entrypoint, which ends with gosu to run the command as the correct user. If anyone is interested in this approach, please comment and I can provide links to a sample using this approach.

Raman
  • 17,606
  • 5
  • 95
  • 112
  • 46
    I'm afraid this is not a solution since you'll have the same issue with the data-only containers. At the end of the day, this containers will be using volumes shared from the host, so you'll still need to manage permissions on those shared folders. – Xabs Nov 19 '14 at 16:44
  • @Xabs I thought the same initially but once I saw the example in the docs (backing up the volume), I realized that most use cases should be possible from within a container. Which is why I asked for your specific use case. – Raman Nov 19 '14 at 21:21
  • Hmmm, your approach is interesting, but let me explain one of my cases: – Xabs Nov 20 '14 at 09:44
  • I run several containers with different applications on the same host: graphite, cabot, some Atlassian test stuff... and all of them need persistent data. In the host, data is stored in `/data/graphite`, `/data/cabot` etc. In both cases, mounting the volume directly and through a _data-only container_, I'm uncertain on what permissions to apply when creating a new data folder for, let's say, a new JIRA test instance. – Xabs Nov 20 '14 at 10:05
  • 2
    Bear in mind that I may need to edit the data folder from my host (ie: delete a test graphite key, delete my JIRA test home folder or update it with the latest production backup...). As far as I understand from your comment, I should be doing things like updating JIRA data via a 3rd container. In any case, what permissions would you apply to a new data folder `/data/newcontainer`? I assume you run `docker` as root (is it possible not to do so?) Also, is there any difference in those permissions if the data is mounted directly in the main container or through a _data-only container_? – Xabs Nov 20 '14 at 10:06
  • 2
    Thanks for your elaborate reply. Will test this as soon as I have the chance. Also, nice references both [your blog](https://medium.com/@ramangupta/why-docker-data-containers-are-good-589b3c6c749e) post and the one about [using minimal images for data containers](http://container42.com/2014/11/18/data-only-container-madness/). – Xabs Nov 25 '14 at 08:42
  • 3
    The only problem with this approach is its very easy to delete a container by mistake. Imagine if it happens to be your data container. I think (CMIIW) the data will still be in `/var/lib/docker` somewhere but still a huge pain – lolski Nov 27 '14 at 02:24
  • @lolski I think the docker guys are working on a new mechanism to manage volumes independently of containers. See https://github.com/docker/docker/pull/8484. Note that as long as you have any container that has the volume mounted using --volumes-from, you can recreate the data container by doing --volumes-from that existing container. – Raman Nov 27 '14 at 16:24
  • Excellent [blog post](http://container42.com/2014/11/18/data-only-container-madness/) thank so much for taking the time :) Could you add an update of my use case? In my team we git clone some source code and the idea is to use docker to run the app so the container needs to access a host directory (git cloned) in order to run the app. We don't want to git clone from within a data container. At [first](http://stackoverflow.com/a/24986166/511069) I thought we needed to match gid/uid for this but you may have a better idea. – Leo Gallucci Nov 28 '14 at 19:59
  • @LeoGallucci it seems to just be a variation of the same thing? i.e. create a container that does the `git clone` into the volume accessed via `--volume-from` a data container... – Raman Nov 28 '14 at 22:17
  • 3
    "you can just use the data container as a "reference/handle" and set the ownership/perms in another container via chown in an entrypoint"... @Raman: This is the section which finally saved me after having numerous permission issues not figured out. Using an entrypoint script and setting permissions in this works for me. Thanks for your elaborate explanation. It is the best I found on the web so far. – Vanderstaaij Jan 16 '15 at 23:59
  • With Docker things are changing fast. The volumes-from option enables to achieve the same results faster. See http://stackoverflow.com/questions/18496940/how-to-deal-with-persistent-storage-e-g-databases-in-docker?lq=1 and in particular the referenced blog post http://container42.com/2014/11/18/data-only-container-madness/ – Philippe May 07 '15 at 18:27
  • I think when you invoke `docker run` with `--volumes-from=graphitedata` the `VOLUME /data/graphite` declaration in the Dockerfile is redundant, if not even a collision (I did not find a corresponding specification in the docs). I have observed the following behavior: when using `--volumes-from=graphitedata`, the corresponding volume is available in the target container *with the same mount point as used in the data-only container*, without having any `VOLUME` entry in the Dockerfile for the target container. – Dr. Jan-Philip Gehrcke Jun 10 '15 at 19:10
  • @Jan-PhilipGehrcke `--volumes-from` is not the same thing as `-v`. The latter is equivalent to `VOLUME` in the Dockerfile, and binds a volume to that container. The former tells Docker to mount the volume from the specified (different) container. – Raman Jun 11 '15 at 04:21
  • @Raman: I know how these features behave when applied *independently*. My goal was to point out that you used both at the same time, referring to the **same volume** (I think so, at least). Your `--volumes-from=graphitedata` binds the volume into your consumer container to a certain mount point. That mount point happens to be `/data/graphite`, because that is the way you defined your data-only container. But you *also* use `VOLUME /data/graphite` in your consumer container. This is redundant and it is even unclear what the specified behavior for this situation should be. – Dr. Jan-Philip Gehrcke Jun 11 '15 at 20:48
  • @Jan-PhilipGehrcke I see what you mean. I believe `--volumes-from` overrides the VOLUME declaration in the container, but I'll update the answer to remove the "collision". – Raman Jun 12 '15 at 03:44
  • @Jan-PhilipGehrcke On second thought, no, I like it as is. You can even see an example of this in the Docker manual (the same `training/postgres` image is used both to create the volume and mount it from another container via `--volumes-from`). I like to add the VOLUME descriptor in the consumer container as well because it *documents* the volume requirement, but leaves the decision of where the volume comes from to the ops team at runtime. – Raman Jun 12 '15 at 03:51
  • When I thought I had this working, I created files on my volume from my primary container and they were still owned by root on the host. The volume was defined by my data-only container which was created with my user id. So when I created files from the data-only container they were owned by me. I probably don't understand this solution. – ThorSummoner Aug 19 '15 at 17:48
  • @ThorSummoner Yeah you're suffering from data container confusion. Basically this solution allows you to bypass worrying about the permissions on the host completely, so the fact that they are owned by root there doesn't matter. Reread the solution, and check out some of the linked blog posts. – Raman Aug 19 '15 at 18:35
  • @Raman I think my goal is different, I want a way for the docker container to have write access to a directory on the host and have the created files be owned by the uid who spawned the container. But not in retrospect I might be able to do the translation by relying on an sshfs mount from the app container to the data container, such that the sshd reads and writes the files as my UID – ThorSummoner Aug 19 '15 at 19:36
  • 1
    @Raman As of docker 1.9.0, data-only containers are no longer a recommended pattern: https://github.com/docker/docker/issues/17798 – thpani Mar 02 '16 at 11:19
  • @thpani Thanks, I added an update to the top of the text accordingly. – Raman Mar 02 '16 at 13:51
  • Great explanation but "add our user and group first" is still a hack to some extent. When the graphite image upgrades to the next version of debian and they've added more users to the base system... boom. Specifying `-u 1000` would be less hacky, and would fail early if your "consistent ID" is conflicted. – sourcejedi Mar 14 '16 at 17:46
  • 3
    While this is probably the best solution for production environments, it falls short if container is for development, where you need to edit code via IDE inside host and see results instantly when calling container. – Marius Balčytis Mar 21 '16 at 20:20
  • 1
    This answer has a long talk, but still not fixed the permission problem in data container. I think @Dimitris answer which is based on redis's solution should be the right one. – srain Aug 22 '16 at 17:07
  • Yeah, as @srain said, this answer doesn't cover the problem with permissions. In any case, you need to able to mount volume from that data-container into a host. – Kirby Oct 14 '16 at 14:17
  • @Raman --> Can you help me on this. I am struggling with storing data within containers and committing & pushing on docker hub so that i can be independent of host machines. This is the reference to my discussion on stackoverflow [link](https://stackoverflow.com/questions/45808227/docker-container-image-not-saving-changes/45809959?noredirect=1#comment78600631_45809959) – AJm Aug 23 '17 at 01:47
  • Answer by @alex_edev was way much simpler! https://stackoverflow.com/a/45640469/1112124 – icarito Apr 09 '18 at 03:31
80

A very elegant solution can be seen on the official redis image and in general in all official images.

Described in step-by-step process:

  • Create redis user/group before anything else

As seen on Dockerfile comments:

add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added

  • Install gosu with Dockerfile

gosu is an alternative of su / sudo for easy step-down from root user. (Redis is always run with redis user)

  • Configure /data volume and set it as workdir

By configuring the /data volume with the VOLUME /data command we now have a separate volume that can either be docker volume or bind-mounted to a host dir.

Configuring it as the workdir (WORKDIR /data) makes it be the default directory where commands are executed from.

  • Add docker-entrypoint file and set it as ENTRYPOINT with default CMD redis-server

This means that all container executions will run through the docker-entrypoint script, and by default the command to be run is redis-server.

docker-entrypoint is a script that does a simple function: Change ownership of current directory (/data) and step-down from root to redis user to run redis-server. (If the executed command is not redis-server, it will run the command directly.)

This has the following effect

If the /data directory is bind-mounted to the host, the docker-entrypoint will prepare the user permissions before running redis-server under redis user.

This gives you the ease-of-mind that there is zero-setup in order to run the container under any volume configuration.

Of course if you need to share the volume between different images you need to make sure they use the same userid/groupid otherwise the latest container will hijack the user permissions from the previous one.

Mustapha-Belkacim
  • 1,653
  • 1
  • 15
  • 19
Dimitris
  • 1,276
  • 10
  • 9
  • 15
    The accepted answer is informative, but it only led me down a weeklong path of frustrations with permissions to finding this answer that actually provides a canonical way to solve the problem. – m0meni Aug 27 '15 at 04:20
  • 5
    Very well explained also here : https://denibertovic.com/posts/handling-permissions-with-docker-volumes/ – kheraud May 16 '16 at 13:28
  • So? How do I make volume writable from inside docker? `chown` it inside `ENTRYPOINT` script? – Gherman Oct 29 '19 at 14:31
  • @Gherman yes: https://github.com/docker-library/redis/blob/master/docker-entrypoint.sh `find . \! -user redis -exec chown redis '{}' +` – Life5ign May 09 '23 at 00:06
39

This is arguably not the best way for most circumstances, but it's not been mentioned yet so perhaps it will help someone.

  1. Bind mount host volume

    Host folder FOOBAR is mounted in container /volume/FOOBAR

  2. Modify your container's startup script to find GID of the volume you're interested in

    $ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)

  3. Ensure your user belongs to a group with this GID (you may have to create a new group). For this example I'll pretend my software runs as the nobody user when inside the container, so I want to ensure nobody belongs to a group with a group id equal to TARGET_GID

  EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)

  # Create new group using target GID and add nobody user
  if [ $EXISTS == "0" ]; then
    groupadd -g $TARGET_GID tempgroup
    usermod -a -G tempgroup nobody
  else
    # GID exists, find group name and add
    GROUP=$(getent group $TARGET_GID | cut -d: -f1)
    usermod -a -G $GROUP nobody
  fi

I like this because I can easily modify group permissions on my host volumes and know that those updated permissions apply inside the docker container. This happens without any permission or ownership modifications to my host folders/files, which makes me happy.

I don't like this because it assumes there's no danger in adding yourself to an arbitrary groups inside the container that happen to be using a GID you want. It cannot be used with a USER clause in a Dockerfile (unless that user has root privileges I suppose). Also, it screams hack job ;-)

If you want to be hardcore you can obviously extend this in many ways - e.g. search for all groups on any subfiles, multiple volumes, etc.

Hamy
  • 20,662
  • 15
  • 74
  • 102
  • 5
    Is this targeted at _reading_ files from the mounted volume? I'm looking for a solution for writing files without them being owned by another user than they who created the docker container. – ThorSummoner Aug 18 '15 at 21:52
  • I'm using this approach since Aug'15 . Everything was OK. Just the permissions of the files created inside the container were distinct. Both, users (inside and outside the container) have the ownerships of their files but both had read access them because they did belong to the same group created by this solution. The problem started when a use case did impose write access to common files. Greater problem was that the shared volumed had git files (its a volume to test dev source files in the same production context). Git started to warn about access problem to the shared code. – yucer Mar 17 '16 at 23:29
  • 1
    I think a better grep for `$TARGET_GID` would be to use `grep ':$TARGET_GID:'`, otherwise if the container has, e.g. gid 10001 and your host is 1000, this check will pass but it shouldn't. – robhudson Mar 07 '18 at 19:21
  • This solution is ok also for *writing*. – You eventually need to run `chmod g+w -R` in the git repository *OR* on the mounted volume, so that `tempgroup` can still write from inside the container. – Kamafeather Jun 13 '21 at 18:28
  • Only downside with the approach of my comment is, depending on your OS (like on macOS), you might have `umask 022` set, rather than `umask 002`, so every file you create on the host side (e.g. from the IDE) will need to run the same `chmod g+w` command, for any file that needs to be writeable in the container. But most of the times this is not the case, for source code files created via the IDE. – Kamafeather Jun 13 '21 at 18:32
24

The same as you, I was looking for a way to map users/groups from host to docker containers and this is the shortest way I've found so far:

  version: "3"
  services:
    my-service:
      .....
      volumes:
        # take uid/gid lists from host
        - /etc/passwd:/etc/passwd:ro
        - /etc/group:/etc/group:ro
        # mount config folder
        - path-to-my-configs/my-service:/etc/my-service:ro
        .....

This is an extract from my docker-compose.yml.

The idea is to mount (in read-only mode) users/groups lists from the host to the container thus after the container starts up it will have the same uid->username (as well as for groups) matchings with the host. Now you can configure user/group settings for your service inside the container as if it was working on your host system.

When you decide to move your container to another host you just need to change user name in service config file to what you have on that host.

Timo Huovinen
  • 53,325
  • 33
  • 152
  • 143
Alex Myznikov
  • 919
  • 9
  • 19
  • This is a great answer, very simple if you want to run containers that handle files on a base system, without exposing the rest of the system. – icarito Apr 09 '18 at 03:29
  • This is my favorite answer. Also, I saw a similar recommendation elsewhere with the docker run command where you pass in your current username/groups via `-u $( id -u $USER ):$( id -g $USER )` and you no longer have to worry about the user name. This works well for local dev environments where you want to generate files (binaries for example) which you have read/write access to by default. – matthewcummings516 Sep 16 '18 at 18:04
  • 1
    This is honestly the expected end-user behavior. Discovering that my docker container's user has the uid/gid of the host but that same uid isn't in the other gid's that I explicitly setup prior to the docker run command baffled me. end user was not being considered when docker came up with their default behavior – LeanMan Jun 03 '21 at 06:28
  • This doesn't always work for some weird reasons. After I tried, `www-data ` was good but mysql didn't seem to accept `mysql` user in host and tried to launch process as root, which it itself does not allow later for safety reasons. – Varun Garg Jun 17 '23 at 22:50
20

Try to add a command to Dockerfile

RUN usermod -u 1000 www-data

credits goes to https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272

FDisk
  • 8,493
  • 2
  • 47
  • 52
16

Ok, this is now being tracked at docker issue #7198

For now, I'm dealing with this using your second option:

Map the users from host into the container

Dockerfile

#=======
# Users
#=======
# TODO: Idk how to fix hardcoding uid & gid, specifics to docker host machine
RUN (adduser --system --uid=1000 --gid=1000 \
        --home /home/myguestuser --shell /bin/bash myguestuser)

CLI

# DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest

UPDATE I'm currently more inclined to Hamy answer

Community
  • 1
  • 1
Leo Gallucci
  • 16,355
  • 12
  • 77
  • 110
  • 1
    use the command `id -u `, `id -g `, `id -G ` to get the user id and group id of a specific user instead – lolski Oct 28 '14 at 03:33
  • 1
    https://unix.stackexchange.com/questions/123062/user-permissions-inside-and-outside-of-lxc-containers – elim Oct 30 '14 at 12:13
  • 17
    This destroys container portability across hosts. – Raman Nov 19 '14 at 15:56
  • 2
    Docker issue #7198 has come to the conclusion they will not be implementing a native solution for this. See my comment an replies at https://github.com/docker/docker/issues/7198#issuecomment-230636074 – Quinn Comendant Sep 01 '16 at 22:30
  • @Raman, some might say that bind mounts already significantly decrease the portability. – Artfaith Sep 05 '22 at 22:17
  • @faither you won't get any disagreement from me. You don't need to use them (for production containers)! – Raman Sep 06 '22 at 02:15
7

My approach is to detect the current UID/GID, then create such user/group inside the container and execute the script under him. As a result, all files he will create will match the user on the host:

# get the location of this script no matter what your current folder is, this might break between shells so make sure you run bash
LOCAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# get current IDs
USER_ID=$(id -u)
GROUP_ID=$(id -g)

echo "Mount $LOCAL_DIR into docker, and match the host IDs ($USER_ID:$GROUP_ID) inside the container."

docker run -v $LOCAL_DIR:/host_mount -i debian:9.4-slim bash -c "set -euo pipefail && groupadd -r -g $GROUP_ID lowprivgroup && useradd -u $USER_ID lowprivuser -g $GROUP_ID && cd /host_mount && su -c ./runMyScriptAsRegularUser.sh lowprivuser"
Anton Krug
  • 1,555
  • 2
  • 19
  • 32
5

For secure and change root for docker container an docker host try use --uidmap and --private-uids options

https://github.com/docker/docker/pull/4572#issuecomment-38400893

Also you may remove several capabilities (--cap-drop) in docker container for security

http://opensource.com/business/14/9/security-for-docker

UPDATE support should come in docker > 1.7.0

UPDATE Version 1.10.0 (2016-02-04) add --userns-remap flag https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2

czerasz
  • 13,682
  • 9
  • 53
  • 63
umount
  • 121
  • 1
  • 4
  • I'm running docker 1.3.2 build 39fa2fa (latest) and see no traces of the `--uidmap` nor the `--private-uids` options. Looks like the PR didn't make it and wasn't merged. – Leo Gallucci Nov 28 '14 at 19:05
  • It's not merge in core, if you want you may use it how patch. Now only possible restrict some capabilities and run you application in container from non root user. – umount Nov 29 '14 at 08:22
  • June 2015 and I don't see this merged in docker 1.6.2 is your answer still valid? – Leo Gallucci Jun 03 '15 at 12:10
  • 1
    Issue still open. Developer should add support in 1.7 version. (--root option) https://github.com/docker/docker/pull/12648 – umount Jun 16 '15 at 07:45
  • I upgraded and still don't have this feature in `Docker version 1.7.0, build 0baf609` – Leo Gallucci Jun 26 '15 at 07:30
  • 2
    It seems the developers once again moved the release with this functionality. Docker developer "icecrime" say `"We apparently do have so some of conflicting designs between libnetwork and user namespaces ... and something we'd like to get in for 1.8.0. So don't think we're dropping this, we're definitely going to take a break after all these, and see how we need to reconsider the current design and integration of libnetwork to make this possible. Thanks!"` https://github.com/docker/docker/pull/12648 So I think we should wait next stable version. – umount Jun 29 '15 at 12:10
  • The trick with `--userns-remap` doesn't work yet in `Docker version 1.10.3, build 20f81dd`. See my comment [here](https://github.com/docker/docker/issues/21130#issuecomment-195649924). – czerasz Mar 12 '16 at 03:20
5

Here's an approach that still uses a data-only container but doesn't require it to be synced with the application container (in terms of having the same uid/gid).

Presumably, you want to run some app in the container as a non-root $USER without a login shell.

In the Dockerfile:

RUN useradd -s /bin/false myuser

# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser

...

ENTRYPOINT ["./entrypoint.sh"]

Then, in entrypoint.sh:

chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; $@"
Ethan
  • 1,057
  • 1
  • 14
  • 23
3

Base Image

Use this image: https://hub.docker.com/r/reduardo7/docker-host-user

or

Important: this destroys container portability across hosts.

1) init.sh

#!/bin/bash

if ! getent passwd $DOCKDEV_USER_NAME > /dev/null
  then
    echo "Creating user $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME"
    groupadd --gid $DOCKDEV_GROUP_ID -r $DOCKDEV_GROUP_NAME
    useradd --system --uid=$DOCKDEV_USER_ID --gid=$DOCKDEV_GROUP_ID \
        --home-dir /home --password $DOCKDEV_USER_NAME $DOCKDEV_USER_NAME
    usermod -a -G sudo $DOCKDEV_USER_NAME
    chown -R $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME /home
  fi

sudo -u $DOCKDEV_USER_NAME bash

2) Dockerfile

FROM ubuntu:latest
# Volumes
    VOLUME ["/home/data"]
# Copy Files
    COPY /home/data/init.sh /home
# Init
    RUN chmod a+x /home/init.sh

3) run.sh

#!/bin/bash

DOCKDEV_VARIABLES=(\
  DOCKDEV_USER_NAME=$USERNAME\
  DOCKDEV_USER_ID=$UID\
  DOCKDEV_GROUP_NAME=$(id -g -n $USERNAME)\
  DOCKDEV_GROUP_ID=$(id -g $USERNAME)\
)

cmd="docker run"

if [ ! -z "${DOCKDEV_VARIABLES}" ]; then
  for v in ${DOCKDEV_VARIABLES[@]}; do
    cmd="${cmd} -e ${v}"
  done
fi

# /home/usr/data contains init.sh
$cmd -v /home/usr/data:/home/data -i -t my-image /home/init.sh

4) Build with docker

4) Run!

sh run.sh
Community
  • 1
  • 1
Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
0

To share folder between docker host and docker container, try below command

$ docker run -v "$(pwd):$(pwd)" -i -t ubuntu

The -v flag mounts the current working directory into the container. When the host directory of a bind-mounted volume doesn’t exist, Docker will automatically create this directory on the host for you,

However, there are 2 problems we have here:

  1. You cannot write to the volume mounted if you were non-root user because the shared file will be owned by other user in host,
  2. You shouldn't run the process inside your containers as root but even if you run as some hard-coded user it still won't match the user on your laptop/Jenkins,

Solution:

Container: create a user say 'testuser', by default user id will be starting from 1000,

Host: create a group say 'testgroup' with group id 1000, and chown the directory to the new group(testgroup

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
0

In my specific case, I was trying to build my node package with the node docker image so that I wouldn't have to install npm on the deployment server. It worked well until, outside out the container and on the host machine, I tried to move a file into the node_modules directory that the node docker image had created, to which I was denied permissions because it was owned by root. I realized that I could work around this by copying the directory out of the container onto the host machine. Via docker docs...

Files copied to the local machine are created with the UID:GID of the user which invoked the docker cp command.

This is the bash code I used to change ownership of the directory created by and within the docker container.

NODE_IMAGE=node_builder
docker run -v $(pwd)/build:/build -w="/build" --name $NODE_IMAGE node:6-slim npm i --production
# node_modules is owned by root, so we need to copy it out 
docker cp $NODE_IMAGE:/build/node_modules build/lambda 
# you might have issues trying to remove the directory "node_modules" within the shared volume "build", because it is owned by root, so remove the image and its volumes
docker rm -vf $NODE_IMAGE || true

If needed, you can remove the directory with a second docker container.

docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
johnklawlor
  • 1,708
  • 2
  • 13
  • 15
0

If you are doing this for development, a good solution is to use bindfs:

  1. Keep the source code owned by the container user. (If possible, let the container clone the source code.)
  2. Use bindfs and map the folder for the host user.

Here is how my docker-compose setup looks now:

project:
  web/src # Container clones it using init scripts.
  web/log
  __web__/src # Host user uses this. It's just bindfs mirror.
  __web__/log

I have thought about this problem for over a year, and bindfs is the easiest option I have come across. There are no runtime costs apart from cloning.

Nishant
  • 20,354
  • 18
  • 69
  • 101
0

I finally ended up writing a script that syncs selected user ids and group ids from host to container using usermod and groupmod.

docker compose:

volumes:
    - /etc/passwd:/etc/passwd.src:ro
    - /etc/group:/etc/group.src:ro
environment:
    - host_users=www-data,mysql
    - host_groups=www-data,mysql,staff

script:

#!/bin/bash

for user in ${host_users//,/ }; do
    echo "syncing user $user" 
    uid=$(grep "^$user:" /etc/passwd.src | awk -F: '{print $3}')
    if [ ! -z "$uid" ]; then
        RET=1
        while [[ RET -ne 0 ]]; do
            usermod -u $uid $user
            RET=$?
            if [[ RET -eq 4 ]]; then
                existing_user=$(id $uid -u)
                existing_user_new_id=$(shuf -i 101-498 -n 1)
                usermod -u $existing_user_new_id $existing_user
                sleep 1
            elif [[ RET -ne 0 ]]; then
                sleep 5
            fi
        done
    else
        echo "syncing user $user, not found in host" 
    fi
done

for group in ${host_groups//,/ }; do
    echo "syncing group $group" 
    gid=$(grep "^$group:" /etc/group.src | awk -F: '{print $3}')
    if [ ! -z "$gid" ]; then
        RET=1
        while [[ RET -ne 0 ]]; do
            groupmod -g $gid $group
            RET=$?
            if [[ RET -eq 4 ]]; then
                existing_group=$(getent group $gid | awk -F: '{print $1}')
                existing_group_new_id=$(shuf -i 1-32766 -n 1)
                groupmod -g $existing_group_new_id $existing_group
                sleep 1
            elif [[ RET -ne 0 ]]; then
                sleep 5
            fi
        done
    else
        echo "syncing group $group, not found in host" 
    fi
done

Also available here: https://github.com/Varun-garg/docker-sync-ids

Varun Garg
  • 2,464
  • 23
  • 37
-5

If you using Docker Compose, start the container in previleged mode:

wordpress:
    image: wordpress:4.5.3
    restart: always
    ports:
      - 8084:80
    privileged: true
  • 5
    This might make it easier to mount volumes but .. Wordpress launched in priviledge mode? That's a horrible idea - that is asking to get compromised. https://wpvulndb.com/wordpresses/453 – Colin Harrington Feb 17 '17 at 23:38