What does this solve?
Docker on Windows and OSX runs ontop a VM. That VM usually ensures that your local machine’s UID and GID and the container UID and GID work fine out of the box.
For Linux, there is no VM layer. Docker instead merely runs by taking advantage of kernel resources. Whilst this results in a much more efficient and speedy performance, this can result in odd behaviors, like you not having permissions inside the container to edit pre-existing files and folders that have been mounted into your container.
Example code for Docker that we’re assuming in this use case
Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.20-alpine3.16 AS base
RUN apk --no-cache add bash bash-completion tzdata make hugo \
&& find /tmp -mindepth 1 -maxdepth 1 | xargs rm -rf
WORKDIR /src
SHELL ["bash"]
FROM base as dev
ENTRYPOINT ["hugo", "server", "-DEF", "--watch=true", "--bind=0.0.0.0", "--baseURL=http://0.0.0.0:1313"]
EXPOSE 1313
docker-compose.yml
version: "3.9"
services:
project:
build:
context: ./cicd/containers
target: dev
environment:
UMASK: 0002
volumes:
- ./src:/src # we assume that a hugo project has already been created
ports:
- "1313:1313" # the end goal should allow us to use http://localhost:1313 to access our hugo dev site
Fixing the issue with maximum potability across operating systems
There are many ways to fix this, but this article will specifically go over the most portable one. I wanted a fix that would not prevent someone on a different machine from being able to spin up my Dockerfile.
We are going to use the user namespace map configuration for the Docker daemon.
You’ll want to check what user and group you are currently running as.
kim@local:~/Personal/techlab/src $ whoami
kim
kim@local:~/Personal/techlab/src $ id -u && id -g
1000
1000
Then you’ll edit these two files so those id’s fit in the range inside the file.
kim@local:~/Personal/techlab/src $ cat /etc/subuid
kim:1000:65536
kim@local:~/Personal/techlab/src $ cat /etc/subgid
kim:1000:65536
Then tell docker that you want to use a specific user namespace for your containers.
kim@local:~/Personal/techlab/src $ cat /etc/docker/daemon.json
{
"userns-remap": "kim"
}
Validate the configuration and restart docker.
kim@local:~/Personal/techlab/src $ dockerd --validate --config-file /etc/docker/daemon.json
configuration OK
kim@local:~/Personal/techlab/src $ systemctl restart docker
Check that docker now has that namespace map that shows the uid and gid (for me, this was 1000.1000, respectively)
kim@local:~/Personal/techlab/src $ sudo ls -la /var/lib/docker/1000.1000
total 52
drwx--x--- 12 root kim 4096 Apr 25 13:21 .
drwx--x--- 15 root kim 4096 Apr 25 12:25 ..
drwx--x--x 5 root root 4096 Apr 25 12:27 buildkit
drwx--x--- 9 root kim 4096 Apr 25 13:23 containers
-rw------- 1 root root 36 Apr 25 12:25 engine-id
drwx------ 3 root root 4096 Apr 25 12:25 image
drwxr-x--- 3 root root 4096 Apr 25 12:25 network
drwx--x--- 26 root kim 4096 Apr 25 13:23 overlay2
drwx------ 4 root root 4096 Apr 25 12:25 plugins
drwx------ 2 root root 4096 Apr 25 13:21 runtimes
drwx------ 2 root root 4096 Apr 25 12:25 swarm
drwx------ 2 root root 4096 Apr 25 13:23 tmp
drwx-----x 2 root root 4096 Apr 25 13:21 volumes
And after all that, build your image and double check that the root user inside the machine now successfully can create files and folders in such a way that on your local machine, those files and folders show up as your present user.
kim@local:~/Personal/techlab/src $ touch from-local-machine
kim@local:~/Personal/techlab/src $ ls -la
total 48
drwxr-s--- 11 kim kim 4096 Apr 25 13:40 .
drwxrwxr-x 6 kim kim 4096 Apr 24 17:46 ..
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 archetypes
-rw-r--r-- 1 kim kim 101 Apr 24 17:52 config.toml
drwxr-xr-x 3 kim kim 4096 Apr 25 10:34 content
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 data
-rw-rw-r-- 1 kim kim 0 Apr 25 13:40 from-local-machine
-rw-r--r-- 1 kim kim 0 Apr 25 10:34 .hugo_build.lock
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 layouts
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 public
drwxr-xr-x 3 kim kim 4096 Apr 25 10:34 resources
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 static
drwxr-xr-x 3 kim kim 4096 Apr 24 17:46 themes
kim@local:~/Personal/techlab/cicd/containers $ docker build --target base -t techlab .
kim@local:~/Personal/techlab/cicd/containers $ docker run -v ~/Personal/techlab/src:/src -it techlab bash
bash-5.1# touch from-inside-container
bash-5.1# ls -la
total 48
drwxr-s--- 11 root root 4096 Apr 25 17:40 .
drwxr-xr-x 1 root root 4096 Apr 25 17:39 ..
-rw-r--r-- 1 root root 0 Apr 25 14:34 .hugo_build.lock
drwxr-xr-x 2 root root 4096 Apr 24 21:43 archetypes
-rw-r--r-- 1 root root 101 Apr 24 21:52 config.toml
drwxr-xr-x 3 root root 4096 Apr 25 14:34 content
drwxr-xr-x 2 root root 4096 Apr 24 21:43 data
-rw-r--r-- 1 root root 0 Apr 25 17:40 from-inside-container
-rw-rw-r-- 1 root root 0 Apr 25 17:24 from-local-machine
drwxr-xr-x 2 root root 4096 Apr 24 21:43 layouts
drwxr-xr-x 2 root root 4096 Apr 24 21:43 public
drwxr-xr-x 3 root root 4096 Apr 25 14:34 resources
drwxr-xr-x 2 root root 4096 Apr 24 21:43 static
drwxr-xr-x 3 root root 4096 Apr 24 21:46 themes
bash-5.1# exit
kim@local:~/Personal/techlab/cicd/containers $ cd ../..
kim@local:~/Personal/techlab $ cd src
kim@local:~/Personal/techlab/src $ ls -la
total 48
drwxr-s--- 11 kim kim 4096 Apr 25 13:40 .
drwxrwxr-x 6 kim kim 4096 Apr 24 17:46 ..
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 archetypes
-rw-r--r-- 1 kim kim 101 Apr 24 17:52 config.toml
drwxr-xr-x 3 kim kim 4096 Apr 25 10:34 content
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 data
-rw-r--r-- 1 kim kim 0 Apr 25 13:40 from-inside-container
-rw-rw-r-- 1 kim kim 0 Apr 25 13:40 from-local-machine
-rw-r--r-- 1 kim kim 0 Apr 25 10:34 .hugo_build.lock
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 layouts
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 public
drwxr-xr-x 3 kim kim 4096 Apr 25 10:34 resources
drwxr-xr-x 2 kim kim 4096 Apr 24 17:43 static
drwxr-xr-x 3 kim kim 4096 Apr 24 17:46 themes
The output above shows that both from-*
files are showing up on the local machine and inside the container correctly.
And you’d do this in lieu of adding a group and user inside the Dockerfile since that approach may backfire as soon as the Dockerfile is spun up on a different linux machine and that user’s uid and gid are different. In other words, this approach is more portable across different operating systems - it provides a way for multiple linux machines to play nice, as well as retaining Windows and OSX compatibility.