File permissions on Docker volumes

When a host directory is mounted into a docker container, how are file permissions handled?

Docker copies files permissions from the host to the container verbatim, inluding numeric user ids. E.g. if the owner of a file in the host file system is user 42, then the owner of the same file in the container would also be user 42. The trouble is, user IDs are not synchronized between the host and the container; there is no guarantee that user 42 in the container OS is the same as in the host OS, or that it even exists. The same goes for groups.


Let’s run a little example. I will use Ubuntu as a host and Alpine as a container. Let’s prepare a folder with a file and give it some custom ownership:

$ mkdir folder
$ touch folder/file
$ sudo chown -R `whoami`:www-data folder
$ ls -l folder
total 0
-rw-rw-r-- 1 ikriv www-data 0 Apr 11 01:20 file

Here www-data is a standard Ubuntu group that is used for web server. You can substitute it with a different group if you wish.

Now, let’s mount the folder into an Alpine container and see what happens:

$sudo docker run -v `pwd`/folder:/etc/folder alpine sh -c "ls -l /etc/folder"
total 0
-rw-rw-r--    1 1000     xfs              0 Apr 11 05:20 file

Here we create a container based on the stock “alpine’ image from the Dockerhub, use -v option to map our host folder to /etc/folder in the container, and execute sh process in the container, asking it to run command ls -l /etc/folder. We need `pwd`, because -v seems to want an absolute path.

In the container, the owner of the file is “1000”, and the group is “xfs”, while on the host they are “ikriv”, and “www-data” respectively. Why is that? This is because file permissions contain numeric user ID and numeric group ID, not the names. The ID of the user ikriv happens to be 1000 on the host (this may vary), and the ID of the group www-data is 33 (this is fairly standard and should be the same on your machine as long as it is running Ubuntu).

$ id -u ikriv
$ id -g www-data

However, in the stock Alpine image, user 1000 does not exist at all, and group 33 is called “xfs”:

$ sudo docker run alpine getent passwd 0
$ sudo docker run alpine getent passwd 1000
$ sudo docker run alpine getent group 33

The first command verifies that user 0 on Alpine is root, the second command demonstrates that user 1000 does not exist (hence no output), and the third command shows that group 33 is xfs.


The good news is that in many container scenarios file permissions don’t matter. The commands in the container are often executed as root, and root usually ignores file permissions. Root is always user 0, so files owned by root on the host will be owned by root in the container and vice versa. However, if more advanced permissions are desired, things can get tricky.

One way to handle it is to ensure that the host and the container use the same or closely related distros (e.g. Ubuntu/Debian), which have the same standard IDs for most things, and avoid giving ownership to custom users. For example, www-data is always 33 on Ubuntu.

The alternative is to use only custom users and groups in both the host and the container, create them with explicit numeric IDs, and make sure they don’t collide with anything existing. E.g. create user and group my-www-data with numeric id 6442 in both the host and the container, and reconfigure the web server to use my-www-data instead of standard www-data.

Suggested solution

According to various sources, suggested solution is not to map host directories into containers, but to use volumes. This sounds like a great idea, but required quite a few extra work in my case. The directory I am mapping happens to contain a clone of a private Git repository that I access via SSH using RSA certificates. It is possible to do this from a container, but I would need to setup Git, the SSH infrastructure, and isntall all proper certificates in that container, which is doable, but seemed like an overkill to me.

Thus, at the moment I chose to run both my host and my containers run Ubuntu. This may be limiting and not portable, but I did not have time or desire to reconfigure everything to use custom groups, nor to setup a container for git-SSH access.


  1. Or just create an alpine-www-data user and group on the host ubuntu system add the group to your user and set alpine to use a umask of 002 instead on 022.


Leave a Reply

Your email address will not be published. Required fields are marked *