A detail step to run desktop environment in container

Note: Desktop environment here is in broad sense. For me a desktop environment is a desktop environment if there is a X11 window manager or wayland compositor with other gui programs. Desktop environment in narrow sense is something like Gnome, Kde, Xfce, etc.

required:
a linux host can run container (lxd, incus, lxc, docker)
a working graphic card
a working sound card
a working network card

Warning: running DE in container is not perfect. You will encounter a lot of tech issues and bugs. Some of them cannot be solved or bypass.

Yes, I have write about it before:
https://discuss.linuxcontainers.org/t/containerlize-gui-apps-with-wayland/

but it surprised me after seeing this post:
https://discuss.linuxcontainers.org/t/is-it-possible-to-run-full-system-gui-container-in-incus/

I showed a way to run a wayland compositor in container, the rest thing need to do is installing wallpaper, file manager, web browser, test editor, video player and so on.

You will see this post 99% unrelated to container.
And I need a lot time to complete this post.

You may need to read extra information:
https://wiki.archlinux.org/title/Desktop_environment
https://wiki.archlinux.org/title/Wayland
https://wiki.archlinux.org/title/Window_manager
https://wiki.archlinux.org/title/Display_manager

1 Like

Chapter I: personal experience

Container installed desktop environment is too long, let’s call it gui ct.

I run a gui ct for over half a year. I use it as HTPC. The biggest issues are container’s wayland compositor will freeze when playing high rate 4K H265 10Bit SDR movie even my gpu can decode it, and host will freeze when CPU or RAM overloaded. Not big issues are can’t auto login because sway(wayland) don’t support display manager, host X11 will crush if X11 container exist, host X11 need xhost or xauthority or x cookie to let container use host X11 socket.

You don’t need to read my old post, I will write from scratch.

First you need something to hold gui ct like X11 window manager or wayland compositor. I think it’s possible we can skip it, but I don’t know what a container need to display in host DRM, it may involve creating a wayland compositor from scratch. I want to choose X11 window manager, but terrible user experience, you can try, I won’t stop you. So I have to choose wayland compositor. For host, it’s ok to use tiling. For container, it’s better to use stacking, unless you are a geek, after testing there are only 2 suit for using: labwc and wayfire.

My host OS is debian, container manager is incus, wayland compositor is sway, you can use whatever you like. You don’t need to install xwayland in host.

Step for gui in host

In my opinion, xfce4-terminal is better than foot. You don’t have to install xfce4-terminal.

apt install sway xfce4-terminal -y

Let’s config sway, otherwize you will see a black screen.

mkdir -p ~/.config/sway
cp /etc/sway/config ~/.config/sway/
nano ~/.config/sway/config
  • * means for all display, if you want to change, you need to start sway, then you can see display name using swaymsg -t get_outputs.
  • sway should choose right resolution for you display, but if you need to change, this is template: resolution --custom 1920x1200@60Hz
  • background image path $HOME/background.jpg change to whatever you want.
  • fill will fill you screen with background image, there are others see: Home · swaywm/sway Wiki · GitHub
  • pos 0 0 will show frame from top left

output * resolution --custom 1920x1200@60Hz bg $HOME/background.jpg fill pos 0 0

  • set default terminal to xfce4-terminal
    set $term xfce4- xfce4-terminal

you need to change below contain to let container shows in fullscreen


#hide border
default_border none
default_floating_border none
font pango:monospace 0
titlebar_padding 1
titlebar_border_thickness 0
#bar {
#    position top
    # When the status_command prints a new line to stdout, swaybar updates.
    # The default just shows the current date and time.
#    status_command while date +'%Y-%m-%d %I:%M:%S %p'; do sleep 1; done

#    colors {
#        statusline #ffffff
#        background #323232
#        inactive_workspace #32323200 #32323200 #5c5c5c
#    }
#}

Now you can start sway using sway in terminal. But if you using VM, WLR_NO_HARDWARE_CURSORS=1 sway.

