Skip to content

Instantly share code, notes, and snippets.

@tdcosta100
Last active December 21, 2024 16:23
Show Gist options
  • Save tdcosta100/385636cbae39fc8cd0937139e87b1c74 to your computer and use it in GitHub Desktop.
Save tdcosta100/385636cbae39fc8cd0937139e87b1c74 to your computer and use it in GitHub Desktop.
A tutorial to use GUI in WSL2 replacing original XServer by Xvnc, allowing WSL to work like native Linux, including login screen

WSL2 with GUI using Xvnc

Note

If you want to use pure WSLg, you can try the new WSLg (XWayland) tutorial or the WSLg (Wayland) tutorial.

In this tutorial, we will setup GUI in WSL2, and access it using VNC. No additional software outside WSL (like VcXsrv) is required, except, of course, a VNC Viewer (RealVNC, TightVNC, TigerVNC, UVNC, etc, all of them might work flawlessly).

The key component we need to install is the desktop metapackage you want (GNOME, KDE, Xfce, Budgie, etc) and tigervnc-standalone-server.

For this setup, I will use Ubuntu (20.04, 22.04 and 24.04 are working), and install GNOME Desktop. 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.

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
    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 \
    `[ ! -z "$(apt-cache search ^acpi-support$)" ] && echo "acpi-support-"` \
    tigervnc-standalone-server \
    ubuntu-desktop
    

    This will install the ubuntu-desktop and tigervnc-standalone-server, but excluding the acpi-support dependency, if included. If acpi-support is installed, it will render your distro almost unusable (see microsoft/WSL#10059), so we will tell apt to not install it.

    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:

    [Service]
    Type=oneshot
    ExecStart=-/usr/bin/umount /tmp/.X11-unix
    ExecStart=/usr/bin/rm -rf /tmp/.X11-unix
    ExecStart=/usr/bin/mkdir /tmp/.X11-unix
    ExecStart=/usr/bin/chmod 1777 /tmp/.X11-unix
    ExecStart=/usr/bin/ln -s /mnt/wslg/.X11-unix/X0 /tmp/.X11-unix/X0
    
    [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 edit the user-runtime-dir@.service service:

    sudo systemctl edit user-runtime-dir@.service
    
  6. Paste the code below in the editor:

    [Service]
    ExecStartPost=-/usr/bin/rm -f /run/user/%i/wayland-0 /run/user/%i/wayland-0.lock
    

Warning

Please read the editor instructions about the correct place to position the text cursor before pasting. If you paste the code in the wrong place, it will be discarded.

  1. Restart WSL using wsl.exe --shutdown, then reopen your distro terminal.

Creating VNC Server passwords

Important

In Debian, the GDM user is Debian-gdm, so you need to replace gdm by Debian-gdm in the commands below.

  1. In some distros, the directory /var/lib/gdm3/ has an incorrect owner, so let's fix it before setting the VNC Server passwords:

    sudo chown gdm:gdm /var/lib/gdm3/
    
  2. In this setup, each user has a different VNC password. So you have to configure at least three passwords, one for the current user, other for root, and other for GDM, who will present the login screen. If you don't configure the password, you won't able to access the login screen, or the user's desktop. First, let's configure the VNC password current user:

    vncpasswd
    
  3. Now, let's configure the VNC password for root (needed if you use LightDM instead GDM):

    sudo -H vncpasswd
    
  4. Finally, let's configure the VNC password for GDM (skip this step if you are not using GDM):

    sudo -H -u gdm vncpasswd
    

    You can repeat the process for other existing users.

Replacing default Xorg by Xvnc

By default, the display manager call multiple Xorg instances, one for each user session, including the login screen, provided by GDM. So we will replace Xorg script by a new version which calls Xvnc instead the classic Xorg. This is the real magic we are trying to do.

  1. First, let's backup the original Xorg script.

    sudo mv /usr/bin/Xorg /usr/bin/Xorg.original
    
  2. Then, we create a new Xorg script.

    sudo nano /usr/bin/Xorg.Xvnc
    
  3. Paste the code below in the editor:

    #!/bin/bash
    for arg do
      shift
      case $arg in
        # Xvnc doesn't support vtxx argument. So we convert to ttyxx instead
        vt*)
          set -- "$@" "${arg//vt/tty}"
          ;;
        # -keeptty is not supported at all by Xvnc
        -keeptty)
          ;;
        # -novtswitch is not supported at all by Xvnc
        -novtswitch)
          ;;
        # other arguments are kept intact
        *)
          set -- "$@" "$arg"
          ;;
      esac
    done
    
    # 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/Xvnc" ":${displayNumber}" "-geometry" "1024x768" "-rfbport" "$((5900 + $displayNumber))" "-rfbauth" "${HOME:-/root}/.vnc/passwd" "$@")
    
    systemd-cat -t /usr/bin/Xorg echo "Starting Xvnc:" "${command[@]}"
    
    exec "${command[@]}"
    

    Please note the resolution of the virtual screen. You can change that to fit your needs (1366x768, 1920x1080, etc). Also, you can change the -rfbauth option to point to a fixed location instead the home of current user, so you don't need to have a password for each user.

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

    sudo chmod 0755 /usr/bin/Xorg.Xvnc
    sudo ln -sf Xorg.Xvnc /usr/bin/Xorg
    

