[Howto] Use the Host's Wayland and XWayland Servers inside containers

Usecases:
Everyone who wants to use demanding graphical applications, such as Games etc. inside a LXD container might be interested in using this method.
This is also usable for X-Server users, because some Wayland compositors (e.g. weston) can run on an X-Server and with XWayland you also have compatibility for legacy X-Server applications inside Wayland.

Why use Wayland instead of X?
Wayland is the planned sucessor of X(org) and more secure, faster, smoother etc.

Alternatives:
See [Overview] GUI inside Containers.


Important Note on Security:

The security improvements of Wayland do not apply to XWayland, as this is just an X-Server running on Wayland.
This means that applications can access each others windows, even between container and host.

Maybe this can be prevented somehow, but I haven’t researched that yet.

You can of course also use Wayland without XWayland.

Limitations:

  1. Nvidia Graphic Cards:

    • Regarding the proprietary Nvidia Driver:
      For Graphic acceleration in XWayland you need:

      • Nvidia driver version 470.57.02 or higher
      • XWayland version 21.1.2 or higher
      • enable DRM KMS
      • Also take a look at the Nvidia Docs on XWayland for more details.
    • Regarding Nouveau (open source community driver):
      Bad performance on most cards and does not support Vulkan yet.

  2. Bugs: Sometimes there are some bugs (especially when using XWayland), including:

    • mouse and keyboard input not recognized correctly

Howto:

Summary:
You simply add one or both of the following sockets of Wayland as unix-proxy devices to your container (depending on your usecase):

  • the Wayland socket (e.g. wayland-0):
    For Wayland compatible applications.

  • the XWayland socket (e.g. X1):
    For legacy X(org) applications, that would not run on Wayland otherwise. Provides an X-Server inside Wayland.

And then set the environment variables accordingly inside the container, so the system finds the correct sockets.

Example Howto with weston on an X-Server:
Host Distribution: Arch Linux
Container Distribution: Arch Linux

  1. Install necessary software:

    • on your host:
      install weston (or weston-eglstream (for proprietary Nvidia Driver))

    • inside your container:
      Just install an example GUI application, that uses either X or Wayland, this should install all necessary packages as well.
      For example: Supertuxkart or Wine

  2. Here I start weston (a Wayland compositor) on my host (running on top of an X-Server), with the “xwayland” option:

    weston --xwayland

    Notes:

    • For more options, see man weston and man weston.ini.
    • You can of course use other compositors as well (though not all can run on an X-Server).
  3. Search for the sockets of wayland and xwayland on your host:
    In my case (Arch Linux):
    Wayland socket is in: /run/user/1000/wayland-0
    XWayland socket is in: /tmp/.X11-unix/X1
    (while my main X-Server socket is X0)

  4. Add the sockets to your container (for example inside a LXD profile under devices:):
    Notes:

    • The below example assumes that both (main) users (on the host and inside the container) have the uid and gid 1000.
      You can check this with id username.
    • Adjust the paths and uids/gids according to the distributions you use.
    • You might have to create the folder “wayland1” in “/mnt” inside the container first.

    Configs:

     Waylandsocket:
         bind: container
         connect: unix:/run/user/1000/wayland-0
         gid: "1000"
         listen: unix:/mnt/wayland1/wayland-0
         mode: "0777"
         security.gid: "1000"
         security.uid: "1000"
         type: proxy
         uid: "1000"
    
     XWaylandsocket:
         bind: container
         connect: unix:/tmp/.X11-unix/X1
         gid: "1000"
         listen: unix:/mnt/wayland1/X1
         mode: "0777"
         security.gid: "1000"
         security.uid: "1000"
         type: proxy
         uid: "1000"
    

    You might have noticed that I mount the sockets in the /mnt (mount) directory inside the container, this is due to the container deleting the mounts if they reside in /tmp and /run (for example) (see Bindmount for .X11-unix only works when done if container is running · Issue #4540 · lxc/lxd · GitHub for details).

    So we use a workaround: we mount the sockets in /mnt and then we link them to their destination inside the container with the following scripts:
    Note: Run the scripts inside the container.

    Link the Wayland socket:

     #!/bin/sh
    
     mkdir /run/user/1000
     ln -s /mnt/wayland1/wayland-0 /run/user/1000/wayland-0
    

    Link the XWayland socket:

     #!/bin/sh
    
     #mkdir /tmp/.X11-unix/
     ln -s /mnt/wayland1/X1 /tmp/.X11-unix/X1
    

    Note: You can automate these scripts with systemd services.
    An older example is in LXD Github Issue 4540 (Note: I did not test it).

  5. Add the following environment variables in .bashrc (in the home folder of your main user inside the container):
    Notes:

    • Adjust the values accordingly.
    • Some distros might use a different file than .bashrc, for example: .zshrc
    export WAYLAND_DISPLAY=wayland-0
    export XDG_RUNTIME_DIR=/run/user/1000
    export DISPLAY=:1
    export XSOCKET=/tmp/.X11-unix/X1
  1. Now reload the .bashrc file with source .bashrc or restart the container and you should be able to start applications via terminal.
    The applications will then appear in the weston window on your host’s display.

If you use this method, give us some feedback in the comments below :wink:.


See also:

Might be of interest:
howto-audio-via-pulseaudio-inside-container


Todo:

  • find method to prevent implications by XWayland on host/container-seperation
    (if someone would try to run a wayland server nested inside a wayland server, this could be the solution)
  • (partly done) add systemd service for automatic linking
  • add more generic way to find the sockets on the host
  • verify if this runs on other distributions (especially Ubuntu), because it seems Ubuntu might need authentication to access the sockets
    (help is appreciated because I don’t use Ubuntu)

Updates:

  • (30.07.21) Upstream releases now support Graphic acceleration for Xwayland
  • (23.06.21) Added news about XWayland Support for Nvidia drivers, streamlining the whole text
  • (20.04.21) Minor changes, especially regarding new versions and patches of/for xwayland
  • (23.01.21) Minor changes and updates
  • (22.10.20) Minor changes
  • (24.08.20) Added notice about proposed patches for acceleration in XWayland for proprietary Nvidia drivers.
4 Likes

Great guide!

add more generic way to find the sockets on the host

You can hardcode the wayland and X11 socket paths exposed to the LXD profile, and start weston/Xwayland in a way that uses the hardcoded paths. This way different LXD profiles can be easily isolated from one another. For example, you can use a script like below. If you ran it with the parameter “2”, then you can find the wayland socket in $XDG_RUNTIME_DIR/wayland-lxd-2 and x11 socket in /tmp/.X11-unix/X2.

#!/bin/bash

readonly TEMP="wayland-lxd-$1"
# WAYLAND_DISPLAY should not be set when starting weston.
weston --socket="$TEMP" &
# Set WAYLAND_DISPLAY before calling Xwayland
export WAYLAND_DISPLAY="$TEMP"
sleep 1
Xwayland ":$1"
1 Like