Skip to content

Instantly share code, notes, and snippets.

@tdcosta100
Last active December 5, 2025 13:41
Show Gist options
  • Select an option

  • Save tdcosta100/e28636c216515ca88d1f2e7a2e188912 to your computer and use it in GitHub Desktop.

Select an option

Save tdcosta100/e28636c216515ca88d1f2e7a2e188912 to your computer and use it in GitHub Desktop.
A tutorial to use GUI in WSL2/WSLg replacing original Xorg by Xwayland, allowing WSL to work like native Linux, including login screen

Full desktop shell in WSL2 using WSLg (XWayland)

Note

If you want to use Wayland in WSLg in a simpler setup, you can try the WSLg (Wayland) tutorial.

In this tutorial, we will setup GUI in WSL2. No additional software outside WSL (like VcXsrv or GWSL) is required. You will find this tutorial very similar to the one that replaces Xorg with Xvnc. Indeed, it's pretty much the same tutorial, with some few changes.

The key component we need to install is the desktop metapackage you want (GNOME, KDE, Xfce, Budgie, etc), and after that, replace the default Xorg by a script that calls Xwayland instead.

For this setup, I will use Ubuntu 24.04, and install GNOME Desktop. Unfortunately older versions of Ubuntu lack some fundamental things, so we cannot reproduce it in older versions (at least not fully). Since the key components aren't bound to Ubuntu or GNOME, you can use your favorite distro and GUI. Check the Sample screenshots section for examples.

So let's go. First, we need a working WSL2 installation.

Warning

WSLg may not work as expected, since Wayland sockets are disabled for everyone, and not every app can handle this. But if you want to use Wayland apps natively, you can use the command export XDG_RUNTIME_DIR=$HOME/runtime-dir and then start your WSLg app.

Before going to real business, let's make sure we are updated.

sudo apt update
sudo apt upgrade

You also need to make sure /etc/wsl.conf have the following lines:

[boot]
systemd=true

If not, create/edit this file, add these lines and restart WSL (for example, using wsl.exe --shutdown, then reopening the distro terminal).

Now we are ready to go.

Installing components