Press Win+Enter to open terminal, I don’t what Win is in Mac, but I know is a button with icon. If you want sway auto open after login, add this to your .profile:

if [ "$(tty)" = "/dev/tty1" ] ; then
    exec sway
fi

Sound server is pipewire, I have problem playing sound using pulseaudio.

apt install pipewire-audio pavucontrol -y

running as user, not as root. Enable and start the new pipewire-pulse service with:

systemctl --user --now enable pipewire pipewire-pulse
systemctl --user --now enable wireplumber.service

It’s time to check whether or not sound card working. Sometime you need to choose the right sound driver in pavucontrol.If you want to change volume, use pavucontrol in host.

That all gui part in host.

It’s time to prepare for container. Since we can create gui ct, it’s better one user one container. And without display manager, it’s not convenient to switch user.

Step for gui in container

Use id to get your gid and uid, they will be 1000 if you are the only user, it’s fine to be other number just 1000 to your number.

Profile

Create a profile for hardware accelerate.

incus profile copy default hwac
incus profile edit hwac

config: {}
description: Hardware acceleration
devices:
  mygpu:
    type: gpu
    gid: '1000'
    uid: '1000'
name: hwac

Create a profile for wayland socket.

incus profile copy default wayland
incus profile edit wayland

config: {}
description: Let containers use host display
devices:
  waylandSocket:
    type: proxy
    bind: container
    connect: unix:/run/user/1000/wayland-1
    listen: unix:/mnt/wayland-1
    mode: '0770'
    security.gid: '1000'
    security.uid: '1000'
    gid: '1000'
    uid: '1000'
name: wayland

Create a profile for pipewire socket.

incus profile copy default pipewire
incus profile edit pipewire

config: {}
description: Let containers use host sound
devices:
  pipewireSocket:
    type: proxy
    bind: container
    connect: unix:/run/user/1000/pipewire-0
    listen: unix:/mnt/pipewire-0
    mode: '0770'
    security.gid: '1000'
    security.uid: '1000'
    gid: '1000'
    uid: '1000'
name: pipewire

Create a profile for autostart.

incus profile copy default autostart
incus profile edit autostart

config:
  boot.autostart: true
description: auto start and shutdown container
devices: {}
name: autostart

If using X11.

incus profile copy default x11
incus profile edit x11

config: 
  environment.DISPLAY: :0
description: x11
devices:
  x11:
    bind: container
    connect: unix:@/tmp/.X11-unix/X0
    listen: unix:@/tmp/.X11-unix/X0
    security.gid: '1000'
    security.uid: '1000'
    type: proxy
name: x11

Now we can create gui ct. I’m using alpine for gui ct, you can use whatever you like.
incus launch images:alpine/3.19 htpc -p default -p wayland -p pipewire -p hwac -p autostart

Container basic step

Alpine default shell is ash.

incus exec htpc ash
apk update
apk upgrade

I like bash.

apk add bash nano bash-doc bash-completion
sed -i 's/ash/bash/g' /etc/passwd

doas is a replace for sudo.

apk add doas
echo "permit persist :wheel" >> /etc/doas.d/doas.conf

wlroots doesn’t like NVIDIA
Intel Video - Alpine Linux
Radeon Video - Alpine Linux

Install intel video driver.

apk add mesa-dri-gallium mesa-va-gallium linux-firmware-i915

If older than gen8:

apk add intel-media-driver

else:

apk add libva-intel-driver
Do you like me who is not a native English speaker, let’s modify alpine to suit us.

sed -i 's/#unicode="YES"/unicode="YES"/g' /etc/rc.conf

You should find font support your language. For example for east asia:

apk add musl-locales font-noto-cjk

It’s container gui part.

apk add labwc font-dejavu font-awesome waybar dbus dbus-x11 swaybg nwg-launchers xfce4-terminal xdg-user-dirs thunar exo tumbler chromium mpv mousepad rhythmbox wayvnc copyq lxappearance wireplumber pipewire pipewire-pulse pipewire-zeroconf xarchiver openssh

