Running Steam in a LXD system container

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.

Prerequisites

  • 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).

wget https://gist.githubusercontent.com/simos/6e84dcb2aa554b8e2954b26d10e40f69/raw/dac99a48a7941281f45c389cd7dd998d9ccf6098/steam.lxdprofile

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

  1. The DISPLAY to :0.
  2. 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.
  3. We use cloud-init to add the i386 architecture and install a set of require packages for X11 and sound.
  4. 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.
  5. We download and install the Steam deb package.
  6. 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.
  7. 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)
Documentation: https://help.ubuntu.com
Management: https://landscape.canonical.com
Support: https://ubuntu.com/advantage
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:
http://www.ubuntu.com/business/services/cloud
7 packages can be updated.
6 updates are security updates.
Your Hardware Enablement Stack (HWE) is supported until April 2023.
ubuntu@steam:~$

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
The driver package is nvidia-driver-390. Image source: ItsFoss.

We have installed the necessary additional driver in the container. We can now run Steam.

Running Steam

Let’s run Steam. We run it from the shell. Below, there are some actual screenshots from this installation of Steam.

ubuntu@steam:~$ steam
Running Steam on ubuntu 18.04 64-bit
STEAM_RUNTIME is enabled automatically
Pins up-to-date!
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.

[Desktop Entry]
Name=Steam
Comment=Play gameis on Steam
Exec=lxc exec steam -- sudo --user ubuntu --login steam
Icon=/home/user/.local/share/icons/steam_home.png
Terminal=false
Type=Application
Categories=Game;

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.

Conclusion

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.

Troubleshooting

Do X11 applications work?

Run the following. You should get as output the clock application.

ubuntu@steam:~$ xclock 
The xclock X11 application running from within the steam container.

Does graphics acceleration work?

Run the following in the container. If you get a similar output, then it works.

ubuntu@steam:~$ glxinfo 
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.

ubuntu@steam:~$ glxgears 
The glxgears OpenGL application running in a LXD system container.

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

Permanent link to this article: https://blog.simos.info/running-steam-in-a-lxd-system-container/

18 comments

1 ping

Skip to comment form

    • Mochamad Taufan Rezzafri on June 19, 2019 at 03:06
    • Reply

    is vulkan works? someone on reddit mention that vulkan doesn’t work on his setup.

    1. 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.

      That’s it.

        • Mochamad Taufan Rezzafri on June 23, 2019 at 00:54
        • Reply

        Thanks, i’ll try it

    • zakk on June 23, 2019 at 03:13
    • Reply

    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

    1. Thanks for trying this out and coming back with instructions!

    • Rowdy on July 14, 2019 at 09:06
    • Reply

    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?

    • nicolas on November 19, 2019 at 16:08
    • Reply

    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?

    1. Thanks!

      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 launch the 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 add to 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.

    • Jack on August 31, 2020 at 07:53
    • Reply

    I keep getting this error upon

    # lxc launch ubuntu:18.04 steam --profile default --profile steam
    Creating steam
    Starting steam                              
    Error: Failed to run: /usr/lib/lxd/lxd forkstart steam /var/lib/lxd/containers /var/log/lxd/steam/lxc.conf: 
    Try `lxc info --show-log local:steam` for more info
    

    And lxc info reveals

    Name: steam
    Remote: unix://
    Architecture: x86_64
    Created: 2020/08/31 06:49 UTC
    Status: Stopped
    Type: persistent
    Profiles: default, steam
    
    Log:
    
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:mkdir_eexist_on_last:1219 - File exists - Failed to create directory "/sys/fs/cgroup/unified//lxc/steam"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:create_path_for_hierarchy:1243 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:cgfsng_payload_create:1321 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:mkdir_eexist_on_last:1219 - File exists - Failed to create directory "/sys/fs/cgroup/unified//lxc/steam-1"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:create_path_for_hierarchy:1243 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam-1"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:cgfsng_payload_create:1321 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam-1"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:mkdir_eexist_on_last:1219 - File exists - Failed to create directory "/sys/fs/cgroup/unified//lxc/steam-2"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:create_path_for_hierarchy:1243 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam-2"
    lxc steam 20200831064913.681 ERROR    cgfsng - cgroups/cgfsng.c:cgfsng_payload_create:1321 - Failed to create cgroup "/sys/fs/cgroup/unified//lxc/steam-2"
    lxc steam 20200831064913.716 ERROR    conf - conf.c:lxc_map_ids:2999 - newuidmap failed to write mapping "newuidmap: uid range [1000-1001) -> [1000-1001) not allowed": newuidmap 21552 0 1000000 1000 1000 1000 1 1001 1001001 999998999
    lxc steam 20200831064913.716 ERROR    start - start.c:lxc_spawn:1708 - Failed to set up id mapping.
    lxc steam 20200831064913.783 WARN     network - network.c:lxc_delete_network_priv:2613 - Invalid argument - Failed to remove interface "veth7SOWVS" from "lxdbr0"
    lxc steam 20200831064913.783 ERROR    lxccontainer - lxccontainer.c:wait_on_daemonized_start:842 - Received container state "ABORTING" instead of "RUNNING"
    lxc steam 20200831064913.783 ERROR    start - start.c:__lxc_start:1939 - Failed to spawn container "steam"
    lxc 20200831064913.786 WARN     commands - commands.c:lxc_cmd_rsp_recv:132 - Connection reset by peer - Failed to receive response for command "get_state"
    
    1. Hi!

      What is your host? Which Linux distribution and version.
      Also, which version of LXD do you run? Snap or deb package?

    • Jack on September 1, 2020 at 08:13
    • Reply

    Distro is Ubuntu 18.04, kernel 5.4.0-42-generic
    LXD is v. 3.0.3, installed via apt install

    System has both iGPU and dGPU. Thanks!

    1. 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).

    • bernd on October 21, 2020 at 13:31
    • Reply

    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)

    1. Hi!

      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.

    • Dave on February 20, 2021 at 05:21
    • Reply

    Hi Simos,

    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

    Thanks!

    1. Hi Dave!

      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.

    • Dave on March 20, 2021 at 02:10
    • Reply

    Hi Simos!

    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?

    • Dave on March 20, 2021 at 12:14
    • Reply

    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:

    devices:
    controller1:
    productid: 02a8
    type: usb
    vendorid: 0e6f

    I also found a unix-char thing through the use of the jstest-gtk command, and added it appropriately

    joystickdevice:
    path: /dev/input/js0
    type: unix-char

    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.

    Cheers,
    Dave

  1. […] With LXD you can run system containers, which are similar to virtual machines. Normally, you would use a system container to run network services. But you can also run X11 applications. See the following discussion and come back here. In this post, we further refine and simplify the instructions for the second way to run X applications. Previously I have written several tutorials on this. […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.