Lets say I have a container running with a non-root user and I want to bind-mount a volume directory from the host into that container. The container then will write to that directory. Say, the directory on the host is /tmp/container/data. If that path does not exist on the host, I observe that it gets created (by docker) with ownership root. As a consequence the container is not able to write anything into that directory (access denied) because my container is not running with user root.
Of course I can take care of creating the /tmp/container/data directory with correct permissions on the host side before starting the container, but this solution obviously does not scale - I will have to do it for each and every container where I want to use a bind volume from the host for which the directory does not exist.
So my question is, what's the best way to use bind-volumes from the host for directories that do not yet exist while still let a non-root container have write access to the volume.
You accurately described the normal behavior of docker, non-existent bind mounts from the docker engine will get initialized to an empty directory owned by root. Note that this doesn't happen with swarm mode, it will fail to schedule the container on the host instead.
Options to use to avoid this include:
Using named volumes. These get initialized to the directory permissions in the image at that location. This is as easy as changing the full path on the host to a short name of the volume.
Run the container as root, and make the entrypoint fix the permissions and drop to the user before launching the application. Something similar to this is done in a jenkins-docker project I threw out on github recently.
Include a script in the container with permissions setuid-root which performs the chown of the directory.
Related
I am creating a docker container, from an image.
In the shell script, I create a volume folder in container and host.
However, when I create a test file, for example, on the container side, I cannot modify it on host. Permission says its owned by root, so I don't have the access rights.
Is there a way to fix the permission or access right in the .sh file?
Well I'm running a container which will create/modify some files that I need to persist outside the life-cycle of the container. As a result, I am voluming a folder into the container for that purpose:
docker run -v $(pwd)/data:/app/data my-image
On the first run, $(pwd)/data does not exist, so Docker will create it for me. However, it creates it as the user its own daemon process is being executed as, which is root. This is problematic since the user within the container obviously is not root.
To fix this permission issue, I have created a user group in the host machine with the same ID as the user within the container, and have given them access to the parent folder of $(pwd)/data, and added group permission settings on the folder (chmod g+s). So if I run the following command as root:
sudo mkdir whatver
the folder whatever will be modifiable by that group and by extension, by the container's user. however when Docker creates the folder $(pwd)/data, it still is created as belonging to the group root, and again the user within the container cannot modify data inside it.
Right now, I have worked around this by making the required folders before running the container. However this is a dirty work-around, and I am looking for a cleaner solution. Has anyone else faced this issue? Is it an issue with Docker not respecting the group permission settings on the parent folder or am I missing something here / doing something wrong?
I don't consider making the folders first a dirty workaround, that's a best practice to me. Docker is running a bind mount for you, and this will fail when the source directory does not exist. For user convenience, docker will create this directory for you when you make a host mount and the directory is missing, but there's no need to use this functionality and you'll find other types of mounts will not perform the directory creation for you (e.g. the --mount syntax often used by swarm mode).
You can start your container as root and fix the permissions on the directory with an entrypoint, before running something like an exec gosu app_user app_server at the end to drop from root to the user. I do this in the entrypoint in my docker-base images, which is useful for a developer workflow where developers often need to dynamically update the container to work with their host environment.
Otherwise, you can switch to named volumes. These will be initialized when they are new or empty using the directory contents and permissions from the image. They are a best practice when you need persistence but not direct filesystem access to the contents of the volume from users on the host. You would instead access the named volume from inside of containers that mount the volume.
When starting a container and specifying a volume you can optionally append a third field that's a comma separated list of options like rw.
docker run -v /some-host/path:/some-container/path:rw
This same options are applicable in the docker.compose.yml
services:
myService:
image: some/image
volumes:
- /some-host/path:/some-container/path:rw
I thought that specifing rw would mean that the container would be able to read from and write to that directory (regardless of user). Contrary to my belief, when the host directory doesn't exist, docker creates it as drwxr-xr-x 2 root root no matter what I specify. The application in the container is not running on root though, so it tries to write to the mounted drive and get's Permission denied.
I've dug through the docker documents, even found this github issue describing the same issue, but can't find anything definitive that explains expected behavior.
So what exactly does rw(read/write) mean when specified as a third option for bind mounted directories?
As DavidMaze says in the comments
in the same way that / on your host is mounted read-write but isn’t world-writable on every file; if it were mounted read-only nobody could write any file.
And the docs:
If neither 'rw' or 'ro' is specified then the volume is mounted in read-write mode.
And
If you supply an absolute path for the host-dir, Docker bind-mounts to the path you specify.
The directory is "mounted" as rw by default. So think that to write in a directory it is not enough a rw mount, you also need file permissions on it. In the other hand, having full files permissions is not enough if the directory is mounted as read only. Think it as two layers permissions.
Also:
There is clear value in the ability to make bind mounts read-only, though. Containers are one example: an administrator may wish to create a container in which processes may be running as root. It may be useful for that container to have access to filesystems on the host, but the container should not necessarily have write access to those filesystems.
I want to achieve the following with Docker: I want to give a container access to a host directory, such that the container can make changes, but the changes are discarded once the container is exiting/removed (pretty much like an overlayfs).
Simply mounting the directory as a volume for the docker container seems like the wrong way to me, since changes made to a volume persist and I don't want that.
How do I tackle this problem?
The only way for a container to modify the host is to mount a directory between the host and the container. But the changes made by host or container will persist.
You could try the other way: COPY the files you want from host to container using a Dockerfile. The files will be only on the container. When you remove and launch another one, the new container will start with the original files.
I'm setting up docker-compose for my php project on my mac. I mount a shared volume with all the code from host machine into container.
Obviously, I do not want to run the php container as root user. But do I have another option?
I tried:
Changing owner of the project files in Dockerfile
Changing owner in entrypoint
Both methods work fine, until you create a file in IDE - in this case the file appears to be owner by root inside container and then you need to restart the container (which is horrible experience for development)
Changing UID for user www-data in container to UID from my host user.
It didn't work, since the files are owned by root
Do I miss any points here?