If you are not a native English speaker, you need to install input method. Somehow ibus malfunction, we need to install fcitx5 in alpine edge repository. Edge - Alpine Linux

You need to install fcitx5 addons suit you language. For example fcitx5-chinese-addons.

apk add fcitx5 fcitx5-gtk3 fcitx5-configtool fcitx5-chinese-addons
LANG=zh_CN.UTF-8 apk add lang

Start service we need.

rc-update add dbus
rc-update add sshd
service sshd start

Create user. Let’s say user is lxc:

adduser -s /bin/bash -g "lxc" lxc

set you password now

adduser lxc wheel
adduser lxc video
adduser lxc input
adduser lxc audio

User lxc is now usable.

su -l lxc
LC_ALL=zh_CN.UTF-8 xdg-user-dirs-update

Don’t start labwc yet. Click Win+Enter open a xfce4-terminal. Push .bashrc and .profile to gui ct.

incus file push ~/.bashrc htpc/home/lxc/
incus file push ~/.profile htpc/home/lxc/

You can close xfce4-terminal using Win+Shift+q.

If you are not a native English speaker, add this to .bashrc in gui ct.

export LANG=zh_CN.UTF-8
export GTK_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
export QT_IM_MODULE=fcitx

Alpine doesn’t provide XDG_RUNTIME_DIR, and wayland compositor(labwc) rely on it. So we need to create it every time we login. So we use .profile. Just add this:

if [ -z "$XDG_RUNTIME_DIR" ]; then
	XDG_RUNTIME_DIR="/tmp/$(id -u)-runtime-dir"
	mkdir -pm 0700 "$XDG_RUNTIME_DIR"
	export XDG_RUNTIME_DIR
fi

if [ -z "$XDG_CONFIG_HOME" ] ; then
    export XDG_CONFIG_HOME=$HOME/.config
fi

Now we have XDG_CONFIG_HOME, we can actually run labwc, don’t do it, else it will be a headless wayland session.

We are going to tell labwc to use host wayland socket. Just add this in .profile:

if ! [ -S "$XDG_RUNTIME_DIR/wayland-1" ] ; then
    ln -s /mnt/wayland-1 $XDG_RUNTIME_DIR/wayland-1
fi

if [ -z "$WAYLAND_DISPLAY" ] ; then
    export WAYLAND_DISPLAY=wayland-1
fi

Now we can actually run labwc:

source .bashrc
source .profile
labwc

If you see a black screen with a curse then you are succeed.
Let’s start custom it.

custom labwc

mkdir -p ~/config/labwc
nano ~/config/labwc/autostart

swaybg -i $HOME/Pictures/background.jpg -m fill >/dev/null 2>&1 &
copyq --start-server enable >/dev/null 2>&1 &
waybar >/dev/null 2>&1 &

nano ~/config/labwc/environment

# This allows xdg-desktop-portal-wlr to function (e.g. for screen-recording)
XDG_CURRENT_DESKTOP=wlroots
XDG_SESSION_DESKTOP=wlroots
XDG_SESSION_TYPE=wlroots
# Force firefox to use wayland backend
MOZ_ENABLE_WAYLAND=1
# Set cursor theme.
# Find icons themes with the command below or similar:
# find /usr/share/icons/ -type d -name "cursors"
# XCURSOR_THEME=breeze_cursors
# Disable hardware cursors. Most users wouldn't want to do this, but if you
# are experiencing issues with disappearing cursors, this might fix it.
WLR_NO_HARDWARE_CURSORS=1
# For Java applications such as JetBrains/Intellij Idea, set this variable
# to avoid menus with incorrect offset and blank windows
# See https://github.com/swaywm/sway/issues/595
_JAVA_AWT_WM_NONREPARENTING=1
QT_QPA_PLATFORM=wayland-egl
QT_WAYLAND_FORCE_DPI=physical
QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
ELM_DISPLAY=wl
SDL_VIDEODRIVER=wayland
SAL_USE_VCLPLUGIN=gtk3
GTK_USE_PORTAL=0
MOZ_ENABLE_WAYLAND=1
MOZ_DBUS_REMOTE=1
XDG_SESSION_TYPE=wayland
intel_iommu=igfx_off
LIBVA_DRIVER_NAME=i965
WAYLAND_DISPLAY=wayland-0