Installing GUI

  1. First you select your favorite desktop environment metapackage. Here is a list of the most common metapackages:

    DistroDesktop EnvironmentMetapackage
    UbuntuBudgieubuntu-budgie-desktop (currently very buggy, I don't recommend using it)
    GNOMEubuntu-desktop
    KDEkubuntu-desktop
    Kylinubuntukylin-desktop
    LXDElubuntu-desktop
    MATEubuntu-mate-desktop
    Studioubuntustudio-desktop
    Unityubuntu-unity-desktop
    Xfcexubuntu-desktop
    Ubuntu/DebianCinnamontask-cinnamon-desktop
    GNOMEtask-gnome-desktop
    GNOME Flashbacktask-gnome-flashback-desktop
    KDE Plasmatask-kde-desktop
    LXDEtask-lxde-desktop
    LXQttask-lxqt-desktop
    MATEtask-mate-desktop
    Xfcetask-xfce-desktop
  2. Once you have chosen the metapackage, let's install it. For example, if you choose ubuntu-desktop, the command will be:

    sudo apt install ubuntu-desktop xwayland
    

    This will install the ubuntu-desktop and xwayland (if not already included as dependency for your metapackage). The installation will take a while, so be patient.

  3. If in Ubuntu, you may want to install snap-store. If you don't need it, you can skip this step:

    sudo snap install snap-store
    

Configuring the environment

If you are using Debian, you need to configure the locale (this is not needed in Ubuntu):

echo "LANG=en_US.UTF-8" | sudo tee -a /etc/default/locale

Create and modify services

  1. Now we have everything installed, we need to fix the directory /tmp/.X11-unix/, because it's mounted as read-only by default. We will create a new systemd unit:

    sudo systemctl edit --full --force wslg-fix.service
    
  2. Paste the code below in the editor:

    [Unit]
    After=wslg.service
    
    [Service]
    Type=oneshot
    ExecStart=-/usr/bin/umount /tmp/.X11-unix
    ExecStart=/usr/bin/rm -rf /tmp/.X11-unix
    ExecStart=/usr/bin/mkdir -m 1777 /tmp/.X11-unix
    ExecStart=/usr/bin/ln -s /mnt/wslg/.X11-unix/X0 /tmp/.   X11-unix/X0
    ExecStart=/usr/bin/chmod 0666 /mnt/wslg/runtime-dir/wayland-0.   lock
    
    [Install]
    WantedBy=multi-user.target
    
  3. Exit the editor saving the changes to the file.

  4. Let's enable wslg-fix.service:

    sudo systemctl enable wslg-fix.service
    
  5. We also need to remove all references to Wayland, because if not, some apps (gnome-terminal, for example) will open outside the desktop shell. We will mask the wslg-session service:

    sudo ln -s /dev/null /etc/systemd/user/wslg-session.service
    
  6. We must disable the acceleration check, because gnome-session will refuse to start:

    sudo sed -i 's/gnome-session-binary/gnome-session-binary --disable-acceleration-check/' /usr/bin/gnome-session
    
  7. Now we will change the default startup target, because if not, a shell window will appear everytime you start your distro (for example, opening a Terminal of your distro in Windows).

    sudo systemctl set-default multi-user.target
    

Replacing default Xorg by XWayland

By default, the display manager call multiple Xorg instances, one for each user session, including the login screen, provided by GDM (if you are using the GDM as your display manager, of course). So we will replace Xorg script by a new version which calls Xwayland instead the classic Xorg. This is the real magic we are trying to do.

  1. First, we create a new Xorg script.

    sudo nano /usr/bin/Xorg.Xwayland
    
  2. Paste the code below in the editor:

    #!/bin/bash
    for arg do
      shift
      case $arg in
        # Xwayland doesn't support vtxx argument. So we convert to ttyxx instead
        vt*)
          set -- "$@" "${arg//vt/tty}"
          ;;
        # -keeptty is not supported at all by Xwayland
        -keeptty)
          ;;
        # -novtswitch is not supported at all by Xwayland
        -novtswitch)
          ;;
        # other arguments are kept intact
        *)
          set -- "$@" "$arg"
          ;;
      esac
    done
    
    # Check if the runtime dir is present, and create it if not
    if [ ! -d $HOME/runtime-dir ]
    then
     mkdir $HOME/runtime-dir
     ln -s /mnt/wslg/runtime-dir/wayland-0 /mnt/wslg/runtime-dir/wayland-0.lock $HOME/runtime-dir/
    fi
    
    # Point the XDG_RUNTIME_DIR variable to $HOME/runtime-dir
    export XDG_RUNTIME_DIR=$HOME/runtime-dir
    
    # Find an available display number
    for displayNumber in $(seq 1 100)
    do
      [ ! -e /tmp/.X11-unix/X$displayNumber ] && break
    done
    
    # Here you can change or add options to fit your needs
    command=("/usr/bin/Xwayland" ":${displayNumber}" "-geometry" "1920x1080" "-fullscreen" "$@")
    
    systemd-cat -t /usr/bin/Xorg echo "Starting Xwayland:" "${command[@]}"
    
    exec "${command[@]}"
    

    Please note the resolution of the virtual screen. You can change that to fit your needs (1366x768, 3840x2160, etc).

  3. Exit the editor saving the changes.

  4. Finally, we set the correct permissions for the file and create a link to it after renaming the original Xorg:

    sudo chmod 0755 /usr/bin/Xorg.Xwayland
    sudo dpkg-divert --local --add --rename /usr/bin/Xorg
    sudo update-alternatives --install /usr/bin/Xorg Xorg /usr/bin/Xorg.Xwayland 100
    

Configuring the monitor resolution under GDM and GNOME