Warning

Sometimes, system updates replace Xorg link with the original version. Just repeat this step if this happens, and Xvnc will work again as Xorg replacement.

Running your distro with GUI enabled

Now you have everything ready to start. First we shut down WSL:

wsl.exe --shutdown

Then reopen your distro terminal.

Doing this is like booting Linux again, and GDM will start automatically, and will create a Xorg instance to display the login interface. We changed this process to make it create Xvnc instances, so we can access them. The first instance will listen to port 5901, the second instance will listen to port 5902, and so on.

Accessing the VNC screen

After a while (usually a few seconds, but it can take more if you don't have a SSD), you can test if it's working properly. Use your favorite VNC Viewer to connect to your localhost port 5901 (localhost:5901). Use the VNC password set for user gdm (or Debian-gdm, if in Debian). The login screen must appear.

After logging in, the screen will be blank and eventually will be closed automatically. This is because a new instance of Xvnc was created for user desktop, listening to port 5902. Connect to this port now. The logged user's desktop must appear. When you log out, the screen at port 5901 will show the login interface again. This applies to GDM (which is the case if you installed Ubuntu Desktop). 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]
    

If you are using LightDM, the desktop screen will appear in port 5901, so there's no need to connect to port 5902.

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.

Tips and tricks

  • VNC is a very adaptive protocol, and by default tries to use the most aggressive compression available, for better networking performance. But in our case, doing this only adds unnecessary CPU extra load, since you are connecting to localhost ("infinite" bandwith, near zero ping). To remove all compression algorithms and reduce lagging, force your VNC Viewer to connect using RAW encoding. The performance gain is noticeable, specially when playing videos (not a spectacular performance in this particular scenario, though).

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 Xvnc, 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 X by Xvnc 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 Xvnc output will be in the file /var/log/lightdm/x-0.log.

  5. If you can connect to 59XX ports, but receive an error like Authentication failure: No password configured for VNC Auth, the file $HOME/.vnc/passwd is missing for that particular user (on port 5901, the user is gdm). Try to repeat the steps described in section Creating VNC Server passwords and try to connect again.

  6. 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 shell again and repeat the steps of section Accessing the VNC screen.

Sample screenshots

GDM

GDM

LightDM

LightDM

GNOME

GNOME

KDE

KDE

Xfce

Xfce

Budgie Desktop

Budgie Desktop

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).

@Momossim
Copy link

Hi, @Momossim. The tutorial says user in Debian is Debian-gdm, not gdm. Try again with Debian-gdm and tell me if it works now.

hey. I followed the tutorial but when i type "sudo -H -u gdm vncpasswd" i get the errors: "sudo: unknown user gdm sudo: error initializing audit plugin sudoers_audit". do i need to create the user gdm ?

This error occurs because you need to paste the code in the right place. Follow the instructions that appear in the file you are editing.

i wiped my vm and redid the whole process but with gnome and it works now thanks

@executer9648
Copy link

Hi all ,hope you are doing well.
i followed the tutorial for a new wsl installation
in the gui i cant start a terminal window ,nothing happens.
another time when i installed snap and pressed the software update button the whole thing crashed
im trying to do this on ubuntu 20.4 as well as on 24.4.
any suggestions?

@MattM1114
Copy link