nano ~/config/labwc/menu.xml

<?xml version="1.0" encoding="UTF-8"?>

<openbox_menu>

<menu id="client-menu">
  <item label="Minimize">
    <action name="Iconify" />
  </item>
  <item label="Maximize">
    <action name="ToggleMaximize" />
  </item>
  <item label="Fullscreen">
    <action name="ToggleFullscreen" />
  </item>
  <item label="Decorations">
    <action name="ToggleDecorations" />
  </item>
  <item label="AlwaysOnTop">
    <action name="ToggleAlwaysOnTop" />
  </item>
  <!--
    Any menu with the id "workspaces" will be hidden
    if there is only a single workspace available.
  -->
  <menu id="workspace" label="Workspace">
    <item label="Move to left workspace">
      <action name="SendToDesktop" to="left" />
    </item>
    <item label="Move to right workspace">
      <action name="SendToDesktop" to="right" />
    </item>
  </menu>
  <item label="Close">
    <action name="Close" />
  </item>
</menu>

<menu id="root-menu">
  <item label="App launcher">
    <action name="Execute" command="nwggrid" />
  </item>
    <item label="File browser">
    <action name="Execute" command="thunar" />
  </item>
  <item label="Web browser">
    <action name="Execute" command="chromium" />
  </item>
  <item label="Terminal">
    <action name="Execute" command="xfce4-terminal" />
  </item>
  <item label="Reconfigure">
    <action name="Reconfigure" />
  </item>
  <item label="Exit">
    <action name="Exit" />
  </item>
</menu>

</openbox_menu>

nano ~/config/labwc/rc.xml

<?xml version="1.0"?>

<labwc_config>

  <keyboard>
    <default />
    <!-- Use a different terminal emulator -->
    <keybind key="W-Return">
      <action name="Execute" command="foot" />
    </keybind>
    <!--
      Remove a previously defined keybind
      A shorter alternative is <keybind key="W-F4" />
    -->
    <keybind key="W-F4">
      <action name="None" />
    </keybind>
  </keyboard>

  <mouse>
    <default />
    <!-- Show a custom menu on desktop right click -->
    <context name="Root">
      <mousebind button="Right" action="Press">
        <action name="ShowMenu" menu="root-menu" />
      </mousebind>
    </context>
  </mouse>

<focus>
  <focusNew>no</focusNew>
  <focusLast>no</focusLast>
  <followMouse>no</followMouse>
  <focusDelay>200</focusDelay>
  <underMouse>no</underMouse>
  <raiseOnFocus>no</raiseOnFocus>
</focus>

</labwc_config>

Custom waybar now. waybar(5) — Arch manual pages

Custom waybar

mkdir -p ~/config/waybar
all icon below will be something else because missing font-awesome.
nano ~/config/waybar/config

{
    "layer": "top", // Waybar at top layer
    // "position": "left", // Waybar position (top|bottom|left|right)
    // "height": 38, // Waybar height (to be removed for auto height)
    //"width": 1000, // Waybar width
    "spacing": 4, // Gaps between modules (4px)
    // Choose the order of the modules
    "modules-left": ["wlr/taskbar"],
    "modules-center": ["clock"],
    "modules-right": ["wireplumber", "tray", "cpu", "memory", "network",  "temperature"],
    // Modules configuration
    "clock": {
        "locale": "zh_CN.UTF-8",
	"format": "<span font='Noto Sans CJK SC'>{:%H:%M} </span> ",
	"format-alt": "<span font='Noto Sans CJK SC'>{:%Y-%m-%d} </span>ïł ",
	"tooltip-format": "<span size='12pt' font='Noto Sans CJK SC'><big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt></span>",
	"calendar": {
		"mode" : "year",
		"mode-mon-col" : 3,
		"weeks-pos" : "right",
		"on-scroll" : 1,
		"on-click-right": "mode",
		"format": {
			"months": "<span color='#ffead3'><b>{}</b></span>",
			"days": "<span color='#ecc6d9'><b>{}</b></span>",
			"weeks": "<span color='#99ffdd'><b>W{}</b></span>",
			"weekdays": "<span color='#ffcc66'><b>{}</b></span>",
			"today": "<span color='#ff6699'><b><u>{}</u></b></span>"
		}
	},
	"actions": {
		"on-click-right": "mode",
		"on-scroll-up": "shift_up",
		"on-scroll-down": "shift_down"
	}
},
    "cpu": {
        "format": "{usage}% ",
        "tooltip": false
    },
    "memory": {
        "format": "{}% "
    },
    "temperature": {
        // "thermal-zone": 2,
        // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
        "critical-threshold": 80,
        // "format-critical": "{temperatureC}°C {icon} ",
        "format": "{temperatureC}°C {icon} ",
        "format-icons": ["", "", ""]
    },
    "network": {
        // "interface": "wlp2*", // (Optional) To force the use of this interface
        "format-wifi": "{essid} ({signalStrength}%) ",
        "format-ethernet": "{ifname}  ",
        "tooltip-format": "{ifname} gateway {gwaddr} ",
        "format-linked": "{ifname} (No IP) ",
        "format-disconnected": "Disconnected ⚠",
        "format-alt": "<span font='Noto Sans CJK SC'>{ifname}: {ipaddr} /{cidr}</span> "
    },
    "wlr/taskbar": {
        "format": "{icon} {name} ",
        "icon-size": 18,
        "tooltip-format": "{title}",
        "on-click": "minimize-raise"
    },
    "tray": {
	"icon-size": 20,
	"spacing": 5
    },
    "wireplumber": {
	"format": "{volume}% ",
	"format-muted": "",
	"max-volume": 1.0,
	"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
	"on-scroll-up": "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+",
	"on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
    }
}

nano ~/config/waybar/style.css

#waybar {
    font-family: "Noto Sans CJK SC";
    font-size: 18px;
}

#window {
    padding: 0 1px;
}

window#waybar {
    border: none;
    border-radius: 0;
    box-shadow: none;
    text-shadow: none;
    transition-duration: 0s;
    color: rgba(217, 216, 216, 1);
    background: rgba(180, 153, 255, 0.6);
}

window#waybar.solo {
    background: rgba(35, 31, 32, 0.5);
}

window#waybar.empty {
    /* display: none; */
}

/* Repeat style here to ensure properties are overwritten as there's no !important and button:hover above resets the colour */

#custom-powermenu {
    color:lightcoral;
}

#temperature {
    color: rgb(153,35,51);
}

#idle_inhibitor {
    color: powderblue;
}

#language {
        color: orchid;
}

#taskbar {
        background-color: lightyellow;
        border-radius: 8px;
}

#taskbar button.active {
        background-color: yellow;
        border-radius: 4px;
        padding: 2px;
        margin: 2px;
}

#taskbar button {
        background-color: transparent;
        border-radius: 4px;
        padding: 2px;
        margin: 2px;
}

#tray {
        background-color: transparent;
}

#wireplumber,
#wireplumber.muted,
#clock,
#taskbar,
#mode,
#battery,
#cpu,
#memory,
#network,
#idle_inhibitor,
#custom-media {
    color: rgb(157, 25, 6);
    margin: 0 2px;
}

#battery {
    color: lightgreen;
}

#battery.warning {
    color: rgba(255, 210, 4, 1);
}

#battery.critical {
    color: rgba(238, 46, 36, 1);
}

#battery.charging {
    color: rgba(217, 216, 216, 1);
}

#custom-storage.warning {
    color: rgba(255, 210, 4, 1);
}

#custom-storage.critical {
    color: rgba(238, 46, 36, 1);
}

@keyframes blink {
    to {
        background-color: #ffffff;
        color: black;
    }
}