Currently, one of the annoying things is the resolution of Xwayland. Even with the -geometry switch, GDM and GNOME don't not respect it. Fortunately, this can be overriden by creating a monitors.xml file. Let's do it then.

  1. First, we create it in the current user directory:

    mkdir ~/.config
    nano ~/.config/monitors.xml
    
  2. Paste the code below in the editor (here it is configured for a 1920x1080 resolution, so change it to reflect your resolution if necessary):

    <monitors version="2">
      <configuration>
        <logicalmonitor>
          <x>0</x>
          <y>0</y>
          <scale>1</scale>
          <primary>yes</primary>
          <monitor>
            <monitorspec>
              <connector>XWAYLAND0</connector>
              <vendor>unknown</vendor>
              <product>unknown</product>
              <serial>unknown</serial>
            </monitorspec>
            <mode>
              <width>1920</width>
              <height>1080</height>
              <rate>59.963</rate>
            </mode>
          </monitor>
        </logicalmonitor>
      </configuration>
    </monitors>
  3. Exit the editor saving the changes to the file.

  4. Now let's copy this file to GDM's home directory:

    sudo mkdir /var/lib/gdm3/.config
    sudo cp ~/.config/monitors.xml /var/lib/gdm3/.config/
    
  5. Finally, we set the correct permissions to the monitors.xml of GDM user:

    sudo chown -R gdm:gdm /var/lib/gdm3/.config/
    
  6. Restart WSL using wsl.exe --shutdown, then reopen your distro terminal.

Running your distro with GUI enabled

Now you have everything ready to start. Just do the following command:

sudo systemctl start graphical.target

After a while (usually a few seconds, but it can take more if you don't have a SSD), the login screen must appear.

After logging in, the logged user's desktop must appear. When you log out, the screen will show the login interface again. This applies to GDM (which is the case if you installed Ubuntu Desktop). In GDM, you can change this behavior changing the configuration file like this:

  1. sudo nano /etc/gdm3/custom.conf

  2. Uncomment and edit the following lines:

     AutomaticLoginEnable=true
     AutomaticLogin=[your username without the brackets]
    

Shutting down

One important thing is: once you start your WSL instance, you cannot just stop it. You must perform a standard Linux shutdown. You can do one of the alternatives below:

  • Power off option on GUI menu
  • sudo poweroff

After doing that, you can safely shut down your WSL instance, either by wsl.exe --terminate or wsl.exe --shutdown. Not doing the shutdown process may cause damage to your WSL instance. So be careful.

Troubleshooting

  1. If it doesn't work at first, try to check your journalctl logs:

    journalctl -b -t /usr/lib/gdm3/gdm-x-session -t /usr/bin/Xorg --no-pager
    

    If you are using Debian, then the command is:

    journalctl -b -t /usr/libexec/gdm-x-session -t /usr/bin/Xorg --no-pager
    

    In the output, you must see what command line was generated for Xwayland, and which error messages appear. Of course, even if it works correctly, you can check the logs just to see what is happening, or for debugging.

  2. You must check if the custom Xorg script was not replaced by the default version of it. If it was the case, just repeat the steps of Replacing default Xorg by Xwayland section.

  3. Check if Xorg is your default display server, not Xephyr or Wayland. If it's not, you must change it to have Xorg as your default display server.

  4. If you are using LightDM, you also need to check logs at /var/log/lightdm (you will need to use sudo to cat files in that directory). The Xwayland output will be in the file /var/log/lightdm/x-0.log.

  5. If it still doesn't work, you can try to restart WSL with wsl.exe --shutdown (don't forget to save everything that is unsaved before, because WSL will shut down completely), then open your distro terminal again and repeat the steps of section Running your distro with GUI enabled.

Sample screenshots

GDM

GDM

LightDM (Kubuntu)

LightDM

GNOME

GNOME

KDE (Kubuntu)

KDE

LXDE (Lubuntu)

LXDE

Xfce (Xubuntu)

Xfce

Contributors

