KDE Plasma in a Jail
This guide turns a FreeBSD jail into a remotely accessible KDE Plasma desktop. XRDP accepts an RDP connection, authenticates a regular FreeBSD account, and starts a private Plasma X11 session through the xorgxrdp display driver.
The default setup renders the desktop in software and does not need a physical display, GPU passthrough, SDDM, Wayland, or a custom DevFS ruleset. An optional host render node can accelerate the XRDP desktop when a supported iGPU is available.
The installer follows the FreeBSD Handbook’s Plasma and D-Bus configuration and the FreeBSD XRDP package’s setup requirements. It is limited to FreeBSD 15.0 or newer and refuses to run on the host unless that safety check is deliberately overridden.
A desktop jail is still a jail, not a virtual machine. It shares the FreeBSD host kernel and has no virtual firmware, emulated graphics card, or local monitor. XRDP creates an Xorg display only when a user connects.
This setup is suitable for administration, development tools, browsers, and ordinary desktop applications. Hardware controls such as suspend, reboot, display brightness, removable devices, and direct GPU settings may be unavailable or inappropriate inside a jail. Use Log Out to end a Plasma session; manage the jail itself from Sylve.
Prepare the jail
Section titled “Prepare the jail”Follow Simple Jail through Start and verify the jail, applying the changes below while creating it. Stop before installing AdGuard Home.
Use these identity values:
- Jail Name:
KDEDesktop - Hostname:
kde-desktop - Description:
KDE Plasma remote desktop
Choose an unused Jail ID.
Storage
Section titled “Storage”Use a FreeBSD 15.1 Base bootstrap on a FreeBSD 15.1 or newer host. The script installs the complete kde package rather than a minimal Plasma set, plus the Rocky Linux compatibility userland used by downloaded Linux applications. Allow at least 12 GiB of free space in the jail dataset.
Package counts and sizes change as the FreeBSD repository changes. A tested FreeBSD 15.1 quarterly installation downloaded about 1 GiB and added about 7 GiB across 600 packages.
Network
Section titled “Network”Attach the jail to a network-facing Standard Switch and give it an address reachable from the intended RDP clients. DHCP works, but reserve the lease on the upstream DHCP server so bookmarks and firewall rules continue to point to the same address. A static address is also suitable.
XRDP listens on TCP port 3389 inside the jail. Confirm that the upstream network and any Sylve-managed firewall permit that port only from a trusted LAN or VPN.
Hardware
Section titled “Hardware”Use 4 CPU cores and 8 GiB of memory for a comfortable full KDE desktop. Two cores and 4 GiB can work for light use, but package installation, web browsers, and software rendering will be slower.
Keep Start On Boot and Resource Limits enabled. Leave Custom DevFS Ruleset disabled for the simple software-rendered setup.
Optional iGPU acceleration
Section titled “Optional iGPU acceleration”The installed xorgxrdp driver supports DRI3 and glamor acceleration through /dev/dri/renderD128; the iGPU remains managed by the FreeBSD host and does not need PCI passthrough. First check the Sylve node’s terminal, outside the jail:
ls -l /dev/dri /dev/drmContinue only if the host graphics driver has created /dev/dri/renderD128. Enable Custom DevFS Ruleset for the jail and enter:
add path 'dri' unhideadd path 'dri/renderD128' unhideThese rules expose only the host render node needed by xorgxrdp, rather than every graphics device. The installer adds the desktop user to the video group and automatically uses the render node when that user can access it; otherwise, it retains software rendering. If you add the rules later, stop and start the jail, then log out of the Plasma session and reconnect.
Advanced
Section titled “Advanced”Keep Type set to FreeBSD, Clean Environment enabled, and the lifecycle hooks disabled. Retain the default FreeBSD allowed options, including SysV IPC (allow.sysvipc). KDE and Xorg use the normal FreeBSD jail IPC facilities; no additional privilege is required.
Create and start the jail, verify its network connection, and create a Snapshot before installing the desktop. A snapshot provides a quick return to the small base jail if the desktop is no longer wanted.
Leave FSTab Entries empty during this initial creation. A clean FreeBSD bootstrap does not yet contain the /compat/linux mount targets. The main installer creates them, and this guide returns to the jail options immediately afterward to add the Linux mounts before the first RDP connection.
Download the installer
Section titled “Download the installer”Open the jail’s Sylve console or connect to it over SSH as root. Download the KDE and XRDP installer:
fetch -o /root/install-kde-xrdp.sh https://sylve.io/scripts/install-kde-xrdp.shReview it before running it:
less /root/install-kde-xrdp.shThe script is safe to rerun after an interrupted package download or on an already configured jail. If it finds an unrelated startwm.sh in the desktop user’s home directory, it preserves that file with a timestamped backup before writing the Plasma session launcher.
Install KDE and XRDP
Section titled “Install KDE and XRDP”Choose a lowercase login name for the desktop account. This example uses desktop:
sh /root/install-kde-xrdp.sh desktopIf the account does not exist, the script creates it and asks for its password. The password is entered through FreeBSD’s passwd utility and is not accepted as a command-line argument. Use a strong, unique password; this account can start a graphical session and access everything owned by that FreeBSD user.
The installer then:
- Refreshes the signed FreeBSD package catalogues.
- Installs
kde, the Ark archive manager, Mesa’s renderer diagnostics,xrdp,xorgxrdp,pulseaudio-module-xrdp,linux-rl9, and every additional compatiblelinux-rl9-*package from the configured repository. - Adds the login account to FreeBSD’s
videogroup. - Creates a per-user Plasma X11 launcher using D-Bus and ConsoleKit.
- Uses an accessible iGPU render node when available, with automatic software-rendering fallback.
- Adds PulseAudio sink and source modules for speaker and microphone redirection over RDP.
- Disables root login through XRDP.
- Enables and starts D-Bus,
xrdp-sesman, and XRDP. - Installs
sylve-linux-appand creates the Linux compatibility mountpoints. - Checks whether Sylve’s host Linux ABI, the jail option, and the FSTab mounts are ready.
- Confirms that XRDP is listening on TCP port
3389.
The full package installation can take several minutes. Do not close the console or interrupt the jail while pkg is installing packages. If the connection is lost, reconnect and run the same command again.
Linux application support is included by default. For a smaller FreeBSD-only desktop, disable that part explicitly:
INSTALL_LINUX_APPS=NO sh /root/install-kde-xrdp.sh desktopFor unattended package installation, the password prompt can be deferred:
SKIP_PASSWORD_SETUP=YES sh /root/install-kde-xrdp.sh desktoppasswd desktopThe account remains locked until passwd desktop succeeds. RDP authentication cannot use SSH keys, so a usable account password is required even when SSH access uses a key.
Complete the jail’s Linux configuration
Section titled “Complete the jail’s Linux configuration”The main installer has now installed the Rocky Linux 9 compatibility userland, created its mountpoints, and placed sylve-linux-app in /usr/local/bin. FreeBSD’s Linux binary compatibility layer can run many unmodified Linux binaries without replacing this jail’s FreeBSD userland, but the remaining kernel and mount configuration belongs to the Sylve node. Keep the jail’s Type set to FreeBSD. Sylve’s Simple Linux FSTab preset is intended for a full Linux-userland jail and must not be saved unchanged for this setup.
The linux-rl9 metapackage includes the GTK, X11, Mesa, D-Bus, audio, font, and multimedia libraries commonly required by downloaded desktop applications. The installer also adds every other Rocky Linux 9 package that can coexist with the metapackage, including optional graphics, Tcl/Tk, Qt, and development tools. It deliberately excludes linux-rl9-sdl_net: that package conflicts with the metapackage’s linux-rl9-sdl12-extralibs because both provide libSDL_net-1.2.so.0; the library remains available through the metapackage. The installer does not mix in the older and incompatible linux-c7 userland, application-specific ports, GPU driver libraries, or hardware kernel modules.
Take another snapshot of the completed in-jail installation. No manual linux_enable or service linux command is needed on the node: when Sylve’s Jails service is enabled, the Sylve backend loads the Linux ABI and supporting kernel modules and prepares the node’s /compat/linux mounts during startup.
Stop the jail. Under Options → Additional Options, add:
linux = new;Open Options → FSTab Entries, choose Manual, and paste the entries produced below. Enter the real host mountpoint of the jail’s root ZFS dataset; it normally follows <pool-mountpoint>/sylve/jails/<Jail ID>.
Enter the host path to the jail root, ending with its Jail ID. Do not enter/compat/linux here.
devfs /zroot/sylve/jails/115/compat/linux/dev devfs rw 0 0
tmpfs /zroot/sylve/jails/115/compat/linux/dev/shm tmpfs rw,size=1g,mode=1777 0 0
fdescfs /zroot/sylve/jails/115/compat/linux/dev/fd fdescfs rw,linrdlnk 0 0
linprocfs /zroot/sylve/jails/115/compat/linux/proc linprocfs rw 0 0
linsysfs /zroot/sylve/jails/115/compat/linux/sys linsysfs rw 0 0
/zroot/sylve/jails/115/tmp /zroot/sylve/jails/115/compat/linux/tmp nullfs rw 0 0
/zroot/sylve/jails/115/home /zroot/sylve/jails/115/compat/linux/home nullfs rw 0 0Seven host-side mount entries ready.
These are host-managed mounts, so the jail does not need allow.mount or any allow.mount.* capabilities. The two nullfs entries share the jail’s own /tmp and /home with Linux applications; they do not expose the Sylve host’s directories.
Start the jail and verify both Linux execution and the installed helper:
/compat/linux/bin/uname -acommand -v sylve-linux-appVerify the services
Section titled “Verify the services”The installer performs these checks automatically. You can repeat them after maintenance or a jail restart:
service dbus statusservice xrdp-sesman statusservice xrdp statussockstat -4 -6 -l | grep ':3389'The final command should show xrdp listening on *:3389. SDDM should remain disabled; XRDP provides the remote login and starts a separate Xorg display for each session.
XRDP uses a self-signed TLS certificate by default, so the RDP client will show a certificate warning on the first connection.
Connect to Plasma
Section titled “Connect to Plasma”Open an RDP client and connect to:
<jail-ip>:3389Sign in with the regular account created by the installer. If the XRDP login screen displays a session selector, choose Xorg. Do not use the root account; the installer disables XRDP root login.
The first connection may take longer while Plasma creates the account’s configuration and caches. A successful login opens a normal Plasma desktop. Later connections normally reattach to the existing session for that user.
Redirect audio to the RDP client
Section titled “Redirect audio to the RDP client”Audio is carried through the RDP connection; the jail does not need access to the Sylve node’s sound devices. In a saved Remmina profile, set Sound to a Local option before connecting. Enable Redirect local microphone only when applications in the remote session should be allowed to use the client’s microphone.
From Konsole inside the Plasma session, run the checks as the regular desktop user, not as root and not from an SSH shell. PulseAudio belongs to the logged-in desktop user:
id -unpactl list short sinks | grep xrdppactl list short sources | grep xrdppaplay /usr/local/share/sounds/ocean/stereo/completion-success.ogaThe first command should print the desktop login name. The next two should show xrdp-sink and xrdp-source. If they show nothing and Plasma displays Dummy Output, or pactl info reports Default Sink: auto_null, log out of Plasma and reconnect. Disconnecting the RDP client leaves the existing session running and does not rerun the desktop’s audio-module autostart. To retry the loader without ending the session, run this as the desktop user from Konsole:
/usr/local/libexec/pulseaudio-module-xrdp/load_pa_modules.shRun Linux applications
Section titled “Run Linux applications”Complete the jail’s Linux configuration above and connect to Plasma before installing graphical Linux applications.
Use the Linux application helper
Section titled “Use the Linux application helper”Run the installed helper from Konsole as the regular Plasma user. Do not run it with doas or as root. It keeps each application under ~/.local/share/sylve-linux-apps, creates a wrapper under ~/.local/bin, and adds a Plasma launcher under ~/.local/share/applications. It does not download applications, enable FUSE, or create setuid files. The standalone helper source remains available for review.
Only install applications obtained from a source you trust. AppImage extraction runs the image’s embedded runtime as your desktop user, and installed applications can access that user’s files and network connections.
Install an AppImage
Section titled “Install an AppImage”The helper extracts a Type 2 AppImage once instead of trying to mount it with FUSE. Some AppImage runtimes identify themselves as generic System V ELF executables; the helper marks only its private extraction copy as Linux so FreeBSD can pass it to Linuxulator. The downloaded AppImage is not modified:
sylve-linux-app install \ --name Feishin \ --electron \ ~/Downloads/Feishin-linux-x86_64.AppImageFeishin should appear in Plasma’s application launcher, with its generated command at ~/.local/bin/feishin. Feishin uses Electron, so this example explicitly includes --electron; omit that option for applications that do not need it. The helper never enables reduced-sandbox mode implicitly. AppImages using an older format or requiring unsupported Linux kernel facilities may still fail. See AppImage’s documented extraction mode for the underlying mechanism.
Install a raw binary or application bundle
Section titled “Install a raw binary or application bundle”A standalone Linux executable needs only the source file:
sylve-linux-app install --name Example ~/Downloads/example-linux-x64For an application distributed as a directory, supply the directory followed by the executable’s relative path. This preserves the application’s libraries, resources, and helper programs:
sylve-linux-app install \ --name Feishin \ --electron \ ~/Downloads/Feishin-linux-x64 \ feishinThe helper also accepts .tar.xz, .tar.gz, .tar.bz2, .txz, .tgz, .tbz, and .zip archives directly. It uses FreeBSD’s base /usr/bin/tar, backed by libarchive, so these formats do not require another package. List the archive first to find its internal executable path, then pass that path as the final argument. For example, Feishin’s archive contains Feishin-linux-x64/feishin:
tar -tf ~/Downloads/Feishin-linux-x64.tar.xz | lesssylve-linux-app install \ --name Feishin \ --electron \ ~/Downloads/Feishin-linux-x64.tar.xz \ Feishin-linux-x64/feishinUse --icon ~/Downloads/example.png when a bundle does not contain a suitable top-level icon.
Ark is installed for normal desktop archive handling and supplies Dolphin’s Extract and Compress actions. The sylve-linux-app helper performs its own extraction so it also works from Konsole without opening Ark or Dolphin.
Manage installed applications with:
sylve-linux-app listsylve-linux-app install --replace --name Feishin --electron ~/Downloads/Feishin-linux-x86_64.AppImagesylve-linux-app remove feishinGraphical Linux applications launched from Plasma inherit its X11 session and the helper forces Mesa software rendering. Packages that require systemd services, Linux namespaces with a working sandbox, cgroups, kernel drivers, Snap, Flatpak, or Docker generally need a Linux virtual machine instead.
Troubleshoot a connection
Section titled “Troubleshoot a connection”If the RDP client cannot reach the jail, first confirm the jail address and listening socket:
ifconfigsockstat -4 -6 -l | grep ':3389'If the port is absent, inspect the service logs:
tail -n 100 /var/log/xrdp.logtail -n 100 /var/log/xrdp-sesman.logIf authentication succeeds but the display turns black, white, frozen, or repeatedly reconnects, compare the timestamps in all four logs:
tail -n 100 /var/log/xrdp.logtail -n 100 /var/log/xrdp-sesman.logtail -n 100 /home/desktop/.local/share/xorg/Xorg.*.logtail -n 100 /home/desktop/.local/share/xrdp/xrdp-chansrv.*.logxrdp.log covers the client connection and graphics negotiation, xrdp-sesman.log covers login and session lifetime, the Xorg log covers the xorgxrdp display, and the channel log covers clipboard, audio, and redirected devices. Replace desktop with the login name passed to the installer.
If the session and Xorg remain alive while a Remmina window becomes blank, disable Remmina’s Dynamic resolution update and reconnect using a fixed resolution. Remmina has an open report for black screens during dynamic resizing against XRDP. To capture the client side, fully exit Remmina, start it from the client terminal, reproduce the problem once, and then close it:
G_MESSAGES_DEBUG=remmina remmina 2>&1 | tee "$HOME/remmina-xrdp.log"Also confirm that /home/desktop/startwm.sh is executable, the jail still has SysV IPC (allow.sysvipc) enabled, and enough free memory is available. A locked account can be repaired from the console with:
ls -l /home/desktop/startwm.shpasswd desktopTo identify the active renderer, run glxinfo -B from Konsole inside the XRDP session. llvmpipe means software rendering is active; an Intel or AMD renderer means the optional iGPU path is working. With software rendering, high CPU use during animations, video, or graphics-heavy web pages does not necessarily indicate an XRDP fault.
After the desktop is working, restart the jail from Sylve during a maintenance window and repeat the service checks. Keep the pre-install snapshot until boot-time service startup and an RDP login have both been confirmed in your environment.