Now you can restart labwc. And something like this will show. Yes, you need to find font-awesome icon.

Setting GTK theme

You can custom it the way gtk does using lxappearance. But something will not update well. Here is a sh script I copyed from somewhere to proper update:


#!/bin/sh

# usage: import-gsettings
config="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-3.0/settings.ini"
if [ ! -f "$config" ]; then exit 1; fi

gnome_schema="org.gnome.desktop.interface"
gtk_theme="$(grep 'gtk-theme-name' "$config" | sed 's/.*\s*=\s*//')"
icon_theme="$(grep 'gtk-icon-theme-name' "$config" | sed 's/.*\s*=\s*//')"
cursor_theme="$(grep 'gtk-cursor-theme-name' "$config" | sed 's/.*\s*=\s*//')"
font_name="$(grep 'gtk-font-name' "$config" | sed 's/.*\s*=\s*//')"
gsettings set "$gnome_schema" gtk-theme "$gtk_theme"
gsettings set "$gnome_schema" icon-theme "$icon_theme"
gsettings set "$gnome_schema" cursor-theme "$cursor_theme"
gsettings set "$gnome_schema" font-name "$font_name"

Now the most weird part.
We have to start pipewire manually.

/usr/libexec/pipewire-launcher >/dev/null 2>&1 &

Then remove it’s socket.

rm $XDG_RUNTIME_DIR/pipewire-0

Then swap with host pipewire socket.

ln -s /mnt/pipewire-0 $XDG_RUNTIME_DIR/pipewire-0

For waybar to display right volume, kill it and start it.

killall waybar
nohup waybar >/dev/null 2>&1 &

If you install fcitx5:

nohup fcitx5 -d >/dev/null 2>&1 &

It’s too complex to do every time login container. So a sh script:

#/bin/sh
/usr/libexec/pipewire-launcher >/dev/null 2>&1 &
sleep 3s
rm $XDG_RUNTIME_DIR/pipewire-0
ln -s /mnt/pipewire-0 $XDG_RUNTIME_DIR/pipewire-0
killall waybar
nohup waybar >/dev/null 2>&1 &
nohup fcitx5 -d >/dev/null 2>&1 &

That’s it. Now you can use this gui ct as normal de.

Every time you need to run this gui ct, you only need to run:

incus exec htpc – su -l lxc

2 Likes

Chapter II: try and error

a nested gui container = a container using x11/Wayland protocol drawing images in host compositor
a headless gui container = a container installed x11 window manager/wayland compositor without drawing images in host compositor

In above, a nested stacking Wayland ct renders itself in host tilling Wayland compositor normally. Because Wayland compositor can draw in itself. So, no matter what host compositor is, every thing nested Wayland ct draws rendering in itself, unless you tell it to render in other place. What about nested x11 ct, things going in complete opposite direction, whatever nested x11 ct draws rendering in host window. Because whatever a x11 app need to draw, it needs to tell window manager to draw for it, and there can be only one active window manager in x11, and a window manager only has one compositor. Yes, compositor is the one rendering images in your screen.

conclusion:
nested wayland is isolated from host display environment.
nested x11 is mixed with host display environment.

nested stacking x11 ct

So, it’s what you will see if a nested stacking x11 ct showing in tilling xwayland compositor.

host os: debian 12
host Wayland compositor: sway
host app: xfce4-terminal
container os: debian 12
container x11: mate
container app: caja


From left to right, host’s xfce4-terminal, mate top panel, mate caja, mate bottom panel. Very tilling taste.

let’s nest a stacking x11 in a stacking xwayland compositor.
I installed labwc in host(I’m too lazy to start a new vm, so just change debian source to testing). Another gui ct called xfce but installed xfce and mate. So, what will happen when we start mate? labwc crushed. i tried wayfire too, crushed. I don’t know why but labwc and wayfire can’t open x11 app with xwayland, even they are installed in host. But there is an old buddy weston who is not very suit for daily use but very suit for testing. Yes, I forgot about it until viewing arch wiki. Can we start x11 app in weston? Yes, in ~/.config/weston.ini, add this:

