When trying to install openFOAM inside a docker container, How do I set the workDir to a location in my mac user directory - linux

I'm using the following script to install openFOAM in a docker container:
#!/bin/sh
#------------------------------------------------------------------------------
# ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration |
# \\ / A nd | Copyright (C) 2017-2020 OpenFOAM Foundation
# \\/ M anipulation |
#-------------------------------------------------------------------------------
# License
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
#
# Script
# openfoam8-macos
#
# Description
# Run script for an OpenFOAM 8 Docker image at:
# https://hub.docker.com/r/openfoam
#
#------------------------------------------------------------------------------
Script=${0##*/}
VER=8
usage () {
exec 1>&2
while [ "$#" -ge 1 ]; do echo "$1"; shift; done
cat <<USAGE
Usage: ${0##*/} [OPTIONS]
options:
-d | -dir host directory mounted (defaults to current directory)
-x | -xhost use custom X authority and give container host network
-h | -help help
-p | -paraview include ParaView in the Docker image
Launches the OpenFOAM ${VER} Docker image.
- Requires installation of docker-engine.
- Runs a "containerized" bash shell environment where the user can run OpenFOAM
and, optionally, ParaView (see below).
- The container mounts the user's file system so that case files are stored
permanently. The container mounts the current directory by default, but the
user can also specify a particular directory using the "-d" option.
- Mounting the user's HOME directory is disallowed.
- The '-xhost' option is useful when accessing the host via 'ssh -X'.
This option should only be used when strictly necessary, as it relies on the
option '--net=host' when launching the container in Docker, which will
give to the container full access to the Docker host network stack and
potentially the host's system services that rely on network communication,
making it potentially insecure.
ParaView:
Graphical applications from the Docker container require installation of the
Xquartz X server to display on the host machine. While applications such as
Gedit, Emacs and GnuPlot will run effectively using Xquartz, more intensive
OpenGL applications, in particular ParaView, can be prohibitively slow.
Therefore, the default Docker image does not contain ParaView and users can
instead install ParaView directly from the vendor and use the built-in reader
module for OpenFOAM: http://www.paraview.org/download
However, if the user wishes to include ParaView with the official OpenFOAM
reader module in their Docker container, they can do so with the "-p" option.
Example:
To store data in ${HOME}/OpenFOAM/${USER}-${VER}, the user can launch
${Script} either by:
cd ${HOME}/OpenFOAM/${USER}-${VER} && ${Script}
or
${Script} -d ${HOME}/OpenFOAM/${USER}-${VER}
Further Information:
http://openfoam.org/download/8-macos
Note:
The container user name appears as "openfoam" but it is just an alias.
USAGE
exit 1
}
DOCKER_IMAGE='openfoam/openfoam8-graphical-apps'
MOUNT_DIR=$(pwd)
CUSTOM_XAUTH=""
DOCKER_OPTIONS=""
while [ "$#" -gt 0 ]
do
case "$1" in
-d | -dir)
[ "$#" -ge 2 ] || usage "'$1' option requires an argument"
MOUNT_DIR=$2
shift 2
;;
-x | -xhost)
CUSTOM_XAUTH=yes
shift
;;
-h | -help)
usage
;;
-p | -paraview)
DOCKER_IMAGE='openfoam/openfoam8-paraview56'
shift
;;
*)
usage "Invalid option '$1'"
;;
esac
done
[ -d "$MOUNT_DIR" ] || usage "No directory exists: $MOUNT_DIR"
MOUNT_DIR=$(cd "$MOUNT_DIR" && pwd -P)
[ "$MOUNT_DIR" = "$(cd "$HOME" && pwd -P)" ] && \
usage "Mount directory cannot be the user's home directory" \
"Make a subdirectory and run from there, e.g." \
" mkdir -p ${HOME}/OpenFOAM/$(whoami)-${VER}" \
" ${Script} -d ${HOME}/OpenFOAM/$(whoami)-${VER}"
if [ -n "$CUSTOM_XAUTH" ]
then
XAUTH_PATH="${MOUNT_DIR}/.docker.xauth.$$"
touch "${XAUTH_PATH}"
# Generate a custom X-authority file that allows any hostname
xauth nlist "$DISPLAY" | sed -e 's/^..../ffff/' | \
xauth -f "$XAUTH_PATH" nmerge -
DOCKER_OPTIONS="-e XAUTHORITY=$XAUTH_PATH
-v $XAUTH_PATH:$XAUTH_PATH
--net=host"
fi
USER_ID=$(id -u 2> /dev/null)
[ -n "$USER_ID" ] || usage "Cannot determine current user ID"
GROUP_ID=$(id -g)
HOME_DIR='/home/openfoam'
echo "Launching $0"
echo "User: \"$(id -un)\" (ID $USER_ID, group ID $GROUP_ID)"
IFACES=$(ifconfig | grep ^en | cut -d: -f1)
[ "$IFACES" ] || \
usage "Cannot find a network interface for DISPLAY with ifconfig" \
"Please report an issue at http://bugs.openfoam.org" \
" providing the output of the command: ifconfig"
for I in $IFACES
do
IP=$(ifconfig "$I" | grep inet | awk '$1=="inet" {print $2}')
[ "$IP" ] && break
done
[ "$IP" ] || \
usage "Cannot find a network IP for DISPLAY with ifconfig" \
"Please report an issue at http://bugs.openfoam.org" \
" providing the output of the command: ifconfig"
xhost + "$IP"
docker run -it \
--rm \
-e DISPLAY=$IP:0 \
-u $USER_ID:$GROUP_ID \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $MOUNT_DIR:$HOME_DIR \
$DOCKER_OPTIONS \
$DOCKER_IMAGE
[ -n "$CUSTOM_XAUTH" -a -e "${XAUTH_PATH}" ] && rm "${XAUTH_PATH}"
This creates a user 'ofuser'. Why? Once I run the above script and then
xhost +local:of_v2006
docker start of_v2006
docker attach of_v2006
I end up in docker with:
[ofuser#3032b6018d82 woo]$ whoami
ofuser
I follow instructions to do a simulation and it creates files that are supposed to be available from the mac os itself, i.e. outside the docker container, but they seem to be in locations like /home/ofuser/...../ofuser/run/..., which don't seem to exist outside the container. Part of output:
[ofuser#5b3db0ac969b woo]$ mkdir $FOAM_RUN
mkdir: cannot create directory '/home/ofuser/OpenFOAM/ofuser-v2006/run': No such file or directory
So, how do I make the user 'foam' or 'woo' instead of ofuser. How did that user come to be? How an I set the workDir to be something like /Users/woo/Containers/foam? Do I have to have that /tmp/.X11-unix stuff in the setup? The display doesn't connect to my XQuartz display I've set up, etc.

I have the same setup running and yes it is possible. Took some time for me to get it running as well but I think I can clarify a few things.
First about the question regarding the directory. This can be set up in the dockerfile. The default dockerfile provided was not sufficient for me as well.
See the extract below:
"Mounts": [
...
{
"Type": "bind",
"Source": "/home/*****/OpenFOAM/run",
"Destination": "/home/openfoam",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
},
......
This binds the "Source" - on your machine - with the "Destination" directory inside the container. The crucial option here for me was the "Z" flag which was not present in the default docker file.
The X11 setup also depends on the rights set by the "Z" flag in the dockerfile and can / should be set there. I am not completely sure about your actual setup but this works fine for me.
"Mounts": [
...
{
"Type": "bind",
"Source": "/tmp/.X11-unix",
"Destination": "/tmp/.X11-unix",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
},
......
Regarding the new added user I still not get the reason behind it myself but I can tell you I had to use a second user and its working fine.
Normally you only use this user to log in into the container and nothing else. Under Linux execute these steps and verify the new user is in the docker usergroup
$ sudo groupadd docker
$ sudo usermod -aG docker <user>
$ sudo groups <user>

Related

Opening a display for Perk Tk from a Docker container [duplicate]

How can you run GUI applications in a Linux Docker container?
Are there any images that set up vncserver or something so that you can - for example - add an extra speedbump sandbox around say Firefox?
You can simply install a vncserver along with Firefox :)
I pushed an image, vnc/firefox, here: docker pull creack/firefox-vnc
The image has been made with this Dockerfile:
# Firefox over VNC
#
# VERSION 0.1
# DOCKER-VERSION 0.2
FROM ubuntu:12.04
# Make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
This will create a Docker container running VNC with the password 1234:
For Docker version 18 or newer:
docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker version 1.3 or newer:
docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker before version 1.3:
docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create
Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'
The Xauthority file can be written in a way so that the hostname does not matter.
We need to set the Authentication Family to 'FamilyWild'. I am not sure, if xauth has a proper command line for this, so here is an example that combines xauth and sed to do that. We need to change the first 16 bits of the nlist output. The value of FamilyWild is 65535 or 0xffff.
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes
I just found this blog entry and want to share it here with you because I think it is the best way to do it and it is so easy.
http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/
PROS:
+ no x server stuff in the docker container
+ no vnc client/server needed
+ no ssh with x forwarding
+ much smaller docker containers
CONS:
- using x on the host (not meant for secure-sandboxing)
in case the link will fail someday I have put the most important part here:
dockerfile:
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y firefox
# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
mkdir -p /home/developer && \
echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
echo "developer:x:${uid}:" >> /etc/group && \
echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
chmod 0440 /etc/sudoers.d/developer && \
chown ${uid}:${gid} -R /home/developer
USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox
build the image:
docker build -t firefox .
and the run command:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
of course you can also do this in the run command with sh -c "echo script-here"
HINT: for audio take a look at: https://stackoverflow.com/a/28985715/2835523
With docker data volumes it's very easy to expose xorg's unix domain socket inside the container.
For example, with a Dockerfile like this:
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
You could do the following:
$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes
This of course is essentially the same as X-forwarding. It grants the container full access to the xserver on the host, so it's only recommended if you trust what's inside.
Note: If you are concerned about security, a better solution would be to confine the app with mandatory- or role-based-access control. Docker achieves pretty good isolation, but it was designed with a different purpose in mind. Use AppArmor, SELinux, or GrSecurity, which were designed to address your concern.
OSX
Jürgen Weigert has the best answer that worked for me on Ubuntu, however on OSX, docker runs inside of VirtualBox and so the solution doesn't work without some more work.
I've got it working with these additional ingredients:
Xquartz (OSX no longer ships with X11 server)
socket forwarding with socat (brew install socat)
bash script to launch the container
I'd appreciate user comments to improve this answer for OSX, I'm not sure if socket forwarding for X is secure, but my intended use is for running the docker container locally only.
Also, the script is a bit fragile in that it's not easy to get the IP address of the machine since it's on our local wireless so it's always some random IP.
The BASH script I use to launch the container:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0
# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')
DISP_NUM=$(jot -r 1 100 200) # random display number between 100 and 200
PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother
socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run \
-it \
--rm \
--user=$USER \
--workdir="/Users/$USER" \
-v "/Users/$USER:/home/$USER:rw" \
-v $XSOCK:$XSOCK:rw \
-v $XAUTH:$XAUTH:rw \
-e DISPLAY=$IPADDR:$DISP_NUM \
-e XAUTHORITY=$XAUTH \
$CONTAINER \
$COMMAND
rm -f $XAUTH
kill %1 # kill the socat job launched above
I'm able to get xeyes and matplotlib working with this approach.
Windows 7+
It's a bit easier on Windows 7+ with MobaXterm:
Install MobaXterm for windows
Start MobaXterm
Configure X server: Settings -> X11 (tab) -> set X11 Remote Access to full
Use this BASH script to launch the container
run_docker.bash:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)
docker run \
-it \
--rm \
--user=$USER \
--workdir="/home/$USER" \
-v "/c/Users/$USER:/home/$USER:rw" \
-e DISPLAY \
$CONTAINER \
$COMMAND
You can also use subuser: https://github.com/timthelion/subuser
This allows you to package many gui apps in docker. Firefox and emacs have been tested so far. With firefox, webGL doesn't work though. Chromium doesn't work at all.
EDIT: Sound works!
EDIT2: In the time since I first posted this, subuser has progressed greatly. I now have a website up subuser.org, and a new security model for connecting to X11 via XPRA bridging.
Sharing host display :0, as stated in some other answers, has two drawbacks:
It breaks container isolation due to some X security leaks. For example, keylogging with xev or xinput is possible, and remote control of host applications with xdotool.
Applications can have rendering glitches and bad RAM access errors due to missing shared memory for X extension MIT-SHM. (Can also be fixed with isolation degrading option --ipc=host).
Below an example script to run a docker image in Xephyr that addresses this issues.
It avoids X security leaks as the docker applications run in a nested X server.
MIT-SHM is disabled to avoid RAM access failures.
Container security is improved with --cap-drop ALL --security-opt no-new-privileges. Also the container user is not root.
An X cookie is created to restrict access to Xephyr display.
The script expects some arguments, first a host window manager to run in Xephyr, second a docker image, optionally third
an image command to be executed.
To run a desktop environment in docker, use ":" instead of a host window manager.
Closing Xephyr window terminates docker container applications. Terminating the dockered applications closes Xephyr window.
Examples:
xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
xephyrdocker : x11docker/lxde
xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom
xephyrdocker script:
#! /bin/bash
#
# Xephyrdocker: Example script to run docker GUI applications in Xephyr.
#
# Usage:
# Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER host window manager for use with single GUI applications.
# To run without window manager from host, use ":"
# DOCKERIMAGE docker image containing GUI applications or a desktop
# IMAGECOMMAND command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"
# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"
# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
[ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber
# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd
# command to run docker
# --rm created container will be discarded.
# -e DISPLAY=$Newdisplay set environment variable to new display
# -e XAUTHORITY=/Xcookie set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro Share new X socket of Xephyr
# --user $Useruid:$Usergid Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro /etc/passwd file with user entry
# --group-add audio Allow access to /dev/snd if shared with '--device /dev/snd'
# --cap-drop ALL Security: disable needless capabilities
# --security-opt no-new-privileges Security: forbid new privileges
Dockercommand="docker run --rm \
-e DISPLAY=:$Newdisplaynumber \
-e XAUTHORITY=/Xcookie \
-v $Xclientcookie:/Xcookie:ro \
-v $Newxsocket:$Newxsocket:rw \
--user $Useruid:$Usergid \
-v $Etcpasswd:/etc/passwd:ro \
--group-add audio \
--env HOME=/tmp \
--cap-drop ALL \
--security-opt no-new-privileges \
$(command -v docker-init >/dev/null && echo --init) \
$Dockerimage"
echo "docker command:
$Dockercommand
"
# command to run Xorg or Xephyr
# /usr/bin/Xephyr an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber first argument has to be new display
# -auth $Xservercookie path to cookie file for X server. Must be different from cookie file of client, not sure why
# -extension MIT-SHM disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp disable tcp connections for security reasons
# -retro nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
-auth $Xservercookie \
-extension MIT-SHM \
-nolisten tcp \
-screen 1000x750x24 \
-retro"
echo "X server command:
$Xcommand
"
# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd
# create xinitrc
{ echo "#! /bin/bash"
echo "# set environment variables to new display and new cookie"
echo "export DISPLAY=:$Newdisplaynumber"
echo "export XAUTHORITY=$Xclientcookie"
echo "# same keyboard layout as on host"
echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"
echo "# create new XAUTHORITY cookie file"
echo ":> $Xclientcookie"
echo "xauth add :$Newdisplaynumber . $(mcookie)"
echo "# create prepared cookie with localhost identification disabled by ffff,"
echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')"
echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
echo "cp $Xclientcookie $Xservercookie"
echo "chmod 644 $Xclientcookie"
echo "# run window manager in Xephyr"
echo $Windowmanager' & Windowmanagerpid=$!'
echo "# show docker log"
echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'
echo "# run docker"
echo "$Dockercommand"
} > $Xinitrc
xinit $Xinitrc -- $Xcommand
rm -Rf $Cachefolder
This script is maintained at x11docker wiki.
A more advanced script is x11docker that also supports features like GPU acceleration, webcam and printer sharing and so on.
Here's a lightweight solution that avoids having to install any X server, vnc server or sshd daemon on the container. What it gains in simplicity it loses in security and isolation.
It assumes that you connect to the host machine using ssh with X11 forwarding.
In the sshd configuration of the host, add the line
X11UseLocalhost no
So that the forwarded X server port on the host is opened on all interfaces (not just lo) and in particular on the Docker virtual interface, docker0.
The container, when run, needs access to the .Xauthority file so that it can connect to the server. In order to do that, we define a read-only volume pointing to the home directory on the host (maybe not a wise idea!) and also set the XAUTHORITY variable accordingly.
docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority
That is not enough, we also have to pass the DISPLAY variable from the host, but substituting the hostname by the ip:
-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")
We can define an alias:
alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'
And test it like this:
dockerX11run centos xeyes
While the answer by Jürgen Weigert essentially covers this solution, it wasn't clear to me at first what was being described there. So I'll add my take on it, in case anyone else needs clarification.
First off, the relevant documentation is the X security manpage.
Numerous online sources suggest just mounting the X11 unix socket and the ~/.Xauthority file into the container. These solutions often work by luck, without really understanding why, e.g. the container user ends up with the same UID as the user, so there's no need for magic key authorization.
First off, the Xauthority file has mode 0600, so the container user won't be able to read it unless it has the same UID.
Even if you copy the file into the container, and change the ownership, there's still another problem. If you run xauth list on the host and container, with the same Xauthority file, you'll see different entries listed. This is because xauth filters the entries depending on where it's run.
The X client in the container (i.e. GUI app) will behave the same as xauth. In other words, it doesn't see the magic cookie for the X session running on the user's desktop. Instead, it sees the entries for all the "remote" X sessions you've opened previously (explained below).
So, what you need to do is add a new entry with the hostname of the container and the same hex key as the host cookie (i.e. the X session running on your desktop), e.g.:
containerhostname/unix:0 MIT-MAGIC-COOKIE-1 <shared hex key>
The catch is that the cookie has to be added with xauth add inside the container:
touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>
Otherwise, xauth tags it in a way that it's only seen outside the container.
The format for this command is:
xauth add hostname/$DISPLAY protocol hexkey
Where . represents the MIT-MAGIC-COOKIE-1 protocol.
Note: There's no need to copy or bind-mount .Xauthority into the container. Just create a blank file, as shown, and add the cookie.
Jürgen Weigert's answer gets around this by using the FamilyWild connection type to create a new authority file on the host and copy it into the container. Note that it first extracts the hex key for the current X session from ~/.Xauthority using xauth nlist.
So the essential steps are:
Extract the hex key of the cookie for the user's current X session.
Create a new Xauthority file in the container, with the container hostname and the shared hex key (or create a cookie with the FamilyWild connection type).
I admit that I don't understand very well how FamilyWild works, or how xauth or X clients filter entries from the Xauthority file depending where they're run. Additional information on this is welcome.
If you want to distribute your Docker app, you'll need a start script for running the container that gets the hex key for the user's X session, and imports it into the container in one of the two ways explained previously.
It also helps to understand the mechanics of the authorization process:
An X client (i.e. GUI application) running in the container looks in the Xauthority file for a cookie entry that matches the container's hostname and the value of $DISPLAY.
If a matching entry is found, the X client passes it with its authorization request to the X server, through the appropriate socket in the /tmp/.X11-unix directory mounted in the container.
Note: The X11 Unix socket still needs to be mounted in the container, or the container will have no route to the X server. Most distributions disable TCP access to the X server by default for security reasons.
For additional information, and to better grasp how the X client/server relationship works, it's also helpful to look at the example case of SSH X forwarding:
The SSH server running on a remote machine emulates its own X server.
It sets the value of $DISPLAY in the SSH session to point to its own X server.
It uses xauth to create a new cookie for the remote host, and adds it to the Xauthority files for both the local and remote users.
When GUI apps are started, they talk to SSH's emulated X server.
The SSH server forwards this data back to the SSH client on your local desktop.
The local SSH client sends the data to the X server session running on your desktop, as if the SSH client was actually an X client (i.e. GUI app).
The X server uses the received data to render the GUI on your desktop.
At the start of this exchange, the remote X client also sends an authorization request, using the cookie that was just created. The local X server compares it with its local copy.
This is not lightweight but is a nice solution that gives docker feature parity with full desktop virtualization. Both Xfce4 or IceWM for Ubuntu and CentOS work, and the noVNC option makes for an easy access through a browser.
https://github.com/ConSol/docker-headless-vnc-container
It runs noVNC as well as tigerVNC's vncserver. Then it calls startx for given Window Manager. In addition, libnss_wrapper.so is used to emulate password management for the users.
The solution given at http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ does seem to be an easy way of starting GUI applications from inside the containers ( I tried for firefox over ubuntu 14.04) but I found that a small additional change is required to the solution posted by the author.
Specifically, for running the container, the author has mentioned:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
But I found that (based on a particular comment on the same site) that two additional options
-v $HOME/.Xauthority:$HOME/.Xauthority
and
-net=host
need to be specified while running the container for firefox to work properly:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $HOME/.Xauthority:$HOME/.Xauthority \
-net=host \
firefox
I have created a docker image with the information on that page and these additional findings: https://hub.docker.com/r/amanral/ubuntu-firefox/
The other solutions should work, but here is a solution for docker-compose.
To fix that error, you need to pass $DISPLAY and .X11-unix to docker, as well as grant the user who started docker access to xhost.
Within docker-compose.yml file:
version: '2'
services:
node:
build: .
container_name: node
environment:
- DISPLAY
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
In terminal or script:
xhost +si:localuser:$USER
xhost +local:docker
export DISPLAY=$DISPLAY
docker-compose up
If you want to run a GUI application headless, then read here. What you have to do is to create a virtual monitor with xvfb or other similar software. This is very helpful if you want to run Selenium tests for example with browsers.
Something not mentioned anywhere is that some software actually themselves use sand-boxing with Linux containers. So for example Chrome will never run normally if you don't use the appropriate flag --privileged when running the container.
There is another solution by lord.garbage to run GUI apps in a container without using VNC, SSH and X11 forwarding. It is mentioned here too.
I'm late to the party, but for Mac users who don't want to go down the XQuartz path, here is a working example that builds a Fedora Image, with a Desktop Environment (xfce) using Xvfb and VNC. It's simple, and works:
https://github.com/ddual/docker_recipes#fedora-with-an-x-window-system
https://github.com/ddual/docker_recipes/tree/master/fedora_gui
On a Mac, you can just access it using the Screen Sharing (default) application, connecting to localhost:5901.
Dockerfile:
FROM fedora
USER root
# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd
# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false \
&& dnf install -y --setopt=deltarpm=false \
openssl.x86_64 \
java-1.8.0-openjdk.x86_64 \
xorg-x11-server-Xvfb \
x11vnc \
firefox \
#xfce-desktop-environment \
&& dnf clean all
# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer
# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh
# Expose VNC, SSH
EXPOSE 5901 22
# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV DISPLAY :1.0
RUN mkdir ~/.x11vnc
RUN x11vnc -storepasswd letmein ~/.x11vnc/passwd
WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]
start-vnc.sh
#!/bin/sh
Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &
bash
# while true; do sleep 1000; done
Check the linked readme for build and run commands if you want/need.
Based on Jürgen Weigert's answer, I have some improvement:
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes
The only difference is that it creates a directory $XAUTH_DIR which is used to place $XAUTH file and mount $XAUTH_DIR directory instead of $XAUTH file into docker container.
The benefit of this method is that you can write a command in /etc/rc.local which is to create a empty folder named $XAUTH_DIR in /tmp and change its mode to 777.
tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local
When system restart, before user login, docker will mount the $XAUTH_DIR directory automatically if container's restart policy is "always". After user login, you can write a command in ~/.profile which is to create $XAUTH file, then the container will automatically use this $XAUTH file.
tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile
Afterall, the container will automatically get the Xauthority file every time the system restart and user login.
For OpenGL rendering with the Nvidia driver, use the following image:
https://github.com/thewtex/docker-opengl-nvidia
For other OpenGL implementations, make sure the image has the same implementation as the host.
I managed to run a video stream from an USB camera using opencv in docker by following these steps:
Let docker access the X server
xhost +local:docker
Create the X11 Unix socket and the X authentication file
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
Add proper permissions
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine
export QT_GRAPHICSSYSTEM=native
Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise
export QT_X11_NO_MITSHM=1
Update the docker run command
docker run -it \
-e DISPLAY=$DISPLAY \
-e XAUTHORITY=$XAUTH \
-v $XSOCK:$XSOCK \
-v $XAUTH:$XAUTH \
--runtime=nvidia \
--device=/dev/video0:/dev/video0 \
nvcr.io/nvidia/pytorch:19.10-py3
Note: When you finish the the project, return the access controls at their default value - xhost -local:docker
More details: Using GUI's with Docker
Credit: Real-time and video processing object detection using Tensorflow, OpenCV and Docker
You can allow the Docker user (here: root) to access the X11 display:
XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image
xhost -SI:localuser:root
Yet another answer in case you already built the image:
invoke docker w/o sudo
(How to fix docker: Got permission denied issue)
share the same USER & home & passwd between host and container share
(tips: use user id instead of user name)
the dev folder for driver dependent libs to work well
plus X11 forward.
docker run --name=CONTAINER_NAME --network=host --privileged \
-v /dev:/dev \
-v `echo ~`:/home/${USER} \
-p 8080:80 \
--user=`id -u ${USER}` \
--env="DISPLAY" \
--volume="/etc/group:/etc/group:ro" \
--volume="/etc/passwd:/etc/passwd:ro" \
--volume="/etc/shadow:/etc/shadow:ro" \
--volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
-it REPO:TAG /bin/bash
you may ask, whats the point to use docker if so many things are the same? well, one reason I can think of is to overcome the package depency hell (https://en.wikipedia.org/wiki/Dependency_hell).
So this type of usage is more suitable for developer I think.
OSX (10.13.6, high sierra)
Similar to #Nick's answer, but his solution did not work for me.
First install socat by doing brew install socat, and install XQuartz (https://www.xquartz.org/)
Then followed these steps here (http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/) in the comments section:
1. in one mac terminal i started:
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
2. and in another mac terminal I ran:
docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
I was also able to launch CLion from my debian docker container too.
fcwu/docker-ubuntu-vnc-desktop (Ubuntu 18.04, 20.04)
https://github.com/fcwu/docker-ubuntu-vnc-desktop provides a convenient setup. That setup is not minimized. It would be good to minimize it. But I just don't have the time, and that one just works every time I try, so I tend to just use it. On the upside, because it is not minimized, it tends to test more complex programs you might actually to see that they are actually working through the infinitely many pitfalls of Docker. Also, since setups breaks on every guest/host update, a minimization would arguably only work for a limited period until you'd have to reminimize that project again.
To fire it up just run:
sudo docker run --name ubvnc -p 6080:80 -p 5900:5900 dorowu/ubuntu-desktop-lxde-vnc:focal
Then on host either:
visit: http://127.0.0.1:6080/#/ which runs a noVNC more limited JavaScript VNC client
run:
sudo apt-get install tigervnc-viewer
xtigervncviewer :5900
To go into fullscreen mode, hit F8 and click on menu entry, or just F8 followed by T: https://superuser.com/questions/285843/how-do-i-switch-in-out-of-fullscreen-mode-from-the-command-line-in-realvnc You might need to close and reopen the screen after that for the image to get larger.
I also tried vinagre, but it was much laggier when scrolling Firefox on YouTube.
Inside vinagre, you might want to go into full screen mode to be able to see the full desktop
To quit just kill docker on the terminal. And to restart the machine:
sudo docker start ubvnc
and then reconnect with VNC. Then to quit the machine:
sudo docker stop ubvnc
You have to wait a few seconds for the VNC server on the guest to start before you can connect.
Chromium inside the guest won't start from the menu. If you try to launch it from the command line it explains why:
Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
so just run it from the CLI with:
chromium-browser --no-sandbox
Firefox does not care however.
TODO: no audio. --device /dev/snd did not help:
How to play sound in a Docker container on Mac OS Yosemite
https://forums.docker.com/t/how-to-get-sound/36527
https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/49
EDIT: they added a section for it: https://github.com/fcwu/docker-ubuntu-vnc-desktop/tree/e4922ce92f945fc482994b7a0fd95ca5de7295b3#sound-preview-version-and-linux-only
See also:
How to open Ubuntu GUI inside a Docker image
Tested on:
Ubuntu 19.04 host, fcwu/docker-ubuntu-vnc-desktop, dorowu/ubuntu-desktop-lxde-vnc image id: 70516b87e92d.
Ubuntu 21.10 host, dorowu/ubuntu-desktop-lxde-vnc:focal (Ubuntu 20.04)
Docker with BRIDGE network.
for Ubuntu 16.04 with display manager lightdm:
cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf
[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp
you can use more private permissions
xhost +
docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name
There're many good answers here on how to connect GUI app in docker container to X server running on host machine, or how to run virtual X server and connect to container using VNC to access it.
There's however another solution (which is quite useful for say kiosks or home theatres) - you can run X server inside docker container with video output to the monitor connected to your host machine.
First let's create a docker volume to store the X11 socket:
docker volume create --name xsocket
Now we can create an image with X Server:
FROM ubuntu
RUN apt-get update && \
DEBIAN_FRONTEND='noninteractive' apt-get install -y xorg
CMD /usr/bin/X :0 -nolisten tcp vt1
Let us build it and start it and store the X11 socket in xsocket docker volume:
docker build . -t docker-x-server:latest
docker run --privileged -v xsocket:/tmp/.X11-unix -d docker-x-server:latest
Now we can run a GUI application in another docker container (yay!) and point it to our X server using xsocket volume:
docker run --rm -it -e DISPLAY=:0 -v xsocket:/tmp/.X11-unix:ro stefanscherer/xeyes
If you need input (like keyboard) install xserver-xorg-input-evdev package and add -v /run/udev/data:/run/udev/data since there's no udev in containers by default.
You can even get rid of --privileged flag by granting SYS_TTY_CONFIG capability and binding some devices into container:
docker run --name docker-x-server --device=/dev/input --device=/dev/console --device=/dev/dri --device=/dev/fb0 --device=/dev/tty --device=/dev/tty1 --device=/dev/vga_arbiter --device=/dev/snd --device=/dev/psaux --cap-add=SYS_TTY_CONFIG -v xsocket:/tmp/.X11-unix -d docker-x-server:latest

Can't use GUI with docker [duplicate]

How can you run GUI applications in a Linux Docker container?
Are there any images that set up vncserver or something so that you can - for example - add an extra speedbump sandbox around say Firefox?
You can simply install a vncserver along with Firefox :)
I pushed an image, vnc/firefox, here: docker pull creack/firefox-vnc
The image has been made with this Dockerfile:
# Firefox over VNC
#
# VERSION 0.1
# DOCKER-VERSION 0.2
FROM ubuntu:12.04
# Make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
This will create a Docker container running VNC with the password 1234:
For Docker version 18 or newer:
docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker version 1.3 or newer:
docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker before version 1.3:
docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create
Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'
The Xauthority file can be written in a way so that the hostname does not matter.
We need to set the Authentication Family to 'FamilyWild'. I am not sure, if xauth has a proper command line for this, so here is an example that combines xauth and sed to do that. We need to change the first 16 bits of the nlist output. The value of FamilyWild is 65535 or 0xffff.
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes
I just found this blog entry and want to share it here with you because I think it is the best way to do it and it is so easy.
http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/
PROS:
+ no x server stuff in the docker container
+ no vnc client/server needed
+ no ssh with x forwarding
+ much smaller docker containers
CONS:
- using x on the host (not meant for secure-sandboxing)
in case the link will fail someday I have put the most important part here:
dockerfile:
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y firefox
# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
mkdir -p /home/developer && \
echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
echo "developer:x:${uid}:" >> /etc/group && \
echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
chmod 0440 /etc/sudoers.d/developer && \
chown ${uid}:${gid} -R /home/developer
USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox
build the image:
docker build -t firefox .
and the run command:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
of course you can also do this in the run command with sh -c "echo script-here"
HINT: for audio take a look at: https://stackoverflow.com/a/28985715/2835523
With docker data volumes it's very easy to expose xorg's unix domain socket inside the container.
For example, with a Dockerfile like this:
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
You could do the following:
$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes
This of course is essentially the same as X-forwarding. It grants the container full access to the xserver on the host, so it's only recommended if you trust what's inside.
Note: If you are concerned about security, a better solution would be to confine the app with mandatory- or role-based-access control. Docker achieves pretty good isolation, but it was designed with a different purpose in mind. Use AppArmor, SELinux, or GrSecurity, which were designed to address your concern.
OSX
Jürgen Weigert has the best answer that worked for me on Ubuntu, however on OSX, docker runs inside of VirtualBox and so the solution doesn't work without some more work.
I've got it working with these additional ingredients:
Xquartz (OSX no longer ships with X11 server)
socket forwarding with socat (brew install socat)
bash script to launch the container
I'd appreciate user comments to improve this answer for OSX, I'm not sure if socket forwarding for X is secure, but my intended use is for running the docker container locally only.
Also, the script is a bit fragile in that it's not easy to get the IP address of the machine since it's on our local wireless so it's always some random IP.
The BASH script I use to launch the container:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0
# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')
DISP_NUM=$(jot -r 1 100 200) # random display number between 100 and 200
PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother
socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run \
-it \
--rm \
--user=$USER \
--workdir="/Users/$USER" \
-v "/Users/$USER:/home/$USER:rw" \
-v $XSOCK:$XSOCK:rw \
-v $XAUTH:$XAUTH:rw \
-e DISPLAY=$IPADDR:$DISP_NUM \
-e XAUTHORITY=$XAUTH \
$CONTAINER \
$COMMAND
rm -f $XAUTH
kill %1 # kill the socat job launched above
I'm able to get xeyes and matplotlib working with this approach.
Windows 7+
It's a bit easier on Windows 7+ with MobaXterm:
Install MobaXterm for windows
Start MobaXterm
Configure X server: Settings -> X11 (tab) -> set X11 Remote Access to full
Use this BASH script to launch the container
run_docker.bash:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)
docker run \
-it \
--rm \
--user=$USER \
--workdir="/home/$USER" \
-v "/c/Users/$USER:/home/$USER:rw" \
-e DISPLAY \
$CONTAINER \
$COMMAND
You can also use subuser: https://github.com/timthelion/subuser
This allows you to package many gui apps in docker. Firefox and emacs have been tested so far. With firefox, webGL doesn't work though. Chromium doesn't work at all.
EDIT: Sound works!
EDIT2: In the time since I first posted this, subuser has progressed greatly. I now have a website up subuser.org, and a new security model for connecting to X11 via XPRA bridging.
Sharing host display :0, as stated in some other answers, has two drawbacks:
It breaks container isolation due to some X security leaks. For example, keylogging with xev or xinput is possible, and remote control of host applications with xdotool.
Applications can have rendering glitches and bad RAM access errors due to missing shared memory for X extension MIT-SHM. (Can also be fixed with isolation degrading option --ipc=host).
Below an example script to run a docker image in Xephyr that addresses this issues.
It avoids X security leaks as the docker applications run in a nested X server.
MIT-SHM is disabled to avoid RAM access failures.
Container security is improved with --cap-drop ALL --security-opt no-new-privileges. Also the container user is not root.
An X cookie is created to restrict access to Xephyr display.
The script expects some arguments, first a host window manager to run in Xephyr, second a docker image, optionally third
an image command to be executed.
To run a desktop environment in docker, use ":" instead of a host window manager.
Closing Xephyr window terminates docker container applications. Terminating the dockered applications closes Xephyr window.
Examples:
xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
xephyrdocker : x11docker/lxde
xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom
xephyrdocker script:
#! /bin/bash
#
# Xephyrdocker: Example script to run docker GUI applications in Xephyr.
#
# Usage:
# Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER host window manager for use with single GUI applications.
# To run without window manager from host, use ":"
# DOCKERIMAGE docker image containing GUI applications or a desktop
# IMAGECOMMAND command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"
# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"
# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
[ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber
# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd
# command to run docker
# --rm created container will be discarded.
# -e DISPLAY=$Newdisplay set environment variable to new display
# -e XAUTHORITY=/Xcookie set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro Share new X socket of Xephyr
# --user $Useruid:$Usergid Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro /etc/passwd file with user entry
# --group-add audio Allow access to /dev/snd if shared with '--device /dev/snd'
# --cap-drop ALL Security: disable needless capabilities
# --security-opt no-new-privileges Security: forbid new privileges
Dockercommand="docker run --rm \
-e DISPLAY=:$Newdisplaynumber \
-e XAUTHORITY=/Xcookie \
-v $Xclientcookie:/Xcookie:ro \
-v $Newxsocket:$Newxsocket:rw \
--user $Useruid:$Usergid \
-v $Etcpasswd:/etc/passwd:ro \
--group-add audio \
--env HOME=/tmp \
--cap-drop ALL \
--security-opt no-new-privileges \
$(command -v docker-init >/dev/null && echo --init) \
$Dockerimage"
echo "docker command:
$Dockercommand
"
# command to run Xorg or Xephyr
# /usr/bin/Xephyr an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber first argument has to be new display
# -auth $Xservercookie path to cookie file for X server. Must be different from cookie file of client, not sure why
# -extension MIT-SHM disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp disable tcp connections for security reasons
# -retro nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
-auth $Xservercookie \
-extension MIT-SHM \
-nolisten tcp \
-screen 1000x750x24 \
-retro"
echo "X server command:
$Xcommand
"
# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd
# create xinitrc
{ echo "#! /bin/bash"
echo "# set environment variables to new display and new cookie"
echo "export DISPLAY=:$Newdisplaynumber"
echo "export XAUTHORITY=$Xclientcookie"
echo "# same keyboard layout as on host"
echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"
echo "# create new XAUTHORITY cookie file"
echo ":> $Xclientcookie"
echo "xauth add :$Newdisplaynumber . $(mcookie)"
echo "# create prepared cookie with localhost identification disabled by ffff,"
echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')"
echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
echo "cp $Xclientcookie $Xservercookie"
echo "chmod 644 $Xclientcookie"
echo "# run window manager in Xephyr"
echo $Windowmanager' & Windowmanagerpid=$!'
echo "# show docker log"
echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'
echo "# run docker"
echo "$Dockercommand"
} > $Xinitrc
xinit $Xinitrc -- $Xcommand
rm -Rf $Cachefolder
This script is maintained at x11docker wiki.
A more advanced script is x11docker that also supports features like GPU acceleration, webcam and printer sharing and so on.
Here's a lightweight solution that avoids having to install any X server, vnc server or sshd daemon on the container. What it gains in simplicity it loses in security and isolation.
It assumes that you connect to the host machine using ssh with X11 forwarding.
In the sshd configuration of the host, add the line
X11UseLocalhost no
So that the forwarded X server port on the host is opened on all interfaces (not just lo) and in particular on the Docker virtual interface, docker0.
The container, when run, needs access to the .Xauthority file so that it can connect to the server. In order to do that, we define a read-only volume pointing to the home directory on the host (maybe not a wise idea!) and also set the XAUTHORITY variable accordingly.
docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority
That is not enough, we also have to pass the DISPLAY variable from the host, but substituting the hostname by the ip:
-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")
We can define an alias:
alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'
And test it like this:
dockerX11run centos xeyes
While the answer by Jürgen Weigert essentially covers this solution, it wasn't clear to me at first what was being described there. So I'll add my take on it, in case anyone else needs clarification.
First off, the relevant documentation is the X security manpage.
Numerous online sources suggest just mounting the X11 unix socket and the ~/.Xauthority file into the container. These solutions often work by luck, without really understanding why, e.g. the container user ends up with the same UID as the user, so there's no need for magic key authorization.
First off, the Xauthority file has mode 0600, so the container user won't be able to read it unless it has the same UID.
Even if you copy the file into the container, and change the ownership, there's still another problem. If you run xauth list on the host and container, with the same Xauthority file, you'll see different entries listed. This is because xauth filters the entries depending on where it's run.
The X client in the container (i.e. GUI app) will behave the same as xauth. In other words, it doesn't see the magic cookie for the X session running on the user's desktop. Instead, it sees the entries for all the "remote" X sessions you've opened previously (explained below).
So, what you need to do is add a new entry with the hostname of the container and the same hex key as the host cookie (i.e. the X session running on your desktop), e.g.:
containerhostname/unix:0 MIT-MAGIC-COOKIE-1 <shared hex key>
The catch is that the cookie has to be added with xauth add inside the container:
touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>
Otherwise, xauth tags it in a way that it's only seen outside the container.
The format for this command is:
xauth add hostname/$DISPLAY protocol hexkey
Where . represents the MIT-MAGIC-COOKIE-1 protocol.
Note: There's no need to copy or bind-mount .Xauthority into the container. Just create a blank file, as shown, and add the cookie.
Jürgen Weigert's answer gets around this by using the FamilyWild connection type to create a new authority file on the host and copy it into the container. Note that it first extracts the hex key for the current X session from ~/.Xauthority using xauth nlist.
So the essential steps are:
Extract the hex key of the cookie for the user's current X session.
Create a new Xauthority file in the container, with the container hostname and the shared hex key (or create a cookie with the FamilyWild connection type).
I admit that I don't understand very well how FamilyWild works, or how xauth or X clients filter entries from the Xauthority file depending where they're run. Additional information on this is welcome.
If you want to distribute your Docker app, you'll need a start script for running the container that gets the hex key for the user's X session, and imports it into the container in one of the two ways explained previously.
It also helps to understand the mechanics of the authorization process:
An X client (i.e. GUI application) running in the container looks in the Xauthority file for a cookie entry that matches the container's hostname and the value of $DISPLAY.
If a matching entry is found, the X client passes it with its authorization request to the X server, through the appropriate socket in the /tmp/.X11-unix directory mounted in the container.
Note: The X11 Unix socket still needs to be mounted in the container, or the container will have no route to the X server. Most distributions disable TCP access to the X server by default for security reasons.
For additional information, and to better grasp how the X client/server relationship works, it's also helpful to look at the example case of SSH X forwarding:
The SSH server running on a remote machine emulates its own X server.
It sets the value of $DISPLAY in the SSH session to point to its own X server.
It uses xauth to create a new cookie for the remote host, and adds it to the Xauthority files for both the local and remote users.
When GUI apps are started, they talk to SSH's emulated X server.
The SSH server forwards this data back to the SSH client on your local desktop.
The local SSH client sends the data to the X server session running on your desktop, as if the SSH client was actually an X client (i.e. GUI app).
The X server uses the received data to render the GUI on your desktop.
At the start of this exchange, the remote X client also sends an authorization request, using the cookie that was just created. The local X server compares it with its local copy.
This is not lightweight but is a nice solution that gives docker feature parity with full desktop virtualization. Both Xfce4 or IceWM for Ubuntu and CentOS work, and the noVNC option makes for an easy access through a browser.
https://github.com/ConSol/docker-headless-vnc-container
It runs noVNC as well as tigerVNC's vncserver. Then it calls startx for given Window Manager. In addition, libnss_wrapper.so is used to emulate password management for the users.
The solution given at http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ does seem to be an easy way of starting GUI applications from inside the containers ( I tried for firefox over ubuntu 14.04) but I found that a small additional change is required to the solution posted by the author.
Specifically, for running the container, the author has mentioned:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
But I found that (based on a particular comment on the same site) that two additional options
-v $HOME/.Xauthority:$HOME/.Xauthority
and
-net=host
need to be specified while running the container for firefox to work properly:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $HOME/.Xauthority:$HOME/.Xauthority \
-net=host \
firefox
I have created a docker image with the information on that page and these additional findings: https://hub.docker.com/r/amanral/ubuntu-firefox/
The other solutions should work, but here is a solution for docker-compose.
To fix that error, you need to pass $DISPLAY and .X11-unix to docker, as well as grant the user who started docker access to xhost.
Within docker-compose.yml file:
version: '2'
services:
node:
build: .
container_name: node
environment:
- DISPLAY
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
In terminal or script:
xhost +si:localuser:$USER
xhost +local:docker
export DISPLAY=$DISPLAY
docker-compose up
If you want to run a GUI application headless, then read here. What you have to do is to create a virtual monitor with xvfb or other similar software. This is very helpful if you want to run Selenium tests for example with browsers.
Something not mentioned anywhere is that some software actually themselves use sand-boxing with Linux containers. So for example Chrome will never run normally if you don't use the appropriate flag --privileged when running the container.
There is another solution by lord.garbage to run GUI apps in a container without using VNC, SSH and X11 forwarding. It is mentioned here too.
I'm late to the party, but for Mac users who don't want to go down the XQuartz path, here is a working example that builds a Fedora Image, with a Desktop Environment (xfce) using Xvfb and VNC. It's simple, and works:
https://github.com/ddual/docker_recipes#fedora-with-an-x-window-system
https://github.com/ddual/docker_recipes/tree/master/fedora_gui
On a Mac, you can just access it using the Screen Sharing (default) application, connecting to localhost:5901.
Dockerfile:
FROM fedora
USER root
# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd
# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false \
&& dnf install -y --setopt=deltarpm=false \
openssl.x86_64 \
java-1.8.0-openjdk.x86_64 \
xorg-x11-server-Xvfb \
x11vnc \
firefox \
#xfce-desktop-environment \
&& dnf clean all
# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer
# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh
# Expose VNC, SSH
EXPOSE 5901 22
# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV DISPLAY :1.0
RUN mkdir ~/.x11vnc
RUN x11vnc -storepasswd letmein ~/.x11vnc/passwd
WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]
start-vnc.sh
#!/bin/sh
Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &
bash
# while true; do sleep 1000; done
Check the linked readme for build and run commands if you want/need.
Based on Jürgen Weigert's answer, I have some improvement:
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes
The only difference is that it creates a directory $XAUTH_DIR which is used to place $XAUTH file and mount $XAUTH_DIR directory instead of $XAUTH file into docker container.
The benefit of this method is that you can write a command in /etc/rc.local which is to create a empty folder named $XAUTH_DIR in /tmp and change its mode to 777.
tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local
When system restart, before user login, docker will mount the $XAUTH_DIR directory automatically if container's restart policy is "always". After user login, you can write a command in ~/.profile which is to create $XAUTH file, then the container will automatically use this $XAUTH file.
tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile
Afterall, the container will automatically get the Xauthority file every time the system restart and user login.
For OpenGL rendering with the Nvidia driver, use the following image:
https://github.com/thewtex/docker-opengl-nvidia
For other OpenGL implementations, make sure the image has the same implementation as the host.
I managed to run a video stream from an USB camera using opencv in docker by following these steps:
Let docker access the X server
xhost +local:docker
Create the X11 Unix socket and the X authentication file
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
Add proper permissions
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine
export QT_GRAPHICSSYSTEM=native
Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise
export QT_X11_NO_MITSHM=1
Update the docker run command
docker run -it \
-e DISPLAY=$DISPLAY \
-e XAUTHORITY=$XAUTH \
-v $XSOCK:$XSOCK \
-v $XAUTH:$XAUTH \
--runtime=nvidia \
--device=/dev/video0:/dev/video0 \
nvcr.io/nvidia/pytorch:19.10-py3
Note: When you finish the the project, return the access controls at their default value - xhost -local:docker
More details: Using GUI's with Docker
Credit: Real-time and video processing object detection using Tensorflow, OpenCV and Docker
You can allow the Docker user (here: root) to access the X11 display:
XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image
xhost -SI:localuser:root
Yet another answer in case you already built the image:
invoke docker w/o sudo
(How to fix docker: Got permission denied issue)
share the same USER & home & passwd between host and container share
(tips: use user id instead of user name)
the dev folder for driver dependent libs to work well
plus X11 forward.
docker run --name=CONTAINER_NAME --network=host --privileged \
-v /dev:/dev \
-v `echo ~`:/home/${USER} \
-p 8080:80 \
--user=`id -u ${USER}` \
--env="DISPLAY" \
--volume="/etc/group:/etc/group:ro" \
--volume="/etc/passwd:/etc/passwd:ro" \
--volume="/etc/shadow:/etc/shadow:ro" \
--volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
-it REPO:TAG /bin/bash
you may ask, whats the point to use docker if so many things are the same? well, one reason I can think of is to overcome the package depency hell (https://en.wikipedia.org/wiki/Dependency_hell).
So this type of usage is more suitable for developer I think.
OSX (10.13.6, high sierra)
Similar to #Nick's answer, but his solution did not work for me.
First install socat by doing brew install socat, and install XQuartz (https://www.xquartz.org/)
Then followed these steps here (http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/) in the comments section:
1. in one mac terminal i started:
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
2. and in another mac terminal I ran:
docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
I was also able to launch CLion from my debian docker container too.
fcwu/docker-ubuntu-vnc-desktop (Ubuntu 18.04, 20.04)
https://github.com/fcwu/docker-ubuntu-vnc-desktop provides a convenient setup. That setup is not minimized. It would be good to minimize it. But I just don't have the time, and that one just works every time I try, so I tend to just use it. On the upside, because it is not minimized, it tends to test more complex programs you might actually to see that they are actually working through the infinitely many pitfalls of Docker. Also, since setups breaks on every guest/host update, a minimization would arguably only work for a limited period until you'd have to reminimize that project again.
To fire it up just run:
sudo docker run --name ubvnc -p 6080:80 -p 5900:5900 dorowu/ubuntu-desktop-lxde-vnc:focal
Then on host either:
visit: http://127.0.0.1:6080/#/ which runs a noVNC more limited JavaScript VNC client
run:
sudo apt-get install tigervnc-viewer
xtigervncviewer :5900
To go into fullscreen mode, hit F8 and click on menu entry, or just F8 followed by T: https://superuser.com/questions/285843/how-do-i-switch-in-out-of-fullscreen-mode-from-the-command-line-in-realvnc You might need to close and reopen the screen after that for the image to get larger.
I also tried vinagre, but it was much laggier when scrolling Firefox on YouTube.
Inside vinagre, you might want to go into full screen mode to be able to see the full desktop
To quit just kill docker on the terminal. And to restart the machine:
sudo docker start ubvnc
and then reconnect with VNC. Then to quit the machine:
sudo docker stop ubvnc
You have to wait a few seconds for the VNC server on the guest to start before you can connect.
Chromium inside the guest won't start from the menu. If you try to launch it from the command line it explains why:
Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
so just run it from the CLI with:
chromium-browser --no-sandbox
Firefox does not care however.
TODO: no audio. --device /dev/snd did not help:
How to play sound in a Docker container on Mac OS Yosemite
https://forums.docker.com/t/how-to-get-sound/36527
https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/49
EDIT: they added a section for it: https://github.com/fcwu/docker-ubuntu-vnc-desktop/tree/e4922ce92f945fc482994b7a0fd95ca5de7295b3#sound-preview-version-and-linux-only
See also:
How to open Ubuntu GUI inside a Docker image
Tested on:
Ubuntu 19.04 host, fcwu/docker-ubuntu-vnc-desktop, dorowu/ubuntu-desktop-lxde-vnc image id: 70516b87e92d.
Ubuntu 21.10 host, dorowu/ubuntu-desktop-lxde-vnc:focal (Ubuntu 20.04)
Docker with BRIDGE network.
for Ubuntu 16.04 with display manager lightdm:
cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf
[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp
you can use more private permissions
xhost +
docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name
There're many good answers here on how to connect GUI app in docker container to X server running on host machine, or how to run virtual X server and connect to container using VNC to access it.
There's however another solution (which is quite useful for say kiosks or home theatres) - you can run X server inside docker container with video output to the monitor connected to your host machine.
First let's create a docker volume to store the X11 socket:
docker volume create --name xsocket
Now we can create an image with X Server:
FROM ubuntu
RUN apt-get update && \
DEBIAN_FRONTEND='noninteractive' apt-get install -y xorg
CMD /usr/bin/X :0 -nolisten tcp vt1
Let us build it and start it and store the X11 socket in xsocket docker volume:
docker build . -t docker-x-server:latest
docker run --privileged -v xsocket:/tmp/.X11-unix -d docker-x-server:latest
Now we can run a GUI application in another docker container (yay!) and point it to our X server using xsocket volume:
docker run --rm -it -e DISPLAY=:0 -v xsocket:/tmp/.X11-unix:ro stefanscherer/xeyes
If you need input (like keyboard) install xserver-xorg-input-evdev package and add -v /run/udev/data:/run/udev/data since there's no udev in containers by default.
You can even get rid of --privileged flag by granting SYS_TTY_CONFIG capability and binding some devices into container:
docker run --name docker-x-server --device=/dev/input --device=/dev/console --device=/dev/dri --device=/dev/fb0 --device=/dev/tty --device=/dev/tty1 --device=/dev/vga_arbiter --device=/dev/snd --device=/dev/psaux --cap-add=SYS_TTY_CONFIG -v xsocket:/tmp/.X11-unix -d docker-x-server:latest

How to generate files in a docker container for having the same owner as the host's user

I use docker containers to get some development tasks done in my git repositories.
The containers I use are built specifically for each project, that way each contributor only need docker CLI to get things done.
The bad side is the container run as root, thus generated files are owned by root once the container stops, and that's bad..
I can't change the owner in the container before exiting because there isn't the required user in the container /etc/passwd file.
I would create a user in the container before compiling the docker image but then the container won't be universal (for all my dev team).
What is the best practice(s) about that ?
You can add your user to the image, launch the container as root, and use an entrypoint to adjust the uid/gid inside the container to match that of the volume mounts from your devs. Then switch to your container user to run your app.
To implement this, I've used an entrypoint shell script that includes the following lines:
OLD_UID=`getent passwd "${opt_u}" | cut -f3 -d:`
NEW_UID=`ls -nd "$1" | cut -f3 -d' '`
if [ "$OLD_UID" != "$NEW_UID" ]; then
usermod -u "$NEW_UID" "$opt_u"
if [ -n "$opt_r" ]; then
find / -mount -uid "$OLD_UID" -exec chown "$opt_u" {} \;
fi
fi
OLD_GID=`getent group "${opt_g}" | cut -f3 -d:`
NEW_GID=`ls -nd "$1" | cut -f4 -d' '`
if [ "$OLD_GID" != "$NEW_GID" ]; then
groupmod -g "$NEW_GID" "$opt_g"
if [ -n "$opt_r" ]; then
find / -mount -gid "$OLD_GID" -exec chgrp "$opt_g" {} \;
fi
fi
In that script, $1 is the volume mount path, opt_u is the username inside the container, opt_g is the group name, and opt_r is a flag to recursively adjust the filesystem inside the container.
At the very end of my entrypoint, I kick off:
exec gosu "${RUN_AS}" "$#"
Which switches to the user $RUN_AS and exec's the command passed to the entrypoint as that user, and as pid 1. Gosu is used to avoid su running as pid 1.
I've posted a similar example of this before with Jenkins as the app inside the container to match the GID of the docker socket. You can find the repo with all the steps here: https://github.com/bmitch3020/jenkins-docker
This has the significant upside of making the image portable to any developer system, and they can run it with the same command. Whatever the permissions are on the filesystem they map into the container from outside become the uid/gid of the user running commands inside the container. There's no need to lookup the local uid/gid on the developer system for your docker run command, or build your image on each developer workstation with unique uid/gid entries. The only downside of this implementation is that docker exec will enter the container as root, since you need to run your entrypoint as root for access to modify users/groups and filesystem ownership.
A solution is to create a new dockerfile in the repository which is based on the former image (pre-installed with devtools but with no user created but root).
In a such dockerfile, write something like the following:
FROM <ABSTRACT_FORMER_IMAGE>
ARG username=developer
RUN useradd -r -u 1001 -g $username $username
USER $username
Then, before starting to work, each developer in the team should use the dockerfile to build its own base image for new project+developer specific containers, passing its username.
$ docker build --build-arg username=$USER Dockerfile
Some better practices out there ?
I would recommend you to read this link to have the complete understanding of docker privileges - and for you, the solution would be to run docker using --user command.

Set docker image username at container creation time?

I have an OpenSuse 42.3 docker image that I've configured to run a code. The image has a single user(other than root) called "myuser" that I create during the initial Image generation via the Dockerfile. I have three script files that generate a container from the image based on what operating system a user is on.
Question: Can the username "myuser" in the container be set to the username of the user that executes the container generation script?
My goal is to let a user pop into the container interactively and be able to run the code from within the container. The code is just a single binary that executes and has some IO, so I want the user's directory to be accessible from within the container so that they can navigate to a folder on their machine and run the code to generate output in their filesystem.
Below is what I have constructed so far. I tried setting the USER environment variable during the linux script's call to docker run, but that didn't change the user from "myuser" to say "bob" (the username on the host machine that started the container). The mounting of the directories seems to work fine. I'm not sure if it is even possible to achieve my goal.
Linux Container script:
username="$USER"
userID="$(id -u)"
groupID="$(id -g)"
home="${1:-$HOME}"
imageName="myImage:ImageTag"
containerName="version1Image"
docker run -it -d --name ${containerName} -u $userID:$groupID \
-e USER=${username} --workdir="/home/myuser" \
--volume="${home}:/home/myuser" ${imageName} /bin/bash \
Mac Container script:
username="$USER"
userID="$(id -u)"
groupID="$(id -g)"
home="${1:-$HOME}"
imageName="myImage:ImageTag"
containerName="version1Image"
docker run -it -d --name ${containerName} \
--workdir="/home/myuser" \
--v="${home}:/home/myuser" ${imageName} /bin/bash \
Windows Container script:
ECHO OFF
SET imageName="myImage:ImageTag"
SET containerName="version1Image"
docker run -it -d --name %containerName% --workdir="/home/myuser" -v="%USERPROFILE%:/home/myuser" %imageName% /bin/bash
echo "Container %containerName% was created."
echo "Run the ./startWindowsLociStream script to launch container"
The below code has been checked into https://github.com/bmitch3020/run-as-user.
I would handle this in an entrypoint.sh that checks the ownership of /home/myuser and updates the uid/gid of the user inside your container. It can look something like:
#!/bin/sh
set -x
# get uid/gid
USER_UID=`ls -nd /home/myuser | cut -f3 -d' '`
USER_GID=`ls -nd /home/myuser | cut -f4 -d' '`
# get the current uid/gid of myuser
CUR_UID=`getent passwd myuser | cut -f3 -d: || true`
CUR_GID=`getent group myuser | cut -f3 -d: || true`
# if they don't match, adjust
if [ ! -z "$USER_GID" -a "$USER_GID" != "$CUR_GID" ]; then
groupmod -g ${USER_GID} myuser
fi
if [ ! -z "$USER_UID" -a "$USER_UID" != "$CUR_UID" ]; then
usermod -u ${USER_UID} myuser
# fix other permissions
find / -uid ${CUR_UID} -mount -exec chown ${USER_UID}.${USER_GID} {} \;
fi
# drop access to myuser and run cmd
exec gosu myuser "$#"
And here's some lines from a relevant Dockerfile:
FROM debian:9
ARG GOSU_VERSION=1.10
# run as root, let the entrypoint drop back to myuser
USER root
# install prereq debian packages
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
apt-transport-https \
ca-certificates \
curl \
vim \
wget \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install gosu
RUN dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
&& chmod 755 /usr/local/bin/gosu \
&& gosu nobody true
RUN useradd -d /home/myuser -m myuser
WORKDIR /home/myuser
# entrypoint is used to update uid/gid and then run the users command
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD /bin/sh
Then to run it, you just need to mount /home/myuser as a volume and it will adjust permissions in the entrypoint. e.g.:
$ docker build -t run-as-user .
$ docker run -it --rm -v $(pwd):/home/myuser run-as-user /bin/bash
Inside that container you can run id and ls -l to see that you have access to /home/myuser files.
Usernames are not important. What is important are the uid and gid values.
User myuser inside your container will have a uid of 1000 (first non-root user id). Thus when you start your container and look at the container process from the host machine, you will see that the container is owned by whatever user having a uid of 1000 on the host machine.
You can override this by specifying the user once you run your container using:
docker run --user 1001 ...
Therefore if you want the user inside the container, to be able to access files on the host machine owned by a user having a uid of 1005 say, just run the container using --user 1005.
To better understand how users map between the container and host take a look at this wonderful article. https://medium.com/#mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf
First of all (https://docs.docker.com/engine/reference/builder/#arg):
Warning: It is not recommended to use build-time variables for passing
secrets like github keys, user credentials etc. Build-time variable
values are visible to any user of the image with the docker history
command.
But if you still need to do this, read https://docs.docker.com/engine/reference/builder/#arg:
A Dockerfile may include one or more ARG instructions. For example,
the following is a valid Dockerfile:
FROM busybox
ARG user1
ARG buildno
...
and https://docs.docker.com/engine/reference/builder/#user:
The USER instruction sets the user name (or UID) and optionally the
user group (or GID) to use when running the image and for any RUN, CMD
and ENTRYPOINT instructions that follow it in the Dockerfile.
USER <user>[:<group>] or
USER <UID>[:<GID>]

Can you run GUI applications in a Linux Docker container?

How can you run GUI applications in a Linux Docker container?
Are there any images that set up vncserver or something so that you can - for example - add an extra speedbump sandbox around say Firefox?
You can simply install a vncserver along with Firefox :)
I pushed an image, vnc/firefox, here: docker pull creack/firefox-vnc
The image has been made with this Dockerfile:
# Firefox over VNC
#
# VERSION 0.1
# DOCKER-VERSION 0.2
FROM ubuntu:12.04
# Make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
This will create a Docker container running VNC with the password 1234:
For Docker version 18 or newer:
docker run -p 5900:5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker version 1.3 or newer:
docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create
For Docker before version 1.3:
docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create
Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'
The Xauthority file can be written in a way so that the hostname does not matter.
We need to set the Authentication Family to 'FamilyWild'. I am not sure, if xauth has a proper command line for this, so here is an example that combines xauth and sed to do that. We need to change the first 16 bits of the nlist output. The value of FamilyWild is 65535 or 0xffff.
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes
I just found this blog entry and want to share it here with you because I think it is the best way to do it and it is so easy.
http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/
PROS:
+ no x server stuff in the docker container
+ no vnc client/server needed
+ no ssh with x forwarding
+ much smaller docker containers
CONS:
- using x on the host (not meant for secure-sandboxing)
in case the link will fail someday I have put the most important part here:
dockerfile:
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y firefox
# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
mkdir -p /home/developer && \
echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
echo "developer:x:${uid}:" >> /etc/group && \
echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
chmod 0440 /etc/sudoers.d/developer && \
chown ${uid}:${gid} -R /home/developer
USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox
build the image:
docker build -t firefox .
and the run command:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
of course you can also do this in the run command with sh -c "echo script-here"
HINT: for audio take a look at: https://stackoverflow.com/a/28985715/2835523
With docker data volumes it's very easy to expose xorg's unix domain socket inside the container.
For example, with a Dockerfile like this:
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
You could do the following:
$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes
This of course is essentially the same as X-forwarding. It grants the container full access to the xserver on the host, so it's only recommended if you trust what's inside.
Note: If you are concerned about security, a better solution would be to confine the app with mandatory- or role-based-access control. Docker achieves pretty good isolation, but it was designed with a different purpose in mind. Use AppArmor, SELinux, or GrSecurity, which were designed to address your concern.
OSX
Jürgen Weigert has the best answer that worked for me on Ubuntu, however on OSX, docker runs inside of VirtualBox and so the solution doesn't work without some more work.
I've got it working with these additional ingredients:
Xquartz (OSX no longer ships with X11 server)
socket forwarding with socat (brew install socat)
bash script to launch the container
I'd appreciate user comments to improve this answer for OSX, I'm not sure if socket forwarding for X is secure, but my intended use is for running the docker container locally only.
Also, the script is a bit fragile in that it's not easy to get the IP address of the machine since it's on our local wireless so it's always some random IP.
The BASH script I use to launch the container:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0
# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')
DISP_NUM=$(jot -r 1 100 200) # random display number between 100 and 200
PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother
socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run \
-it \
--rm \
--user=$USER \
--workdir="/Users/$USER" \
-v "/Users/$USER:/home/$USER:rw" \
-v $XSOCK:$XSOCK:rw \
-v $XAUTH:$XAUTH:rw \
-e DISPLAY=$IPADDR:$DISP_NUM \
-e XAUTHORITY=$XAUTH \
$CONTAINER \
$COMMAND
rm -f $XAUTH
kill %1 # kill the socat job launched above
I'm able to get xeyes and matplotlib working with this approach.
Windows 7+
It's a bit easier on Windows 7+ with MobaXterm:
Install MobaXterm for windows
Start MobaXterm
Configure X server: Settings -> X11 (tab) -> set X11 Remote Access to full
Use this BASH script to launch the container
run_docker.bash:
#!/usr/bin/env bash
CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)
docker run \
-it \
--rm \
--user=$USER \
--workdir="/home/$USER" \
-v "/c/Users/$USER:/home/$USER:rw" \
-e DISPLAY \
$CONTAINER \
$COMMAND
You can also use subuser: https://github.com/timthelion/subuser
This allows you to package many gui apps in docker. Firefox and emacs have been tested so far. With firefox, webGL doesn't work though. Chromium doesn't work at all.
EDIT: Sound works!
EDIT2: In the time since I first posted this, subuser has progressed greatly. I now have a website up subuser.org, and a new security model for connecting to X11 via XPRA bridging.
Sharing host display :0, as stated in some other answers, has two drawbacks:
It breaks container isolation due to some X security leaks. For example, keylogging with xev or xinput is possible, and remote control of host applications with xdotool.
Applications can have rendering glitches and bad RAM access errors due to missing shared memory for X extension MIT-SHM. (Can also be fixed with isolation degrading option --ipc=host).
Below an example script to run a docker image in Xephyr that addresses this issues.
It avoids X security leaks as the docker applications run in a nested X server.
MIT-SHM is disabled to avoid RAM access failures.
Container security is improved with --cap-drop ALL --security-opt no-new-privileges. Also the container user is not root.
An X cookie is created to restrict access to Xephyr display.
The script expects some arguments, first a host window manager to run in Xephyr, second a docker image, optionally third
an image command to be executed.
To run a desktop environment in docker, use ":" instead of a host window manager.
Closing Xephyr window terminates docker container applications. Terminating the dockered applications closes Xephyr window.
Examples:
xephyrdocker "openbox --sm-disable" x11docker/lxde pcmanfm
xephyrdocker : x11docker/lxde
xephyrdocker xfwm4 --device /dev/snd jess/nes /games/zelda.rom
xephyrdocker script:
#! /bin/bash
#
# Xephyrdocker: Example script to run docker GUI applications in Xephyr.
#
# Usage:
# Xephyrdocker WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER host window manager for use with single GUI applications.
# To run without window manager from host, use ":"
# DOCKERIMAGE docker image containing GUI applications or a desktop
# IMAGECOMMAND command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"
# Container user
Useruid=$(id -u)
Usergid=$(id -g)
Username="$(id -un)"
[ "$Useruid" = "0" ] && Useruid=1000 && Usergid=1000 && Username="user$Useruid"
# Find free display number
for ((Newdisplaynumber=1 ; Newdisplaynumber <= 100 ; Newdisplaynumber++)) ; do
[ -e /tmp/.X11-unix/X$Newdisplaynumber ] || break
done
Newxsocket=/tmp/.X11-unix/X$Newdisplaynumber
# cache folder and files
Cachefolder=/tmp/Xephyrdocker_X$Newdisplaynumber
[ -e "$Cachefolder" ] && rm -R "$Cachefolder"
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xinitrc=$Cachefolder/xinitrc
Etcpasswd=$Cachefolder/passwd
# command to run docker
# --rm created container will be discarded.
# -e DISPLAY=$Newdisplay set environment variable to new display
# -e XAUTHORITY=/Xcookie set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro Share new X socket of Xephyr
# --user $Useruid:$Usergid Security: avoid root in container
# -v $Etcpasswd:/etc/passwd:ro /etc/passwd file with user entry
# --group-add audio Allow access to /dev/snd if shared with '--device /dev/snd'
# --cap-drop ALL Security: disable needless capabilities
# --security-opt no-new-privileges Security: forbid new privileges
Dockercommand="docker run --rm \
-e DISPLAY=:$Newdisplaynumber \
-e XAUTHORITY=/Xcookie \
-v $Xclientcookie:/Xcookie:ro \
-v $Newxsocket:$Newxsocket:rw \
--user $Useruid:$Usergid \
-v $Etcpasswd:/etc/passwd:ro \
--group-add audio \
--env HOME=/tmp \
--cap-drop ALL \
--security-opt no-new-privileges \
$(command -v docker-init >/dev/null && echo --init) \
$Dockerimage"
echo "docker command:
$Dockercommand
"
# command to run Xorg or Xephyr
# /usr/bin/Xephyr an absolute path to X server executable must be given for xinit
# :$Newdisplaynumber first argument has to be new display
# -auth $Xservercookie path to cookie file for X server. Must be different from cookie file of client, not sure why
# -extension MIT-SHM disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -nolisten tcp disable tcp connections for security reasons
# -retro nice retro look
Xcommand="/usr/bin/Xephyr :$Newdisplaynumber \
-auth $Xservercookie \
-extension MIT-SHM \
-nolisten tcp \
-screen 1000x750x24 \
-retro"
echo "X server command:
$Xcommand
"
# create /etc/passwd with unprivileged user
echo "root:x:0:0:root:/root:/bin/sh" >$Etcpasswd
echo "$Username:x:$Useruid:$Usergid:$Username,,,:/tmp:/bin/sh" >> $Etcpasswd
# create xinitrc
{ echo "#! /bin/bash"
echo "# set environment variables to new display and new cookie"
echo "export DISPLAY=:$Newdisplaynumber"
echo "export XAUTHORITY=$Xclientcookie"
echo "# same keyboard layout as on host"
echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - :$Newdisplaynumber"
echo "# create new XAUTHORITY cookie file"
echo ":> $Xclientcookie"
echo "xauth add :$Newdisplaynumber . $(mcookie)"
echo "# create prepared cookie with localhost identification disabled by ffff,"
echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
echo 'Cookie=$(xauth nlist '":$Newdisplaynumber | sed -e 's/^..../ffff/')"
echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'
echo "cp $Xclientcookie $Xservercookie"
echo "chmod 644 $Xclientcookie"
echo "# run window manager in Xephyr"
echo $Windowmanager' & Windowmanagerpid=$!'
echo "# show docker log"
echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'
echo "# run docker"
echo "$Dockercommand"
} > $Xinitrc
xinit $Xinitrc -- $Xcommand
rm -Rf $Cachefolder
This script is maintained at x11docker wiki.
A more advanced script is x11docker that also supports features like GPU acceleration, webcam and printer sharing and so on.
Here's a lightweight solution that avoids having to install any X server, vnc server or sshd daemon on the container. What it gains in simplicity it loses in security and isolation.
It assumes that you connect to the host machine using ssh with X11 forwarding.
In the sshd configuration of the host, add the line
X11UseLocalhost no
So that the forwarded X server port on the host is opened on all interfaces (not just lo) and in particular on the Docker virtual interface, docker0.
The container, when run, needs access to the .Xauthority file so that it can connect to the server. In order to do that, we define a read-only volume pointing to the home directory on the host (maybe not a wise idea!) and also set the XAUTHORITY variable accordingly.
docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority
That is not enough, we also have to pass the DISPLAY variable from the host, but substituting the hostname by the ip:
-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")
We can define an alias:
alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'
And test it like this:
dockerX11run centos xeyes
While the answer by Jürgen Weigert essentially covers this solution, it wasn't clear to me at first what was being described there. So I'll add my take on it, in case anyone else needs clarification.
First off, the relevant documentation is the X security manpage.
Numerous online sources suggest just mounting the X11 unix socket and the ~/.Xauthority file into the container. These solutions often work by luck, without really understanding why, e.g. the container user ends up with the same UID as the user, so there's no need for magic key authorization.
First off, the Xauthority file has mode 0600, so the container user won't be able to read it unless it has the same UID.
Even if you copy the file into the container, and change the ownership, there's still another problem. If you run xauth list on the host and container, with the same Xauthority file, you'll see different entries listed. This is because xauth filters the entries depending on where it's run.
The X client in the container (i.e. GUI app) will behave the same as xauth. In other words, it doesn't see the magic cookie for the X session running on the user's desktop. Instead, it sees the entries for all the "remote" X sessions you've opened previously (explained below).
So, what you need to do is add a new entry with the hostname of the container and the same hex key as the host cookie (i.e. the X session running on your desktop), e.g.:
containerhostname/unix:0 MIT-MAGIC-COOKIE-1 <shared hex key>
The catch is that the cookie has to be added with xauth add inside the container:
touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>
Otherwise, xauth tags it in a way that it's only seen outside the container.
The format for this command is:
xauth add hostname/$DISPLAY protocol hexkey
Where . represents the MIT-MAGIC-COOKIE-1 protocol.
Note: There's no need to copy or bind-mount .Xauthority into the container. Just create a blank file, as shown, and add the cookie.
Jürgen Weigert's answer gets around this by using the FamilyWild connection type to create a new authority file on the host and copy it into the container. Note that it first extracts the hex key for the current X session from ~/.Xauthority using xauth nlist.
So the essential steps are:
Extract the hex key of the cookie for the user's current X session.
Create a new Xauthority file in the container, with the container hostname and the shared hex key (or create a cookie with the FamilyWild connection type).
I admit that I don't understand very well how FamilyWild works, or how xauth or X clients filter entries from the Xauthority file depending where they're run. Additional information on this is welcome.
If you want to distribute your Docker app, you'll need a start script for running the container that gets the hex key for the user's X session, and imports it into the container in one of the two ways explained previously.
It also helps to understand the mechanics of the authorization process:
An X client (i.e. GUI application) running in the container looks in the Xauthority file for a cookie entry that matches the container's hostname and the value of $DISPLAY.
If a matching entry is found, the X client passes it with its authorization request to the X server, through the appropriate socket in the /tmp/.X11-unix directory mounted in the container.
Note: The X11 Unix socket still needs to be mounted in the container, or the container will have no route to the X server. Most distributions disable TCP access to the X server by default for security reasons.
For additional information, and to better grasp how the X client/server relationship works, it's also helpful to look at the example case of SSH X forwarding:
The SSH server running on a remote machine emulates its own X server.
It sets the value of $DISPLAY in the SSH session to point to its own X server.
It uses xauth to create a new cookie for the remote host, and adds it to the Xauthority files for both the local and remote users.
When GUI apps are started, they talk to SSH's emulated X server.
The SSH server forwards this data back to the SSH client on your local desktop.
The local SSH client sends the data to the X server session running on your desktop, as if the SSH client was actually an X client (i.e. GUI app).
The X server uses the received data to render the GUI on your desktop.
At the start of this exchange, the remote X client also sends an authorization request, using the cookie that was just created. The local X server compares it with its local copy.
This is not lightweight but is a nice solution that gives docker feature parity with full desktop virtualization. Both Xfce4 or IceWM for Ubuntu and CentOS work, and the noVNC option makes for an easy access through a browser.
https://github.com/ConSol/docker-headless-vnc-container
It runs noVNC as well as tigerVNC's vncserver. Then it calls startx for given Window Manager. In addition, libnss_wrapper.so is used to emulate password management for the users.
The solution given at http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/ does seem to be an easy way of starting GUI applications from inside the containers ( I tried for firefox over ubuntu 14.04) but I found that a small additional change is required to the solution posted by the author.
Specifically, for running the container, the author has mentioned:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
But I found that (based on a particular comment on the same site) that two additional options
-v $HOME/.Xauthority:$HOME/.Xauthority
and
-net=host
need to be specified while running the container for firefox to work properly:
docker run -ti --rm \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $HOME/.Xauthority:$HOME/.Xauthority \
-net=host \
firefox
I have created a docker image with the information on that page and these additional findings: https://hub.docker.com/r/amanral/ubuntu-firefox/
The other solutions should work, but here is a solution for docker-compose.
To fix that error, you need to pass $DISPLAY and .X11-unix to docker, as well as grant the user who started docker access to xhost.
Within docker-compose.yml file:
version: '2'
services:
node:
build: .
container_name: node
environment:
- DISPLAY
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
In terminal or script:
xhost +si:localuser:$USER
xhost +local:docker
export DISPLAY=$DISPLAY
docker-compose up
If you want to run a GUI application headless, then read here. What you have to do is to create a virtual monitor with xvfb or other similar software. This is very helpful if you want to run Selenium tests for example with browsers.
Something not mentioned anywhere is that some software actually themselves use sand-boxing with Linux containers. So for example Chrome will never run normally if you don't use the appropriate flag --privileged when running the container.
There is another solution by lord.garbage to run GUI apps in a container without using VNC, SSH and X11 forwarding. It is mentioned here too.
I'm late to the party, but for Mac users who don't want to go down the XQuartz path, here is a working example that builds a Fedora Image, with a Desktop Environment (xfce) using Xvfb and VNC. It's simple, and works:
https://github.com/ddual/docker_recipes#fedora-with-an-x-window-system
https://github.com/ddual/docker_recipes/tree/master/fedora_gui
On a Mac, you can just access it using the Screen Sharing (default) application, connecting to localhost:5901.
Dockerfile:
FROM fedora
USER root
# Set root password, so I know it for the future
RUN echo "root:password123" | chpasswd
# Install Java, Open SSL, etc.
RUN dnf update -y --setopt=deltarpm=false \
&& dnf install -y --setopt=deltarpm=false \
openssl.x86_64 \
java-1.8.0-openjdk.x86_64 \
xorg-x11-server-Xvfb \
x11vnc \
firefox \
#xfce-desktop-environment \
&& dnf clean all
# Create developer user (password: password123, uid: 11111)
RUN useradd -u 11111 -g users -d /home/developer -s /bin/bash -p $(echo password123 | openssl passwd -1 -stdin) developer
# Copy startup script over to the developer home
COPY start-vnc.sh /home/developer/start-vnc.sh
RUN chmod 700 /home/developer/start-vnc.sh
RUN chown developer.users /home/developer/start-vnc.sh
# Expose VNC, SSH
EXPOSE 5901 22
# Set up VNC Password and DisplayEnvVar to point to Display1Screen0
USER developer
ENV DISPLAY :1.0
RUN mkdir ~/.x11vnc
RUN x11vnc -storepasswd letmein ~/.x11vnc/passwd
WORKDIR /home/developer
CMD ["/home/developer/start-vnc.sh"]
start-vnc.sh
#!/bin/sh
Xvfb :1 -screen 0 1024x768x24 &
sleep 5
x11vnc -noxdamage -many -display :1 -rfbport 5901 -rfbauth ~/.x11vnc/passwd -bg
sleep 2
xfce4-session &
bash
# while true; do sleep 1000; done
Check the linked readme for build and run commands if you want/need.
Based on Jürgen Weigert's answer, I have some improvement:
docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH_DIR=/tmp/.docker.xauth
XAUTH=$XAUTH_DIR/.xauth
mkdir -p $XAUTH_DIR && touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH_DIR:$XAUTH_DIR -e XAUTHORITY=$XAUTH xeyes
The only difference is that it creates a directory $XAUTH_DIR which is used to place $XAUTH file and mount $XAUTH_DIR directory instead of $XAUTH file into docker container.
The benefit of this method is that you can write a command in /etc/rc.local which is to create a empty folder named $XAUTH_DIR in /tmp and change its mode to 777.
tr '\n' '\000' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|\x00XAUTH_DIR=.*\x00\x00|\x00|' /etc/rc.local >/dev/null
tr '\000' '\n' < /etc/rc.local | sudo tee /etc/rc.local >/dev/null
sudo sed -i 's|^exit 0.*$|XAUTH_DIR=/tmp/.docker.xauth; rm -rf $XAUTH_DIR; install -m 777 -d $XAUTH_DIR\n\nexit 0|' /etc/rc.local
When system restart, before user login, docker will mount the $XAUTH_DIR directory automatically if container's restart policy is "always". After user login, you can write a command in ~/.profile which is to create $XAUTH file, then the container will automatically use this $XAUTH file.
tr '\n' '\000' < ~/.profile | sudo tee ~/.profile >/dev/null
sed -i 's|\x00XAUTH_DIR=.*-\x00|\x00|' ~/.profile
tr '\000' '\n' < ~/.profile | sudo tee ~/.profile >/dev/null
echo "XAUTH_DIR=/tmp/.docker.xauth; XAUTH=\$XAUTH_DIR/.xauth; touch \$XAUTH; xauth nlist \$DISPLAY | sed -e 's/^..../ffff/' | xauth -f \$XAUTH nmerge -" >> ~/.profile
Afterall, the container will automatically get the Xauthority file every time the system restart and user login.
For OpenGL rendering with the Nvidia driver, use the following image:
https://github.com/thewtex/docker-opengl-nvidia
For other OpenGL implementations, make sure the image has the same implementation as the host.
I managed to run a video stream from an USB camera using opencv in docker by following these steps:
Let docker access the X server
xhost +local:docker
Create the X11 Unix socket and the X authentication file
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
Add proper permissions
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine
export QT_GRAPHICSSYSTEM=native
Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise
export QT_X11_NO_MITSHM=1
Update the docker run command
docker run -it \
-e DISPLAY=$DISPLAY \
-e XAUTHORITY=$XAUTH \
-v $XSOCK:$XSOCK \
-v $XAUTH:$XAUTH \
--runtime=nvidia \
--device=/dev/video0:/dev/video0 \
nvcr.io/nvidia/pytorch:19.10-py3
Note: When you finish the the project, return the access controls at their default value - xhost -local:docker
More details: Using GUI's with Docker
Credit: Real-time and video processing object detection using Tensorflow, OpenCV and Docker
You can allow the Docker user (here: root) to access the X11 display:
XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image
xhost -SI:localuser:root
Yet another answer in case you already built the image:
invoke docker w/o sudo
(How to fix docker: Got permission denied issue)
share the same USER & home & passwd between host and container share
(tips: use user id instead of user name)
the dev folder for driver dependent libs to work well
plus X11 forward.
docker run --name=CONTAINER_NAME --network=host --privileged \
-v /dev:/dev \
-v `echo ~`:/home/${USER} \
-p 8080:80 \
--user=`id -u ${USER}` \
--env="DISPLAY" \
--volume="/etc/group:/etc/group:ro" \
--volume="/etc/passwd:/etc/passwd:ro" \
--volume="/etc/shadow:/etc/shadow:ro" \
--volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
-it REPO:TAG /bin/bash
you may ask, whats the point to use docker if so many things are the same? well, one reason I can think of is to overcome the package depency hell (https://en.wikipedia.org/wiki/Dependency_hell).
So this type of usage is more suitable for developer I think.
OSX (10.13.6, high sierra)
Similar to #Nick's answer, but his solution did not work for me.
First install socat by doing brew install socat, and install XQuartz (https://www.xquartz.org/)
Then followed these steps here (http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/) in the comments section:
1. in one mac terminal i started:
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
2. and in another mac terminal I ran:
docker run -ti --rm \
-e DISPLAY=$(ipconfig getifaddr en0):0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
firefox
I was also able to launch CLion from my debian docker container too.
fcwu/docker-ubuntu-vnc-desktop (Ubuntu 18.04, 20.04)
https://github.com/fcwu/docker-ubuntu-vnc-desktop provides a convenient setup. That setup is not minimized. It would be good to minimize it. But I just don't have the time, and that one just works every time I try, so I tend to just use it. On the upside, because it is not minimized, it tends to test more complex programs you might actually to see that they are actually working through the infinitely many pitfalls of Docker. Also, since setups breaks on every guest/host update, a minimization would arguably only work for a limited period until you'd have to reminimize that project again.
To fire it up just run:
sudo docker run --name ubvnc -p 6080:80 -p 5900:5900 dorowu/ubuntu-desktop-lxde-vnc:focal
Then on host either:
visit: http://127.0.0.1:6080/#/ which runs a noVNC more limited JavaScript VNC client
run:
sudo apt-get install tigervnc-viewer
xtigervncviewer :5900
To go into fullscreen mode, hit F8 and click on menu entry, or just F8 followed by T: https://superuser.com/questions/285843/how-do-i-switch-in-out-of-fullscreen-mode-from-the-command-line-in-realvnc You might need to close and reopen the screen after that for the image to get larger.
I also tried vinagre, but it was much laggier when scrolling Firefox on YouTube.
Inside vinagre, you might want to go into full screen mode to be able to see the full desktop
To quit just kill docker on the terminal. And to restart the machine:
sudo docker start ubvnc
and then reconnect with VNC. Then to quit the machine:
sudo docker stop ubvnc
You have to wait a few seconds for the VNC server on the guest to start before you can connect.
Chromium inside the guest won't start from the menu. If you try to launch it from the command line it explains why:
Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
so just run it from the CLI with:
chromium-browser --no-sandbox
Firefox does not care however.
TODO: no audio. --device /dev/snd did not help:
How to play sound in a Docker container on Mac OS Yosemite
https://forums.docker.com/t/how-to-get-sound/36527
https://github.com/fcwu/docker-ubuntu-vnc-desktop/issues/49
EDIT: they added a section for it: https://github.com/fcwu/docker-ubuntu-vnc-desktop/tree/e4922ce92f945fc482994b7a0fd95ca5de7295b3#sound-preview-version-and-linux-only
See also:
How to open Ubuntu GUI inside a Docker image
Tested on:
Ubuntu 19.04 host, fcwu/docker-ubuntu-vnc-desktop, dorowu/ubuntu-desktop-lxde-vnc image id: 70516b87e92d.
Ubuntu 21.10 host, dorowu/ubuntu-desktop-lxde-vnc:focal (Ubuntu 20.04)
Docker with BRIDGE network.
for Ubuntu 16.04 with display manager lightdm:
cd /etc/lightdm/lightdm.conf.d
sudo nano user.conf
[Seat:*]
xserver-allow-tcp=true
xserver-command=X -listen tcp
you can use more private permissions
xhost +
docker run --volume="$HOME/.Xauthority:/root/.Xauthority:rw" --env="DISPLAY=$HOST_IP_IN_BRIDGE_NETWORK:0" --net=bridge $container_name
There're many good answers here on how to connect GUI app in docker container to X server running on host machine, or how to run virtual X server and connect to container using VNC to access it.
There's however another solution (which is quite useful for say kiosks or home theatres) - you can run X server inside docker container with video output to the monitor connected to your host machine.
First let's create a docker volume to store the X11 socket:
docker volume create --name xsocket
Now we can create an image with X Server:
FROM ubuntu
RUN apt-get update && \
DEBIAN_FRONTEND='noninteractive' apt-get install -y xorg
CMD /usr/bin/X :0 -nolisten tcp vt1
Let us build it and start it and store the X11 socket in xsocket docker volume:
docker build . -t docker-x-server:latest
docker run --privileged -v xsocket:/tmp/.X11-unix -d docker-x-server:latest
Now we can run a GUI application in another docker container (yay!) and point it to our X server using xsocket volume:
docker run --rm -it -e DISPLAY=:0 -v xsocket:/tmp/.X11-unix:ro stefanscherer/xeyes
If you need input (like keyboard) install xserver-xorg-input-evdev package and add -v /run/udev/data:/run/udev/data since there's no udev in containers by default.
You can even get rid of --privileged flag by granting SYS_TTY_CONFIG capability and binding some devices into container:
docker run --name docker-x-server --device=/dev/input --device=/dev/console --device=/dev/dri --device=/dev/fb0 --device=/dev/tty --device=/dev/tty1 --device=/dev/vga_arbiter --device=/dev/snd --device=/dev/psaux --cap-add=SYS_TTY_CONFIG -v xsocket:/tmp/.X11-unix -d docker-x-server:latest

Resources