Docker run: Mirror Host User
Docker makes it easy to share host network and filesystem, but it doesn't make it easy to share the host's user ID and group ID. This is very useful when using a container locally via docker run, when it needs to write files to a volume shared with the host. In that case it is useful for the files to have the same user ID and group ID as the user on host.
Currently the way I do this is as follows:
  • In the Dockerfile, I create a user runuser and group rungroup via useradd and groupadd commands, and I set final USER so container defaults to that user:
1
RUN groupadd rungroup \
2
&& useradd -ms /bin/bash -g rungroup runuser
3
4
USER runuser
5
6
...more setup...
7
8
# ensure container runs as runuser
9
USER runuser
Copied!
  • In a small script, run the docker container in detached mode (--detach)
1
docker run \
2
...
3
--env USER \
4
--name $CTNR_NAME \
5
--rm \
6
--detach \
7
--tty \
8
IMAGE_NAME
Copied!
  • Then docker exec usermod and groupmod to match the host's user ID and group ID:
1
docker exec -it -u root $CTNR_NAME groupmod -g "$GROUP_ID" rungroup
2
docker exec -it -u root $CTNR_NAME usermod -u $UID runuser
Copied!
  • A final line in the script executes the desired command in the container, such as a shell. The command will run as runuser:rungroup but with the ID that match that of the host:
1
# runs shell as last USER in Dockerfile
2
docker exec -it $CTNR_NAME id
Copied!
  • If you need a root shell in container, say to install more apps temporarily (because if you restart the container those apps will be gone -- which is a good thing, ensure clean slate for any new container), change user:
1
docker exec -it -u root $CTNR_NAME /bin/bash
Copied!
This is quite tricky and required a fair bit of time to figure out.
I've seen another solution of mounting /etc/passwd and /etc/group but this exposes way more info in the container than necessary (which is just one line of each file). So for me this is not a solution.
In any case the above enables multiple simultaneous shells, each one running as either root or runuser, in latter case the UID and GROUP_ID will match that of host user who started the container, and all shells can be exited without terminating the container.
One caveat is that there will still be files owned by the original user ID that got created in the Dockerfile. Eg if the useradd command in Dockerfile created user runuser with ID 2000, and then in Dockerfile other commands are run as that user that creates files, the files will have ownership by user ID 2000. The docker exec that is run later changes the runuser ID to something else, but this does not change the ownership of any files already created. Therefore, you may need to chown those files via an additional docker exec. Eg
1
docker exec -it -u root $CTNR_NAME \
2
chown runuser /var/run/docker.sock
Copied!
In the small script I additionally have a check to determine if the container is already running, in that case it skips the docker run, and also to easy choose between runuser and root:
1
#!/usr/bin/env bash
2
3
run_as_root=false
4
if [[ ${1:-} == '--su' ]]; then
5
shift
6
echo "Will run as root"
7
run_as_root=true
8
fi
9
10
CTNR_NAME=something
11
12
if [[ -z $( docker ps -qf name=$CTNR_NAME ) ]]; then
13
echo "Starting new container $CTNR_NAME"
14
docker run \
15
...
16
17
# match host user ID and group ID
18
docker exec -it -u root $CTNR_NAME groupmod -g "$GROUP_ID" rungroup
19
docker exec -it -u root $CTNR_NAME usermod -u $UID runuser
20
21
# some files need to be re-owned by runuser
22
docker exec -it -u root $CTNR_NAME chown runuser /var/run/docker.sock
23
24
else
25
echo "Container $CTNR_NAME is already running"
26
fi
27
28
echo "Shelling into $CTNR_NAME container"
29
if [[ $run_as_root == true ]]; then
30
docker exec -it --user root $CTNR_NAME /bin/bash
31
else
32
docker exec -it --user runuser $CTNR_NAME /bin/bash
33
fi
Copied!
Copy link