XServer inside LXC container

alex9434 asked how to run Xserver inside an LXC container? My response is rather long, so I decided to put it in a separate thread. Four files are included below:

  1. The container’s config file.
  2. The container’s xorg.conf
  3. The container’s lightdm.conf
  4. A script to install lightdm, xorg etc in the container.

All of the files except lightdm.conf are hardware-dependent, so you will need to adjust them. The hardware I used was an old laptop with intel integrated graphics.

I have used this setup to run the following desktops in containers:

  • kubuntu-desktop 18.04 & 19.04
  • lubuntu-desktop 18.04
  • ubuntu-mate-desktop 18.04
  • xubuntu-desktop 18.04

The following things work:

  • OpenGL (with hardware-acceleration)

The following things don’t work:

  • audio (the config file does not include the audio device)
  • brightness-keys
  • udev
  • apparmor

config file

# The *privileged* container's config file.
lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.arch = linux64
lxc.include = /etc/lxc/default.conf
# systemd tries to create 6 gettys and complains if /dev/tty1 ... /dev/tty6 don't exist.
lxc.tty.max = 6
lxc.rootfs.path = dir:/var/lib/lxc/testing/rootfs
lxc.uts.name = testing

# Note that lxc.autodev mounts some devices; they will over-mount devices mounted via lxc.mount.entry

# X11 graphics devices: all devices owned by root:video (plus vga_arbiter just in case it is useful)
#                     : the host is an old laptop with intel integrated graphics.
lxc.mount.entry = /dev/video0      dev/video0      none bind,create=file 0 0
lxc.mount.entry = /dev/agpgart     dev/agpgart     none bind,create=file 0 0
lxc.mount.entry = /dev/fb0         dev/fb0         none bind,create=file 0 0
lxc.mount.entry = /dev/dri         dev/dri         none bind,create=dir 0 0
lxc.mount.entry = /dev/vga_arbiter dev/vga_arbiter none bind,create=file 0 0
# mouse/touchpad  (plus psaux just in case it is useful)
lxc.mount.entry = /dev/input       dev/input       none bind,create=dir 0 0
lxc.mount.entry = /dev/psaux       dev/psaux       none bind,create=file 0 0
# tty8 will be used to display the desktop
lxc.mount.entry = /dev/tty8        dev/tty8        none bind,create=file 0 0
# If /dev/tty0 is omitted, login fails for any graphical display-manager (eg lightdm)
lxc.mount.entry = /dev/tty0        dev/tty0        none bind,create=file 0 0
# Warning: When I mounted /dev/console, lxc-stop -n <container> ... crashed X11 on the host.

# ******************************************************************************************
# * warning: lxc.cgroup.devices.allow is sensitive to the number of spaces !!!             *
# *        : It fails if there are multiple consecutive spaces !!!                         *
# ******************************************************************************************

# Character devices to allow: (major and minor numbers found via ls -l)
# /dev/video0                 81:0
lxc.cgroup.devices.allow = c 81:0 rwm
# /dev/agpgart 10:175
lxc.cgroup.devices.allow = c 10:175 rwm
# /dev/fb0 29:0
lxc.cgroup.devices.allow = c 29:0 rwm
# /dev/dri/* 226:*
lxc.cgroup.devices.allow = c 226:* rwm
# /dev/vga_arbiter 10:63
lxc.cgroup.devices.allow = c 10:63 rwm
# /dev/input/* 13:*
lxc.cgroup.devices.allow = c 13:* rwm
# /dev/psaux 10:1
lxc.cgroup.devices.allow = c 10:1 rwm
# dev/tty8
lxc.cgroup.devices.allow = c 4:8 rwm
# dev/tty0
lxc.cgroup.devices.allow = c 4:0 rwm

# debug: all character devices allowed (not secure)
#lxc.cgroup.devices.allow = c
# debug: all devices allowed (not secure)
#lxc.cgroup.devices.allow = a

xorg.conf

# Place this file in the container at /etc/X11/xorg.conf

# On the host, Xorg can often be run without an explicit xorg.conf, because udev provides Xorg with a list of devices.
# In a container udev does not enumerate devices which are bind-mounted into the container.
# Consequently:
#      -we need to set "AutoAddDevices" to "false" and list all devices.
#      -The libinput driver, which appears to depend upon udev, does not work in a container.
#      -InputClasses do not work.  (The lower-level InputDevices do work).

