Docker Community Forums

Share and learn in the Docker community.

Update DISPLAY env for already running docker containers for X11 forwarding over SSH

I have a GUI that is launched inside my docker container that I want to view over SSH.

Here is how I create my container:
SOCK=/tmp/.X11-unix docker run --name motiv_cont --gpus all -it --privileged --mount type=bind,source="/home/micra/.ros",target=/root/.ros --volume "$HOME/.Xauthority:/root/.Xauthority:ro" --network=host -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK hub/image

This works great for me. But if another user tries to use this container, it doesn’t work since the the DISPLAY and doesn’t get updated in the container.

Is there a way to get around this?

There is a nice and semi-easy way of getting graphical output from a Docker container without having to run an sshd daemon inside of the container. Docker can provide bare metal performance when running a single process which in this case is supposed to be R. Running an sshd daemon will, marginal as it may be, introduce additional overhead. This is not made better by running the sshd daemon as a child process of the supervisor daemon. Both can be dispensed with when one makes good use of bind mounts. After building the image from which the container is supposed to be run we start an interactive container and bind mount the /tmp/.X11-unix folder into it. I will state the complete command and explain in detail what it does:

docker run -i -t --rm \

-i sets up an interactive session; -t allocates a pseudo tty; --rm makes this container ephemeral
-e DISPLAY=$DISPLAY \

sets the host display to the local machines display (which will usually be :0)
-u docker \

-u specify the process should be run by a user (here docker) and not by root. This step is important (v.i.)!
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \

-v bind mounts the X11 socket residing in /tmp/.X11-unix on your local machine into /tmp/.X11-unix in the container and :ro makes the socket read only.
–name=“rdev” ubuntu-r R

–name="" specify the name of the container (here rdev); the image you want to run the container from (here ubuntu-r); the process you want to run in the container (here R). (The last step of specifying a process is only necessary if you have not set a default CMD or ENTRYPOINT for your image.)
After issuing this command you should be looking at the beautiful R start output. If you were to try demo(graphics) to see if graphical output is already working you would note that it is not. That is because of the Xsecurity extension preventing you from accessing the socket. You could now type xhost + on your local machine and try demo(graphics) in your container again. You should now have graphical output. This method however, is strongly discouraged as you allow access to your xsocket to any remote host you’re currently connected to. As long as you’re only interacting with single-user systems this might be somehow justifiable but as soon as there are multiple users involved this will be absolutely unsafe! Hence, you should use a less dangerous method. A good way is to use the server interpreted

xhost +si:localuser:username
which can be used to specify a single local user (see man xhost). This means username should be the name of the user which runs the X11 server on your local machine and which runs the docker container. This is also the reason why it is important that you specify a user when running your container. Last but not least there is always the more complex solution of using xauth and .Xauthority files to grant access to the X11 socket (see man xauth). This however will also involve a little more knowledge how X works.

The positive effect this can have can be seen in the number of processes that need to be run in order to achieve what is wanted.

(1) with supervisor and sshd running in the container:

UID PID PPID C STIME TTY TIME CMD
root 4564 718 1 18:16 ? 00:00:00 /usr/bin/python /usr/bin/supervisord
root 4576 4564 0 18:16 ? 00:00:00 /usr/sbin/sshd
when logged in via ssh and running R:

UID PID PPID C STIME TTY TIME CMD
root 4564 718 0 18:16 ? 00:00:00 /usr/bin/python /usr/bin/supervisord
root 4576 4564 0 18:16 ? 00:00:00 /usr/sbin/sshd
root 4674 4576 0 18:17 ? 00:00:00 sshd: docker [priv]
chb 4725 4674 0 18:18 ? 00:00:00 sshd: docker@pts/0
chb 4728 4725 1 18:18 pts/0 00:00:00 -bash
(2) with bind mount method:

UID PID PPID C STIME TTY TIME CMD
chb 4356 718 0 18:12