[core]
xwayland=true

Let’s try again. Start mate-session in ct.


wait, what is that?
Killed mate and start with nohup mate-session >/dev/null 2>&1 &
Same thing, but a panel missing. But we can start app in terminal now. Try xeyes. Haha, work.

Well, it seems like we can open more than one mate-session.

And panel is usable, cool.


Nested x11 apps work in stacking way, great.

What about xfce.


Better, but xfce panel blocked by weston panel and xfce dock.

Chapter III:

Last year, I was updating my server, and decided to pass-through gpu to vm to isolate gui environment from host to reduce possible risks that something I do in gui might destroy host. I did, but there is no audio output from vm even with an audio card pass-throughed. Most people are fine with that, but not me, a htpc is not a htpc without audio output. So I started searching for alternative. I decided to nest x11 in container after seeing this 2 post:

Due to my short years in Linux desktop environment, I failed, kde won’t let me using x11 or xwayland socket in container, because of xauthority. It’s time to revenge.

Let’s see some necessary file in Gnome.

Launch a container.
incus launch images:debian/12 x -p default -p x11 -p hwac
Got to push xauth file to container. But xauth file in gnome changes name every time restarted. But you can see this post solved the problem: https://discuss.linuxcontainers.org/t/incus-lxd-profile-for-gui-apps-wayland-x11-and-pulseaudio/.
incus file push /run/user/1000/.mutter-* x/mnt/xauth

incus exec x bash

apt install xfce4
export XAUTHORITY=/mnt/xauth
startxfce4

But shows a warn.


But who’s dbus-launch? Container’s.

apt install dbus-x11
Start again. Well, it’s quite easy. I remember I was pulling my hair off with no idea what to do last year.

But this nested x11 is not usable, at least that’s what I think. To make it work, we need to jump out of box. We can’t open x11 in a window like wayland, but we can mix ct x11 with host.

As I said in last chapter, whatever a x11 app need to draw, it needs to tell window manager to draw for it. So, we don’t need to start a full desktop environment(Gnome) in host, a window manager is enough. Let’s try fluxbox which I’ve never used before.
In host:
apt install fluxbox -y
Now you can select fluxbox using display manager.


Let’s see some necessary file in fluxbox.

Let’s run startxfce4 in ct.

That’s way better than gnome. And way smooth than mate in weston. Wait, I forgot to push xauth file to ct, it opened anyway, I should have tried it earlier.
But what will happen after killed xfce? Fluxbox background was destroyed.

conclusion:

  • Nesting x11 is way more easier but less secure than wayland.
  • Nested x11 ct have more bugs, you can see a lot warnings in terminal.
  • I recommend nest wayland in wayland, x11 in x11.

That’s all, you have learnt how to run desktop envirement in container.

1 Like

Hey, thanks for this post, thought i let you know that it is hard to follow instructions at times especially when one is not as familiar with system parts as you are and does not know in which shell, host, container and if as user or root you are doing the modifications. :wink:

EDIT: My first attempt was with ubuntu 24.04 container but failed, i am using a amd rx580 on the host.
Then i tried with a alpine container and got to see the black screen switch!!! That is it though, cannot progress further atm, might come back at a later time. It was fun and learned something.

You saw a black screen means you succeeded.
I guess you used labwc in Alpine. You just need to install a Wayland supported terminal emulator and add it to labwc menu then you can use cursor to launch terminal emulator in menu and start gui application using cli. Pure wayland compositors need to config a lot to fit normal use.

Ubuntu container failed, it’s interesting, I did test different linux distributions container and they work almost the same, since Ubuntu based on debian, and I did show you how to run x11 in debian, it should be working.
So, what’s your host DE, what’s your container DE.
If host DE is wayland, ct DE is Wayland, I guess host DE doesn’t support nesting.
If host DE is X11, ct DE is wayland, then I have no idea, I’ve never tried.
If host DE is X11, ct DE is X11, it must be permission error. You need to find the right xauthority file then push to ct and “export XAUTHORITY=(the path of xauthority file)”, if you succeed as root but fail as normal user is because I forgot to add gid and uid in x11 profile.

