Docker makes local environment same as CI and production, right? Well, in theory. Recently we had a single test failing in CI (dockerized CircleCI v2) but passing in a Docker container running locally. What the hell!
The test basically checked file permissions. We were creating a folder, then changing its permission to disallow listing its contents, and then made an attempt to list the contents. We were expecting the attempt to fail - and when running in plain Mac OSX terminal it did.
// the setup
We placed this code inside a Docker image using the following Dockefile. You can find the example code in repo bahmutov/docker-file-permissions
- the problematic Dockerfile is in branch
I built the container and ran it
> docker run --name perms -it gleb/docker-file-permissions /bin/bash
We can list the current user in the container (which is
root with id 0)
and the permissions on the folder
foo. I am using commands
ls -ld and
stat present on Linux and Mac to inspect user and file
[email protected]97d0341957e8:/usr/src/app# id
So we see that we are user with
id=0 from group
gid=0 and the file
belongs to this user and group. Despite changed permissions, the folder is
- Create a new user
personand switch to it
- Copy and install app inside the new user's home folder.
If we had global NPM installs, we would need to give user
/usr/local/node folder. In our example everything was local (as it should
be in general).
Rebuilding the image and running the container catches the expected exception
[email protected]:~/app$ node .
Inspecting the user and the folder shows the non-root owner
[email protected]:~/app$ id
Glad it is working!
Geoff Goodman has pointed out that our problem
was not with user namespaces (how Docker can map internal user to external
user), but with
root user permission. In fact, he asked, how could our
local Docker test pass when we were running as root?!
The full picture: in order to save time and avoid building Docker image with
full source code, we only build the run environment and mapped source folder
as data volume at runtime. The Dockerfile has nothing, just plain Node image
(I pushed this code as
Running container with mapped current folder brings source
$ docker run --name perms -v $PWD:/usr/src/app -it gleb/docker-file-permissions /bin/bash
root@50415a48be51:/usr/src/app# node .
So running as
root with mapped data volume on Mac OSX
(Docker 17.06.0-rc1-ce-mac13) hides the problem, which becomes apparent
when running Docker on true Linux box.
Same Geoff Goodman also reminded me that Node Docker image has
a non-root user already named
node (surprise, surprise).
I could have used it and not create my own
person. In my Docker file I could
have switched to this user with
USER node or when running the container
-u node - this is assuming we gave this user permission to create folders
in the container.