I'm sorry to getting so long to answer you. Please tell me what distro are using, and the version (for example, Ubuntu 22.04).

no problemo, im using Debian 12

@kchovi, unfortunately Debian 12 suffers from the same problem Ubuntu 22.04 does: it is so bonded to WSLg that it's somewhat broken with the setup I propose in this tutorial. So if you want to make it work like intended in this tutorial, you must use Debian 11.

I am running the newest form of ubuntu is there any way of fixing this

@tdcosta100
Copy link
Author

Hi, @MattM1114. I updated the tutorial recently. Did you make the steps from the scratch? What distro are you using?

@tdcosta100
Copy link
Author

Hi, @executer9648. Can you please run this command from a terminal and paste the result?

ls -la /run/user/`id -u`

@MattM1114
Copy link

MattM1114 commented Jul 20, 2024 via email

@executer9648
Copy link

Hi, @executer9648. Can you please run this command from a terminal and paste the result?

ls -la /run/user/`id -u`

sudo ls -la /run/user/id -u
[sudo] password for user:
total 12
drwx------ 3 root root 4096 Jul 23 21:30 .
drwxr-xr-x 4 root root 4096 Jul 23 21:32 ..
d--------- 3 user user 4096 Jul 23 21:30 inaccessible

@MattM1114
Copy link

MattM1114 commented Jul 24, 2024 via email

@executer9648
Copy link

Hi, @executer9648. Can you please run this command from a terminal and paste the result?

ls -la /run/user/`id -u`

sudo ls -la /run/user/id -u [sudo] password for user: total 12 drwx------ 3 root root 4096 Jul 23 21:30 . drwxr-xr-x 4 root root 4096 Jul 23 21:32 .. d--------- 3 user user 4096 Jul 23 21:30 inaccessible

i tried changing the permissions here, it didnt help.
however after installing lightdm and using that instead of gdm the gnome-terminal is working fine.
the only thing that leaves me uneasy is when doing the "check of updates" in the ubuntu gui" the whole vnc crashes and i have to reload the wsl

@tdcosta100
Copy link
Author

Hi, @executer9648. There is something wrong with your installation. If your gnome-terminal is not working properly, probably it's using WSLg instead your custom setup. What application do you use to check updates, so I can test it in my WSL?

@executer9648
Copy link

Hi, @executer9648. There is something wrong with your installation. If your gnome-terminal is not working properly, probably it's using WSLg instead your custom setup. What application do you use to check updates, so I can test it in my WSL?

to check updates i managed to reproduce it from inside the terminal in the vnc gui with this:"sudo update-manager"
regarding the gnome-terminal it started working after switching from gdm to lightdm

@tdcosta100
Copy link
Author

Try to use update-manager without sudo and then tell me if it works. Here it worked normally, without sudo.

@executer9648
Copy link

Hi @tdcosta100 i tried doing that command and the same crash happened on ubuntu 20.04 ,however , on 24.04 it works well.
regarding the terminal not opening in gdm, i found out why.
in the /etc/gdm3/custom.conf i used user "gdm" and after switching to "user" the terminal now works well.
the terminal issue and resolution is the same on 20.04 and 24.04

@tdcosta100
Copy link
Author

Nice to know you managed to fix your setup. Indeed, you cannot use gdm as a regular user, it was not intended to be used like that.

@tdcosta100
Copy link
Author

Hey guys, I created a brand-new tutorial which brings a similar experience (probably better), but not using Xvnc, just WSLg (XWayland). The link is at the beginning of this tutorial.

@nikhilbhave9
Copy link

nikhilbhave9 commented Aug 10, 2024

Hi, I went through your setup and it is working for me. I want to run a selenium-chrome instance in the gui, is that possible. And how would I do it?

Edit: Right now, it says chrome crashed and failed to start if I add: chrome_options.add_argument("--display=:1") # Use the VNC display

@tdcosta100
Copy link
Author

tdcosta100 commented Aug 10, 2024

You have to see the logs to know what's happening, but maybe the reason is the X11 authentication failing. Try to add a -ac switch to the Xvnc command in /usr/bin/Xorg_new, restart your WSL and see if it works now.

@jeffbrl
Copy link

jeffbrl commented Nov 22, 2024

