Update February 2021: This tutorial shows how to install manually the shared libraries for OpenGL in the container. The user needs to make sure that the library versions in the container matches those of the host. That was not very good. Now, LXD supports the NVidia container runtime, which is provided by NVidia and has the matching shared library versions from the host. Please, follow instead the new post on How to Run X11 Applications in a LXD container.
You can run Steam directly on your desktop, but it tends to install all sorts of 32-bit packages. How can you restrict Steam and any games into a container so that it does not mess the rest of your desktop? A virtual machine would be overkill. In this post, we see how to do this with a LXD system container. We create a LXD system container with Ubuntu 18.04, configure it to run GUI applications that will appear on our desktop with full graphics acceleration and sound, and install Steam.
- You need to have LXD installed and configured on your desktop computer.
- You need to have a desktop with X11 (or Wayland with XWayland).
- If you have an NVidia graphics card and you are using the closed-source driver, note down the driver package. For example, nvidia-driver-390. You need to install that driver additionally into the system container.
- If you have an NVidia graphics card along with an integrated graphics card, then your NVidia graphics card is your second graphics card. You need to take note of that later in this guide.
Adding the steam LXD profile
When you create system containers with LXD, you specify one or more profiles to be applied to the newly created container. In our case, we create a LXD profile specifically for Steam LXD system containers. First, we download the profile from the LXD Steam profile gist. Then, we create an empty LXD profile and populate it with the contents of the gist. The profile has been tested Ubuntu 18.04 (either deb or snap packages of LXD).
lxc profile create steam
cat steam.lxdprofile | lxc profile edit steam
lxc profile show steam
Let’s see the contents of the profile to figure out where to make changes, if needed. Here are the notable places that you may need to change, if you adapt for another system. With this profile, we set in the container
- The DISPLAY to :0.
- The IDMap to the UID and GID of our desktop user account on the host. For Ubuntu, the default non-root account has UID/GID of 1000/1000 respectively.
- We use cloud-init to add the i386 architecture and install a set of require packages for X11 and sound.
- We configure PulseAudio to connect to the PulseAudio server of the host. There is an additional device directive later in the profile for the PulseAudio socket.
- We download and install the Steam deb package.
- We make available the Unix sockets for both X11 and PulseAudio to the container. If your active GPU is NOT the second GPU on your system, then change /tmp/.X11-unix/X1 into /tmp/.X11-unix/X0 As it is, this profile is for a system with an intergrated GPU and an NVidia GPU, and the latter is in use.
- We enable the GPU sharing between the host and the container.
config: environment.DISPLAY: :0 raw.idmap: both 1000 1000 user.user-data: | #cloud-config runcmd: - 'dpkg --add-architecture i386' - 'apt-get update' - 'apt-get install -y x11-apps:i386' - 'apt-get install -y mesa-utils:i386' - 'apt-get install -y libgl1-mesa-glx:i386' - 'apt-get install -y libcanberra-gtk-module:i386' - 'apt-get install -y pulse-audio' - 'apt-get install -y dbus-x11' - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf' - 'echo export PULSE_SERVER=unix:/tmp/.pulse-native | tee --append /home/ubuntu/.profile' - 'wget https://steamcdn-a.akamaihd.net/client/installer/steam.deb' - 'dpkg -i /steam.deb' - 'apt-get install -y -f' description: Steam LXD profile devices: PASocket: path: /tmp/.pulse-native source: /run/user/1000/pulse/native type: disk X0: path: /tmp/.X11-unix/X0 source: /tmp/.X11-unix/X1 type: disk mygpu: type: gpu name: steam used_by:
Up to this point, we created the steam LXD profile. In the next section, we launch the container and get playing.
Launching the LXD system container for Steam
We launch the container with the following command. The container is ubuntu:18.04, which means it runs Ubuntu 18.04. We apply first the default profile, which means that the container will use the configuration of your local LXD installation (i.e. storage, networking). Then, we apply the steam profile, which is the one we added just earlier.
lxc launch ubuntu:18.04 steam --profile default --profile steam
Let’s the the container running.
$ lxc list steam
| NAME | STATE | IPV4 | TYPE |
| steam | RUNNING | 10.10.10.210 (eth0) | PERSISTENT |
We now get a shell into the steam LXD system container. Since your LXD installation is probably pristine, we create first a nice LXD alias and then use it to get a shell into the container.
lxc alias add ubuntu 'exec @ARGS@ --mode interactive -- /bin/sh -xac $@ubuntu - exec /bin/login -p -f '
Now, we can get a shell with lxc ubuntu steam. “lxc” is the command, “ubuntu” is the alias that gives us a non-root shell into the Ubuntu container (default non-root account is also called “ubuntu”), and finally “steam” is the name of the container.
$ lxc ubuntu steam
exec /bin/login -p -f ubuntu
Last login: Thu Apr 4 15:44:07 UTC 2019 on UNKNOWN
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-47-generic x86_64)
System information as of Thu Apr 4 15:44:07 UTC 2019
System load: 0.49 Processes: 21
Usage of /home: unknown Users logged in: 0
Memory usage: 1% IP address for eth0: 10.10.10.210
Swap usage: 0%
Get cloud support with Ubuntu Advantage Cloud Guest:
7 packages can be updated.
6 updates are security updates.
Your Hardware Enablement Stack (HWE) is supported until April 2023.
Great! We got a shell into the container. Before continuing, let’s verify first that the cloud-init instructions have completed in the container. For example, if you are too fast, the container may still be installing Steam! We check by running the following. It says finished, all good!
ubuntu@steam:~$ tail -1 /var/log/cloud-init-output.log Cloud-init v. 18.5-45-g3554ffe8-0ubuntu1~18.04.1 finished at Thu, 04 Apr 2019 15:43:59 +0000. Datasource DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud-net][dsmode=net]. Up 205.00 seconds ubuntu@steam:~$
The container is ready. In the following section we install the NVidia closed-source driver, if required. If you do not need it, skip to the section on Running Steam.
Installing the NVidia closed-source driver (if required)
If you have an NVidia graphics card, you need to install the driver in the container as well. Note that the LXD system containers do not and cannot add Linux kernel drivers. But by installing the driver, we manage to install the user-space libraries that are required for software to work in the container.
In this example, we install the nvidia-driver-390 package in the container. Please make sure that you install the exact same version as on the host.
ubuntu@steam:~$ sudo apt install nvidia-driver-390
We have installed the necessary additional driver in the container. We can now run Steam.
Let’s run Steam. We run it from the shell. Below, there are some actual screenshots from this installation of Steam.
Running Steam on ubuntu 18.04 64-bit
STEAM_RUNTIME is enabled automatically
Installing breakpad exception handler for appid(steam)/version(1550534751)
In terms of functionality, any game should be able to run from within the container. Note that we have not installed a web browser or a file browser, or even an image viewer in the container. You may want to do so. For example, if you take screenshots, you would need a file browser program to view them in the container.
Creating a shortcut to Steam
As it stands now, we would need to open a shell into the steam container and then run steam. If you would rather create a shortcut, perform the following on the host (your desktop, not the container).
First, grab an icon for Steam and place it into a sensible directory. In our case, we place it into ~/.local/share/icons/.
lxc file pull steam/home/ubuntu/.local/share/Steam/tenfoot/resource/images/steam_home.png ~/.local/share/icons/
Then, paste the following in a text editor and save it as steam.desktop.
Comment=Play gameis on Steam
Exec=lxc exec steam -- sudo --user ubuntu --login steam
Finally, move steam.desktop into the ~/.local/share/applications directory.
mv steam.desktop ~/.local/share/applications/
We can then search for the Steam application and place it on the launcher.
We have seen how we can install Steam in a LXD system container. It is handy to separate gaming from our desktop. If you found any gotchas that need addressing, please post a comment.
Do X11 applications work?
Run the following. You should get as output the clock application.
Does graphics acceleration work?
Run the following in the container. If you get a similar output, then it works.
name of display: :0
display: :0 screen: 0
direct rendering: Yes
server glx vendor string: NVIDIA Corporation
server glx version string: 1.4
server glx extensions
Do accelerated OpenGL applications work?
Run the following in the container. If you get a similar output, then it works.
I rebooted my desktop and now it does not work!
When you reboot the desktop, the steam container may not autostart. You need to start it yourself by running the following. This has to do with the Unix socket for X11 and PulseAudio; when the container is being started, it is likely that you have not logged in into the desktop (especially if you set it to ask you for a password). There are ways to get around this, but that’s for another tutorial.
lxc start steam
is vulkan works? someone on reddit mention that vulkan doesn’t work on his setup.
Vulkan works as long as GPU hardware acceleration works in the container.
And obviously, as long as Vulkan works on the host.
First, install “vulkan-utils” on the host, and run “vulkaninfo” to verify that you have the appropriate GPU driver on the host.
Second, repeat the same in the container and check with “vulkaninfo” that you get the same output in the container.
Thanks, i’ll try it
I believe I’m the person they are referring to on reddit. I figured out the issue.
I’m using an AMD card (RX480) and vulkan works in the host.
First thing: in the container, you need to install libvulkan1, mesa-vulkan-drivers vulkan-utils
vulkaninfo will fail with an initialization error.
After doing some traces, I noticed it is failing to open files in /dev/dri
The container has the wrong permissions; on the host those files have an ACL set that lets the current user access them, the container does not.
So to fix: in the container run sudo setfacl -m “u:ubuntu:rw-” /dev/dri/*
vulkaninfo and vulkan-smoketest will now run
Thanks for trying this out and coming back with instructions!
Great stuff… really awesome!
Now how to link in steam library either from the host (via some kind of shared folder) or to remote location such as NAS share?
Really nice tutorial! If i wanted to package this up into a simple install script, how would i do that? Are there prepackaged container configuration systems?
Currently there is no generic configuration system so using a script would likely be the way to go.
I suppose you have in mind to ask a user to first install LXD (snap package), if not already installed. Then, they run your script to create the proper LXD profile with the correct X11 socket depending on which GPU is used (find this from $DISPLAY).
Then, they would need to
lxc launchthe container using the new profile, get a shell in there, and setup Steam.
Alternatively, to avoid setting up Steam in the container every time, you can create a container image out of the container that you have just installed Steam (but not logged in yet). Then, you can publish this container image on your public LXD server for others to use. You can also use this locally if you have more than one Steam accounts.
Here is an example.
lxc stop steamcontainer
lxc publish steamcontainer --alias steamcontainer
Then, setup a VPS (like Hetzner, Scaleway, Linode or DigitalOcean), install LXD and enable remote access to the LXD service (it is an option when you run “lxd init”). Follow this guide, https://stgraber.org/2016/04/12/lxd-2-0-remote-hosts-and-container-migration-612/ to connect your desktop’s LXD installation with this public LXD on the VPS. Next, with “lxc copy” you can copy the container image from your desktop to the public LXD server. Finally, you can share the URL of the public LXD server so that users can
lxc remote addto their desktop’s LXD and be able to get your prepared container image. They would run something like “lxc launch nicolasserver:steamcontainer mysteam” to launch a container with your container image.
Alternative (x2), you can create a custom container image with Steam support using distrobuilder, https://github.com/lxc/distrobuilder and publish it to your public LXD server that serves container images.
I keep getting this error upon
What is your host? Which Linux distribution and version.
Also, which version of LXD do you run? Snap or deb package?
Distro is Ubuntu 18.04, kernel 5.4.0-42-generic
LXD is v. 3.0.3, installed via
System has both iGPU and dGPU. Thanks!
I think you are hitting issues with the ‘idmap’ line in the profile.
I think it would be easier to try out the newer guide that does away with the idmap, https://blog.simos.info/running-x11-software-in-lxd-containers/
Note that LXD 3.0.x may not support proxy devices for “Unix sockets”. You may need to upgrade to the snap package of LXD, currently running LXD 4.5. You can do so if you get an error that the profile is invalid.
https://linuxcontainers.org/lxd/news/ should show when proxy devices for Unix sockets was first introduced. The page needs some effort to parse; I vaguely recollect that it was a newer version than 3.0 (therefore, not supported in 3.0.x).
thanks for the instructions, it works great.
Can you help me to get something like this running headless for steam remote?
i have a headless server with a geforce 1660 ti, with your instructions i set up a lxd container that has access to the graphics card
“nvidia-smi” works, opening gui programs via “ssh -X” works without problems.
i can also start steam and run “steam remote” on my client, but after no time it breaks off.
some games don’t start on the headless server, others start but the connection breaks down immediately
Translated with http://www.DeepL.com/Translator (free version)
I am not familiar with Steam Remote Play, i.e. I have not used it.
I would guess that it is likely not meant to be run in a headless system. Normally, when you run Steam Remote Play, does it keep some window open? With Steam Remote Play, do you need to be an active player in the shared game?
Without a local X server with GL support it is likely that the Remote Play process just crashes. Normally, Steam will keep a crash log around.
How would you go about getting games to run that are installed outside of the container? I have a hard drive filled with games, however I seem to run into issues when launching them, I believe it has something to do with permissions.
I’m using LXD 4.0.5
You would need to look for guides about moving your Steam folder to a different computer. In essence, your host and the LXD container are different computers.
Here is the generic guide, https://support.steampowered.com/kb_article.php?ref=7418-YUBN-8129
Do note that I have not tried those instructions.
Thank you so much for your response!
Couldn’t fully resolve my issue but found a work-around.
Anyway, just wondering if you have any experience with adding wired usb controllers to the container? Or even bluetooth controllers?
I’ll add a little bit more information regarding trying to add my own usb controller, as I’ve currently given you no clue to my current situation. Both my host and container OS is ubuntu 20.04 if that is of any importance.
Basically I used lsusb to gather that my controller has the identity:
Bus 003 Device 007: ID 0e6f:02a8 Logic3 PDP Wired Controller for Xbox One – Arctic White
So I added the device to my container called steam1 using LXC config device add, and is shown as:
I also found a unix-char thing through the use of the jstest-gtk command, and added it appropriately
I noticed my /dev/input/jso didn’t have the same permissions as it has on host so I had them become the same. Initially I could not get my usb controller recognized in jstest-gtk in the container, but now it is recognized in both after the permissions change. In another forum I saw you mentioned that the container should take full control of a usb device but this has not occured? I’m unsure if this is outdated information as I’m on LXD 4.0.5…
Anyway, I’m not quite out of the woods as it is completely unrecognized on steam. Progress has been made as at least the container registers it now.
Also lsusb shows all my usb devices on the container. Also I run it as a privileged container as this computer has no personal information on it at all except for steam/ is basically a console.
Also just for reference my ultimate goal is to create splitscreen (xrandr or fakexrandr) and run two instances of steam through containers (I want to make a program that pushes specific controllers to specific containers) so that I can play splitscreen halo with friends on the one computer on the one screen. We all own Halo so no dramas with the one copy per person deal. Already got it to kinda work with your guides, just optimizing it now.