How to easily run graphics-accelerated GUI apps in LXD containers on your Ubuntu desktop
UPDATE June 2020: See newer post at https://blog.simos.info/running-x11-software-in-lxd-containers/ for simplified instructions. They require a recent LXD (version 4.0 or newer), and snap packages work.
Note: This post is about LXD containers. These are system containers, which means they are similar to Docker but behave somewhat like virtual machines. When you start a LXD (lex-dee) container, you are starting a new system on your computer, with its own IP address and all. You can get LXD as a snap package. Go https://docs.snapcraft.io/core/install to install snap support, then sudo snap install lxd. See also Getting Started with LXD.
In that older post, we saw how to manually setup a LXD container in order to run GUI apps from there, and have them appear on our X11 desktop.
In this post, we are going to see how to easily set up our LXD installation in order to be able to launch on demand containers that we can run GUI apps in them. First, we will see the instructions and explanation on how to use them. Then, we explain these instructions in detail. And finally we go through some common troubleshooting issues.
Prerequisites
The following have been tested with
the host runs either Ubuntu 18.04 or Ubuntu 16.04
the containers run either Ubuntu 18.04 or Ubuntu 16.04 or Ubuntu 14.04 or Ubuntu 12.04
LXD version 3.0 or newer (probably works fine with LXD 2.0.8+ as well)
works fine with either the LXD deb package or the LXD snap package
To verify whether you run the deb package or the snap package, run the command which lxd
. If the output is /usr/bin/lxd
, then you have the deb package of LXD. Otherwise, if it is /snap/bin/lxd
, you have the snap package of LXD.
These instructions should work with other distributions as well. Read further below on the detailed explanation of the instructions in order to adapt to your favorite distribution.
In the following, we see the two steps to set up our system so that we can then create GUI containers on demand. Step 1 is only required if you run the deb package of LXD. In subsequent sections, we see an explanation of the instructions so that you can easily port of other Linux distributions. At the end, have a look at the Troubleshooting section to see commons issues and how to solve them.
Step 1 Mapping the user ID of the host to the container
This step is only required if you run the deb package of LXD. If instead you have the snap package of LXD, skip to Step 2.
Run on the host (only once) the following command (source): (Note: if you do not use the bash shell, then $UID is the user-id of the current user. You can replace $UID with $(id -u) in that case.)
The command appends a new entry in both the /etc/subuid and /etc/subgid subordinate UID/GID files. It allows the LXD service (that runs as root) to remap our user’s ID ($UID, from the host) as requested.
Step 2 Creating the gui LXD profile
You will be creating a LXD profile with settings relevant to launching GUI applications. All configuration that you did manually at the old How to run graphics-accelerated GUI apps in LXD containers on your Ubuntu desktop post, are now included in a single LXD profile.
Download the file lxdguiprofile.txt and save it locally.
Then, create an empty LXD profile with the name gui. Finally, put the downloaded profile configuration into the newly created gui profile.
Verify that the profile has been created.
You can view the contents of the profile gui by running lxc profile show gui. A discussion on the profile contents is found two sections below.
Launching gui containers in LXD
Let’s launch some GUI containers in LXD. The gui LXD profile only has instructions related to running GUI applications. Due to this, you need to specify first another profile with information on the disk and the networking. The default LXD profile is suitable for this. You may use a bridge profile or macvlan profile instead.
You have launched two containers, with Ubuntu 18.04 and Ubuntu 16.04 respectively. You have specified two LXD profiles, default and gui. This means that the new container gets configuration from default, then from gui.
Next, make sure that the containers are up and running. In the LXD profile there are instructions to install additional packages automatically for us. That takes time. Here is how we check. We get a shell as the non-root account ubuntu in the container, and tail the end of the cloud-init log file. It says that it has 0 failures, it took (on this case) about 22 seconds to complete, and the startup was successful.
Subsequently, run glxgears to test graphics hardware acceleration. You may also try glxinfo.
Finally, test the audio and whether Pulseaudio works.
Audio works fine as well. If there was an error, the pactl info command would have showed it here.
Now, you can install deb packages of GUI programs in these containers, such as Firefox, Chromium browser, Chrome, Steam and so on. Installing snap packages inside the containers and having them appear on your desktop is not supported yet. That would require LXD 3.2 and a few modifications to the profile (not covered in this post).
In the following subsections, we see some useful examples.
Running a separate instance of a program
We are creating a GUI container in order to run Firefox from it. It will be a separate and independent instance of Firefox compared to our desktop browser.
Running old programs in old versions of Ubuntu
redet is a Tcl/Tk program that does not run easily on Ubuntu 18.04 because it needs some extra packaging effort for newer versions of Ubuntu. One option would have been to install Ubuntu 12.04 in VirtualBox. Here is the LXD alternative: We launch an Ubuntu 12.04 container, then install redet and finally run it. It took around 40 seconds from launch to GUI.
Running Windows programs with Wine
When you need to run a particular Windows program with Wine, you would prefer not to install all the dependencies on your desktop Ubuntu but rather have them confined into a container. Here is how to do this. We launch a new GUI container called wine, then install Wine (package wine-stable, Wine version 3.0) according to the official instructions, and finally install a Windows program. We can reuse the same container to install more Windows programs.
Then, you can set up the environment to run winetricks in order to easily install Windows programs.
We have installed Internet Explorer through winetricks and here it is,
A closer look into the gui LXD profile
Let’s have a closer look at the gui LXD profile contents.
The config node
First, there is environment.DISPLAY, with the default value :0. This is an environment variable, and has the value of the default display of the host’s X11 server. You may have to change this to :1 if you have more displays (for example, multiple graphics cards). Here is how to set this to :1,
The raw.idmap has a value that refers to the sets of $UID/$GID of the non-root user on the host and in the container. By default on Ubuntu the default ID for the first non-root account is 1000 (both user ID and group ID). This is necessary for the bind-mounting of sockets of the host to the container. If you need to change it, here is how to do it. Because we use the both keyword, the first number (i.e. 1000) is the $UID/$GID on the host, and the second number (i.e. 1001) is the $UID/$GID of the non-user account in the container.
The user.user-data are instructions for cloud-init. The LXD container images from the ubuntu: repository support cloud-init, and we use it to pass configuration to the newly created container.
In cloud-init, we use _runcmd _ to run two commands. First, disable shm in PulseAudio so that it uses an alternative that works in LXD. Second, set the PULSE_SERVER environment variable to the Unix socket that have bind-mounted in the devices node.
In packages, we get cloud-init to install for us the minimal packages to get X11 libraries, Mesa libraries and the PulseAudio client libraries. On top of that, we get cloud-init to run apt update for us so that when we get into the container, we can install packages straight away.
The description node
This node has the description text of the LXD profile.
The devices node
The devices node has two Unix sockets, one for PulseAudio and one for X11.
It also gives access to the gpu device.
The used_by node
We do not edit this node, it will include the created containers that have this profile.
Creating shortcuts to the gui container applications
If you want to run Internet Exploerr from the container, you can simply run from a terminal window the following,
and that’s it.
To make a shortcut, create the following .desktop file on the host and finally use desktop-file-install to install into /usr/share/applications/.
This is how the (randomly-selected) icon looks like in a File Manager.
Here is the icon on the Launcher. Simply drag from the File Manager and drop to the Launcher in order to get the application at your fingertips.
Troubleshooting
Error _sudo: unknown user: ubuntu** and **unable to initialize policy plugin_
You get this error when you create a container and then very quickly try to connect to it with a shell. Here is how it looks.
The Ubuntu container images come with cloud-init instructions that, among others, create the non-root account ubuntu. When you launch a container, it takes several seconds to start the runtime and then execute the cloud-init instructions. You get this error when you try to connect too soon, when the ubuntu account has not been created yet. You can try again until the account is created.
Error Pulseaudio, _Connection failure: Connection refused_
You got a shell in the newly created container, but when you try to use the audio, you get Connection refused. Here is how it looks,
The cloud-init instructions in the gui LXD profile have commands to install packages and commands to setup the PulseAudio environment variable. The sequence is to install first the packages, and then add PULSE_SERVER in the ~/.profile. This means that if you get a shell in the container before cloud-init has completed, you have missed the addition of PULSE_SERVER in ~/.profile. As a solution, you can log out and then connect again. Or, do
I have an existing container, can I make it a _gui_** container?**
Yes, you can. You can assign profiles to a container, then restart the container. Here is how,
I have a gui container, can I remove the gui profile from it?
Yes, by assigning the default or any other profile. Then, restart the container.
More errors
Report in the comments any issues that you encounter and I will be adding here.
I tested this on both Intel and AMD GPUs and they worked fine for me. For NVidia there might be some additional issues, so I would rather investigate again rather than copy from the old post.
Discussion
A year ago, I wrote the first version of the post on how to run GUI application in a LXD system container. I had put together older sources from the Internet while writing that post. In this post, I used the comments and feedback of last year’s post to automate the process and make it less error-prone.
Up to now, we have seen how to reuse the existing display of our desktop for any GUI apps running in a container. The downside is that a malicious application in a container can attack the desktop because X11. One solution is to use Xephyr instead of our desktop’s DISPLAY (:0). It is elemental to adapt this post to use Xephyr. However, in terms of usability, it would be ideal to create some sort of VirtualBox clone that would use LXD containers instead of VMs to launch Linux distributions. In this VirtualBox clone, it would be easy to select whether we want to output in a window on the desktop’s DISPLAY or in a Xephyr window. Also, in a Xephyr window we can launch a window manager, therefore we can have a proper Linux desktop environment in a window.
Last updated