I found this tutorial enormously helpful, and I thank the author. I was successful. I ran into one challenge with win11/debian bookworm. Installing the gdm3 package does not create /var/lib/gdm3/ which this tutorial references. I overcame this by switching to LightDM and following the author's instructions for it.

@ersanmaz
Copy link

ersanmaz commented Dec 8, 2024

Hello @tdcosta100. Thank you for this great guide. I ran into login issue when I enabled auto login in /etc/gdm3/custom.conf. Basically, I get below screen. Do you have any solution for that?

image

@tdcosta100
Copy link
Author

Hi, @ersanmaz. I could not reproduce your scenario. Could you please attach the log.txt generated by the following command?

sudo journalctl -b --no-pager > ~/log.txt

@ersanmaz
Copy link

ersanmaz commented Dec 8, 2024

Hi, @ersanmaz. I could not reproduce your scenario. Could you please attach the log.txt generated by the following command?

sudo journalctl -b --no-pager > ~/log.txt

Here is the log content. Log content deleted

@tdcosta100
Copy link
Author

Since I read your log, please, edit your post and erase this huge piece of text, so people don't need to roll this page a lot. I saw some interesting things, but I need more clarification: are you using the default tutorial setup, that is, did you install ubuntu-desktop on Ubuntu 24.04.1 WSL? Did you come from an older version of Ubuntu (for example, Ubuntu 24.04), or are you doing a fresh install?

@ersanmaz
Copy link

ersanmaz commented Dec 9, 2024

I installed ubuntu-gnome-desktop instead of ubuntu-desktop. Regarding the exact version, I am not sure. I installed it ~ a month ago. I guess it was Ubuntu 24.04.01

@tdcosta100
Copy link
Author

If possible, please try the tutorial using a fresh install using the ubuntu-desktop package and see if the problem goes away.

@ersanmaz
Copy link

ersanmaz commented Dec 9, 2024

I did the fresh install with ubuntu-desktop and still get the same issue.

I see below entries in journalctl

Dec 10 00:12:30 win-desktop gdm-autologin][536]: gkr-pam: no password is available for user
Dec 10 00:12:30 win-desktop gdm-autologin][536]: pam_unix(gdm-autologin:session): session opened for user ersan(uid=1000) by ersan(uid=0)
Dec 10 00:12:30 win-desktop gdm-autologin][536]: gkr-pam: couldn't unlock the login keyring.
Dec 10 00:12:32 win-desktop gdm-autologin][536]: pam_unix(gdm-autologin:session): session closed for user ersan
Dec 10 00:12:32 win-desktop gdm3[530]: Gdm: GdmDisplay: Session never registered, failing
Dec 10 00:12:32 win-desktop gdm-autologin][960]: gkr-pam: no password is available for user
Dec 10 00:12:32 win-desktop gdm-autologin][960]: pam_unix(gdm-autologin:session): session opened for user ersan(uid=1000) by ersan(uid=0)
Dec 10 00:12:32 win-desktop gdm-autologin][960]: gkr-pam: couldn't unlock the login keyring.

@tdcosta100
Copy link
Author

Yes, the problem continues. If you can post the entire log in a pastebin link, it would be easier to track where the real error is.

@ersanmaz
Copy link

Here is the whole log: https://pastebin.com/WJpRWTaV

@tdcosta100
Copy link
Author

I was able to reproduce your problem, and here I solved by removing the /run/user/1000/wayland-0* files. I don't know why these files were present, since they are supposed to be removed in the user-runtime-dir service.

@ersanmaz
Copy link

I manually deleted /run/user/1000/wayland-0* files but after each reboot, they are re-generated. I checked user-runtime-dir service in journalctl (journalctl -u user-runtime-dir@1000.service) and it runs ok. So, I assume this service runs before the service which generates those wayland files. As a workaround, I added some delay user-runtime-dir service and it worked. It's now like this:

[Service]
ExecStartPost=-/usr/bin/bash -c "sleep 5 && rm -f /run/user/%i/wayland-0 /run/user/%i/wayland-0.lock"

Thanks a lot @tdcosta100 for your help.

@STEFnr1
Copy link

STEFnr1 commented Dec 18, 2024

Heyyo, I followed the guide completely, however I am getting the following error when running the server (via vncserver command):
image
Before this happened, I saw that it needed to update some components, which I ignored, and completed setup.

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