1 Like

Thanks for the kind reply! When i find the time i will look into it.

This was a test for me to see how things have progressed with my first incus install. My servers still run LXD, then i saw your post and thought to give it a go and see how far i can get and play around, it was fun.

Yeah, playing new stuff is always fun. I even had a old laptop for testing specifically. Until a drop of water killed it.
I’ve used lxd and incus. The only difference I noticed is lxd uses lxc, incus uses incus.

You are doing God’s work. Quick question, am I right to presume that things are probably easier if we install xrdp & xrdpxorg (I favor X11 right now) and just RDP from another machine to the container? Host could be kept slimmer as well.

You are doing superb work. What do you think about installing RDP server (xrdp + xrdpxorg) on the container and using another machine to RDP into it? It would be easier wouldn’t it? And the host could be kept slimmer.

simos has already written about it.

So did Scotti: https://discussion.scottibyte.com/t/linux-desktop-environments-in-an-incus-container/444. Scotti’s seems way simpler than Simos’s. But they all pretty much assume a desktop environment on the host. Your instructions are not clear in that how much sway + a terminal emulator is enough in themselves or more “DE components” are implicitly there. If I can just put an X11 compositor on top of my minimal Fedora and then display the container DE then that would be sweet.

It’s not easy to post replies in this forum, this was my second that got lost.

So I was saying, Scotti has covered it nicely as well. However, it seems to me that both he and the one you mentioned assume DE installed on the host. Does your approach really only need a compositor and that’s it? (I favor the X11 route though right now)

I use this server as a htpc too, so I need a compositor on host. But you don’t have to.
If you just want a headless GUI CT, you don’t even need to read this post, just “incus launch images:debian/12 headlessguict”, “incus exec headlessguict bash”, “apt update && apt upgrade”, “apt install whateverDEyouwant”, “apt install whateverRDPorVNCyouwant”, create a user. Then as the new user config autostart vnc or rdp as the DE required. You can test now using cli, start the DE, after DE started, vnc or rdp also started, connect it, if succeeded read next, else fix it.
Now you will encounter a problem, incus doesn’t have a init system like docker, so after this CT start, it will do nothing, means your DE won’t start, your vnc or rdp won’t start, basically useless. So read this: https://discuss.linuxcontainers.org/t/autostart-container-desktop-environment/. But don’t completely follow it. You host doesn’t need to have a compositor, so only need to check whether CT started, if started, ssh to it, else start it then ssh to it. But how to run script when host started, either cron job or systemd, learn it yourself.
Basically, running a headless CT is almost the same as a headless VM, except autostart.

Thanks a lot! That clears up quite a bit. I will get on it. Btw I use minimal Fedora 40 netinst (headless; discounting GNU screen :slight_smile:) for the host and the base incus Fedora 40 image for the guests, DE being Cinnamon with some of them. We shall soon see.

Usually once you trip the moderation system, posting more responses is just going to get them stuck too until one of the moderators get to look at the review queue and process the entries.

We have the forum configured to be somewhat aggressive on flagging things for moderation, either because it’s detecting many accounts at the same IP, posts being written too fast (easy to trip when copy/pasting) or having a high score according to whatever external antispam we use.

That’s needed as we’ve been repeatedly attacked by scam bots, registering in some cases hundreds of accounts a day and then trying to respond or start new posts, mostly copying content from other posts but then suggesting people reach out to them on whatsapp or other platforms
 Those bots also seem to be somewhat adaptable as just adding their usual keywords in the blocklist wasn’t effective


You do whatever you need to do, but inform the user what’s happening, swallowing the comments without signalling what’s happening is very disorienting.

Discourse does show a notification when this first happens, but it’s pretty easy to miss

There is an upstream issue that’s trying to have this change with a prominent persistent notification instead: Persistent notification for user when their post is held for approval - Feature - Discourse Meta

1 Like