Setting Up a Graphical Desktop Environment in WSL2
Did you know that with Windows Subsystem for Linux (WSL), WSL2 specifically, you can use the entire Linux environment right on your Windows machine? I will walk you through the requirements and steps needed to set up a WSL2 graphical desktop environment.
When can this be of use to software developers or engineers? There are several use-cases I can think of, but let’s just bring up general ones:
- Doing away with Dual Boot. Boot on Windows 10/11 and still have full access to your Linux with Graphical Environment.
- No resource (time and money) to build your own homelab using Proxmox or Unraid.
- On Windows but needing to support a team mate’s issue on Ubuntu and no resource (storage & RAM) for dual boot or run Hypervisor, VirtualBox, VMWare, QEMU, etc. - USB Live mode is too slow.
Or simply, out of curiosity - can I run Graphical Applications in Windows 10/11 via WSL2?
You can. Let’s get started!
Requirements
Windows 10/11 with WSL2 Installed
wsl --install
Linux Distribution (e.g., Ubuntu)
Install a Linux distribution from the Microsoft Store, such as Ubuntu.
X Server for Windows
Install an X server to handle the graphical display from WSL. A popular choice is:
The X Server is a core component of the X Window System, managing graphical displays, handling inputs, and providing services to graphical applications. It is a client-server architecture that is extensible making it a powerful tool for creating and managing graphical environments on Unix-like operating systems. Run in Windows, it enables WSL2 Linux distributions to display desktop and applications with graphical interfaces on Windows.
Firewall Configuration
Configure the Windows firewall to allow connections to the X server and PulseAudio server if needed.
Necessary Packages in WSL
Install the required packages in your WSL environment. Open your WSL terminal and run:
sudo apt update
and cautiously perform an upgrade
sudo apt upgrade
Once your system is set with the packages you know you absolutely need and have frozen those you deem essential, install the necessary packages
sudo apt install xauth dbus-x11 gnome-shell
Setting Up the Environment
Install and Configure X Server
Install VcXsrv and configure it to start automatically when Windows starts. Make sure it listens on the default display port :0
.
Install and Configure PulseAudio Server
Install a PulseAudio server on Windows. A recommended option is PulseAudio for Windows
.
- Download it from the official website.
- Follow the installation instructions provided.
Understanding the Script
The full script is available to be copied over and executed in your environment. But before proceeding, it helps to understands what each of the calls is doing. This is necessary so that in case the script doesn’t work for you, you’ll know the code block to check or troubleshoot from.
Host & Services
Host IP for X Server & PulseAudio
export HOST_IP=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}')
The Host IP forms the base for X Server and PulseAudio server address. We’re simply saving a copy now for efficient reuse later.
Default Login Mode
Changes the default login mode to a non-graphical target.
sudo systemctl set-default multi-user.target
The above command sets the Ubuntu in WSL2 to the following mode:
Non-Graphical Environment
The system starts in text-based multi-user mode without launching a graphical user interface (GUI). Users interact with the system through command line interfaces (CLI) or terminal sessions on the host machine or remote environment.
Service Initialization
Services required for a networked multi-user environment will be started. This typically includes network services, login services, and other essential background services that enable multiple users to log in and use the system.
Resource Usage
A non-graphical environment consumes fewer system resources (CPU, memory), which can be beneficial for systems with limited resources or occasional use of graphical environment only.
Suitability
This mode is suitable for servers, development environments, and systems where GUI is not necessary. It’s also useful for troubleshooting and recovery purposes when issues with the graphical environment arise.
Environment Variables for Ubuntu Desktop
XDG & GNOME Desktop Environment
Let’s setup “X Desktop Group” environment variables needed. XDG’s open standards and specifications ensure interoperability between various desktop environments and applications in Linux and Unix-like operating system.
This particular script sets things up for Ubuntu and GNOME.
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export XDG_SESSION_DESKTOP=ubuntu
export DESKTOP_SESSION=ubuntu
export GNOME_SHELL_SESSION_MODE=ubuntu
export XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
export XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop
export XDG_MENU_PREFIX=gnome-
export XDG_SESSION_TYPE=x11
export XDG_SESSION_CLASS=user
export GDK_BACKEND=x11
Graphics Library Configuration
Disables using Direct3D in the Mesa 3D graphics library.
export LIBGL_ALWAYS_SOFTWARE=1
There are still some issues with hardware accelerated rendering. For the time being, we’ll use software rendering until the issues are resolved or stable.
.Xauthority Generation
Password Placeholder
From hereon, {PASSWORD}
should be interpreted as the literal password. You need to replace that string in the script with your own or modify several portions of the script using the placeholder.
There are several ways to do it but I leave that to you as an exercise. The modification you make for this part will also tell you your risk tolerance.
The Generator
A function to generate and set up the .Xauthority file, which is necessary for authenticating X sessions.
# Generate .Xauthority
generate_xauthority() {
local magiccookie=$(echo '{PASSWORD}' | tr -d '\n\r' | md5sum | awk '{print $1}')
xauth add "$DISPLAY" . "$magiccookie"
local userprofile="$(wslpath $(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null))"
if [ -f "$HOME/.Xauthority" ]; then
cp "$HOME/.Xauthority" "$userprofile"
echo "copied $HOME/.Xauthority to $userprofile"
echo $magiccookie
else
echo "Unable to find $HOME/.Xauthority"
fi
}
generate_xauthority
When running a graphical desktop environment in WSL2, the .Xauthority
file plays a crucial role in enabling secure communication between Linux GUI applications and an X server running on Windows. The setup script above generates and configures this file to ensure that the graphical applications can authenticate correctly with the X server.
The above script creates a new one and overwrites the previously copied .Xauthority
to your Windows’ user profile directory. If you wish to run multiple GUI Linux on WSL2, you should modify the script to merge instead of overwrite.
Runtime Directory Permissions
Ensures that the runtime directory is properly set up and has the correct permissions.
# Extra check to synchronize runtime dir and ensure correct permissions export XDG_RUNTIME_DIR=/run/user/$(id -u)
set_xdg_dir_permissions() {
echo $XDG_RUNTIME_DIR
if [ ! -d "$XDG_RUNTIME_DIR" ]; then
sudo mkdir $XDG_RUNTIME_DIR
echo "Created user runtime directories"
fi
sudo chmod 700 $XDG_RUNTIME_DIR && sudo chown $(id -un):$(id -gn) $XDG_RUNTIME_DIR
}
set_xdg_dir_permissions
DBUS Session Setup
Configures the DBUS session bus, which is necessary for many desktop applications to communicate with each other.
set_session_dbus() {
local bus_file_path="$XDG_RUNTIME_DIR/bus"
sudo chown -Rf $(id -un):$(id -gn) $XDG_RUNTIME_DIR
export DBUS_SESSION_BUS_ADDRESS=unix:path=$bus_file_path
if [ ! -e "$bus_file_path" ]; then
/usr/bin/dbus-daemon --session --address=$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &
fi
}
set_session_dbus
PulseAudio Configuration
Configures the PulseAudio server to enable sound.
echo "tcp:$HOST_IP"
export PULSE_SERVER="tcp:$HOST_IP"
Run the Custom Script
Copy the custom script provided below into a file, for example, setup_wsl_desktop.sh
, and save it in your Ubuntu home directory.
# Host IP
export HOST_IP=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}')
# Initialize needed variables
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
export LIBGL_ALWAYS_INDIRECT=0
# Change default login mode
sudo systemctl set-default multi-user.target
# Setting up essential environment variables for Ubuntu desktop
# Ubuntu default desktop (GNOME Shell variant)
# https://wiki.gnome.org/Projects/GnomeShell
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export XDG_SESSION_DESKTOP=ubuntu
export DESKTOP_SESSION=ubuntu
export GNOME_SHELL_SESSION_MODE=ubuntu
# Commonly referenced environment variables for X11 sessions
# https://specifications.freedesktop.org/basedir-spec/latest/
export XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
export XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop
export XDG_MENU_PREFIX=gnome-
export XDG_SESSION_TYPE=x11
export XDG_SESSION_CLASS=user
export GDK_BACKEND=x11
# Disable using Direct3D in Mesa 3D graphics library
export LIBGL_ALWAYS_SOFTWARE=1
# Generate .Xauthority
generate_xauthority() {
local magiccookie=$(echo '{PASSWORD}' | tr -d '\n\r' | md5sum | awk '{print $1}')
xauth add "$DISPLAY" . "$magiccookie"
local userprofile="$(wslpath $(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null))"
if [ -f "$HOME/.Xauthority" ]; then
cp "$HOME/.Xauthority" "$userprofile"
echo "copied $HOME/.Xauthority to $userprofile"
echo $magiccookie
else
echo "Unable to find $HOME/.Xauthority"
fi
}
generate_xauthority
# Extra check to synchronize runtime dir and ensure correct permissions export XDG_RUNTIME_DIR=/run/user/$(id -u)
set_xdg_dir_permissions() {
echo $XDG_RUNTIME_DIR
if [ ! -d "$XDG_RUNTIME_DIR" ]; then
sudo mkdir $XDG_RUNTIME_DIR
echo "Created user runtime directories"
fi
sudo chmod 700 $XDG_RUNTIME_DIR && sudo chown $(id -un):$(id -gn) $XDG_RUNTIME_DIR
}
set_xdg_dir_permissions
set_session_dbus() {
local bus_file_path="$XDG_RUNTIME_DIR/bus"
sudo chown -Rf $(id -un):$(id -gn) $XDG_RUNTIME_DIR
export DBUS_SESSION_BUS_ADDRESS=unix:path=$bus_file_path
if [ ! -e "$bus_file_path" ]; then
/usr/bin/dbus-daemon --session --address=$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &
fi
}
set_session_dbus
# pulseaudio
echo "tcp:$HOST_IP"
export PULSE_SERVER="tcp:$HOST_IP"`
Running X Server on Windows & GNOME Session
Execute the Script
In your Ubuntu WSL, make the script executable and run it.
chmod +x setup_wsl_desktop.sh
./setup_wsl_desktop.sh
If there are no errors, the environment is ready.
You may also add the script to your .bashrc
to setup the environment everytime you login to your Terminal.
echo "./setup_wsl_desktop.sh" >> ~/.bashrc
Run GNOME Session
You can now start gnome-session
to start sending data to the X Server in Windows.
gnome-session
or
gnome-session &
if you want to reuse the terminal and run gnome-session
in the background.
In either case, you need to make sure the terminal that started gnome-session is not closed or terminated.
Windows X Server
If you configured X Server in Windows 10/11 as written here then all you have to do is bring to foreground the X Window :0
or whichever display you set it, as the profile may already be running in the background. If not; configure, save a profile, and run the profile.
PulseAudio Server
Understand that if you do not have this running as a service in Windows 10/11, you will not have audio in your Ubuntu Graphical Environment through Windows X Server.
If you do not have this as a service, run this before you start gnome-session
.
Are you curious?
Will this run in Ubuntu through Docker, in Apple Silicon with XQuartz?
I hope that with the above breakdown of the provided script, I’ve accelerated your set up of a functioning graphical desktop environment in WSL2, which you can interact smoothly through the Windows host.