# -Many of the entries below were copied from a config generated via 'X -configure' on the host.
#  The host is an old laptop with intel integrated graphics.

Section "ServerFlags"
        Option      "AutoAddDevices" "false"
EndSection

Section "Module"
        Load  "glx"
EndSection

Section "ServerLayout"
        Identifier     "X.org Configured"
        Screen      0  "Default Screen" 0 0
        InputDevice    "Default Keyboard" "CoreKeyboard"
        InputDevice    "Configured Mouse" "CorePointer"
EndSection

Section "Screen"
        Identifier "Default Screen"
        Device     "Configured Video Device"
        Monitor    "Default Monitor"
EndSection

Section "Monitor"
        Identifier   "Default Monitor"
EndSection

Section "Device"
        Identifier  "Configured Video Device"
        Driver      "modesetting"
#       According to the config generated via 'X -configure' on the host, there are two BusID's available,
#       PCI:0:2:0 and PCI:0:2:1.
#       The host uses PCI:0:2:0.  
#       The container can either share the host's BusID or use the other BusID.
#       If the container uses a separate BusID, it gets a separate framebuffer.
#       I thought this might be advantageous, so I tried it.
#       Indeed, a separate framebuffer is required by OpenGL, and hence by Compiz.
        BusID       "PCI:0:2:1"
#       For driver options, see the "modesetting" manpage (the defaults are almost always best)
EndSection

Section "InputDevice"
        Identifier  "Default Keyboard"
        Driver      "kbd"
EndSection

Section "InputDevice"
        Identifier  "Configured Mouse"
        Driver      "mouse"
        Option      "Protocol" "auto"
        Option      "Device" "/dev/input/mice"
        Option      "ZAxisMapping" "4 5 6 7"
EndSection

local_lightdm.conf

# Place this file in the container at /etc/lightdm/lightdm.conf.d/local_lightdm.conf
# (It overrides the default values in /usr/share/doc/lightdm/lightdm.conf.gz)
# The container's display is tty8.  When the container stops, we return to tty7.

[LightDM]
minimum-display-number=8
minimum-vt=8

[Seat:*]
display-stopped-script=/bin/dash -c 'chvt 7'

install.sh

#!/bin/bash
# run this script in the container.

apt_error() { echo "exiting since package-$* failed"; exit 1; }
apt_get()   { apt-get --no-install-recommends "$1" "${@:2}" || apt_error  "$1"; echo ''; }
apt_get_with_recommends()   { apt-get "$1" "${@:2}" || apt_error  "$1"; echo ''; }

apt_get update
apt_get dist-upgrade

# display-manager
# I could only get lightdm to work in a container.  sddm and slim failed to start Xorg.
apt_get install lightdm lightdm-gtk-greeter

apt_get install xorg
# intel integrated graphics:  we will use the modesetting driver in xserver-xorg-core.
apt_get install xserver-xorg-core \
                xserver-xorg-input-kbd xserver-xorg-input-mouse

# install your favourite desktop
apt_get_with_recommends install kubuntu-desktop

# uninstall alternate display-managers
apt_get purge gdm3 lxdm nodm sddm slim wdm xdm
2 Likes

my intent is to install LXD mostly for its new form of lxc command. can that use or convert this container config? or can i use both LXC and LXD on the same host to setup different containers?

There is a lxc-to-lxd tool which can be used to convert existing containers.

With LXD, I vaguely recall reading that it is possible to make the container’s udev aware of the devices. That might eliminate the need for xorg.conf .

So LXC 4.0 just came out and has this in the changelog:

allow to mount /sys rw in unprivileged containers

With new kernel work done by the LXD team it is now possible to send uevents inside user namespaces. This means it is time to let udev run inside containers. A pre-condition for this is that /sys is mounted rw . If it is not udev will refuse to start.

Will this allow us to create desktop environments in LXC containers more easily?

Possibly, yes, though I wouldn’t call that easy with LXC.

With LXD, we do forward uevents for usb type devices. We need to also do it for unix-hotplug type devices which should be mostly what X11 needs as far as uevents.