Thanks to this guys, whose feedback made this tutorial reach the current level of quality and completeness (and it will be more and more complete as more feedback is given).

@pentaly7
Copy link

pentaly7 commented Nov 15, 2025

i am using arch linux with Full KDE Plasma desktop installed, i have follow all of your steps with using sddm.
but when i start sudo systemctl start graphical.target nothing happen.

but when i start with startx it start the desktop but all of the component is in individual window, not inside the the Xwayland desktop. do you have solution for this?

edit :
i success by running it using this script

X &

sleep 2

DISPLAY=:1 WAYLAND_DISPLAY= startplasma-x11

@ienwb
Copy link

ienwb commented Nov 15, 2025

@lingmingxi
Is downgrade possible without reinstall in wsl, how?

edit
I got the same error, latest wsl version.

@tdcosta100
Copy link
Author

Hi guys. I updated the tutorial for the last version of WSL. Now we have to mask the user service wslg-session.service and add the --disable-acceleration-check switch to the gnome-session command.

@ienwb
Copy link

ienwb commented Nov 17, 2025

Any plan to add more* DE tutorial besides ubuntu gnome, e.g., Kubuntu? IK arch wiki has some of the display manager info but it warrants a lot of self research and time. Take your time, im currently on a new kubuntu install, my ubuntu gnome has some errors from wsl first try. Thank you for the update.

Edit:
Spellings

Edit 2:
I might make a feature request of this guide into a wsl command by posting the issue in wsl github repo, or posting it to each DE flavor support team eg kubuntu repo issue, GNOME, xfce. If anyone got the time feel free to make a feature request out of this. This guide with GPU pass-through if m$ ever implemented it would mean WSL is close to r/linux_gaming specification. GPU pass-through and the year of linux DE on wsl.

@tdcosta100
Copy link
Author

Currently I'm overwhelmed by my work demands, so I cannot promise anything but the maintenance of this tutorial for now. But I want to add more supporting DEs as much as possible.

While I was testing the tutorial (yes, I recreate everything from scratch to guarantee everything is fine) I faced some errors too. Some gnome-shell services are not correctly detected as active, maybe caused by a race condition. Anyway, I started my PC now and while I'm writing this message, everything is working as intended, so maybe for others this will happen too.

Since I created this tutorial, I think how beneficial would be a better architecture for WSLg, where the GPU were presented as a device inside WSL, and on the Windows side it would be possible to choose wether I want separate windows, or surfaces for each virtual screen, allowing Desktop Environments to be present.

The way it is set now, we are seeing through RDP connections. This is very slow compared to direct rendering, so it's a huge bottleneck. Maybe a redesign will come in the future, I hope so.

@ienwb
Copy link

ienwb commented Nov 19, 2025

I'm gonna report this as feature request to Microsoft, and other DE companies, eg kubuntu team, ubuntu gnome team, so that the workload are shared between company and community devs. after i got more free time, currently busy on other paper.
Thanks for your continuous support on updating this.πŸ™πŸ½πŸ™πŸ½πŸ™πŸ½ @tdcosta100
this is basically a pioneer on WSL as GUI desktop environment besides the default server usage.

as to not burden you anymore, im gonna reinstall with gnome ubuntu, kde could wait, take your time king.

edit 1:
yeah linux philosophy if implemented fully would mean, gpu passthrough means the gpu is presented as physical device or could be accessed from wsl. the way i see this atm, only app like CUDA has accel, other apps dont have accel.

@Kyant0
Copy link

Kyant0 commented Nov 23, 2025

Not work

@priamfive
Copy link

This now works again for me - many thanks for all your work.

@krad213
Copy link

krad213 commented Dec 3, 2025

Sound with ubuntu-desktop doesn't work (it works when I start GUI apps from WLS CLI), is there any way to fi this, or maybe there is other environment where it works ?

@tdcosta100
Copy link
Author

There are some suggested solutions, but unfortunately none of them are very effective for now. Yet, I accept suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment