Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
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

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 components we need to install are tigervnc-standalone-server and systemd-genie.

For this setup, I will use Ubuntu 20.04 LTS (Focal Fossa), 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.

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

sudo apt update
sudo apt upgrade

If you are trying to use Debian, you also will need:

sudo apt install curl wget

Now we are ready to go.

Installing components

Installing GUI

  1. Ubuntu has a handy component installer called tasksel, but it's not installed by default. So let's install it.

    sudo apt install tasksel
  2. Once we have it installed, let's run it.

    sudo tasksel
  3. At the package list, select your favorite GUI package. I selected Ubuntu Desktop. The installation will take a while, so be patient.

Installing VNC Server

Pretty easy, one command and you are done:

sudo apt install tigervnc-standalone-server

Installing dotnet-runtime

systemd-genie requires dotnet-runtime, but it ins't installed automatically. Follow the install instructions. Here are the commands to install dotnet-runtime-5.0 on Ubuntu 20.04 LTS (Focal Fossa):

wget -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update
sudo apt install dotnet-runtime-5.0

Installing systemd-genie

Now we will install systemd-genie, which is responsible for turning the minimalist WSL into a more complete Linux instance, with systemd and other related stuff. This is necessary to run GDM (GNOME Display Manager) and LightDM properly, giving the user a full graphic interface experience, with login and everything. Here are the install instructions. As of January 27th, 2021, these following commands are needed to install it (I gave up trying to use the repository method, since it stops working too often):

deb_name=`curl -s | grep name | grep deb | cut -d '"' -f 4`
sudo dpkg -i $deb_name
sudo apt --fix-broken install

The installation process is finally done.

Configuring the environment

Creating VNC Server passwords

  1. 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 whill 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:

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

    sudo -H vncpasswd
  3. Finally, let's configure the VNC password for GDM:

    sudo -H -u gdm vncpasswd

    Tip: In Debian, the GDM user is Debian-gdm.

    You can repeat the process for other existing users.

Replacing default X by Xvnc

By default, the display manager call multiple X 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 X/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_old
  2. Then, we create a new Xorg script.

    sudo nano /usr/bin/Xorg
  3. Paste the following content in the editor (remember, WSL shell uses CTRL + SHIFT + V for pasting, instead our traditional CTRL + V key combination):

    for arg do
      case $arg in
        # Xvnc doesn't support vtxx argument. So we convert to ttyxx instead
          set -- "$@" "${arg//vt/tty}"
        # -keeptty is not supported at all by Xvnc
        # -novtswitch is not supported at all by Xvnc
        # other arguments are kept intact
          set -- "$@" "$arg"
    # Here you can change or add options to fit your needs
    command=("/usr/bin/Xvnc" "-geometry" "1024x768" "-PasswordFile" "${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 -PasswordFile 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.

    Attention: sometimes, system updates replace Xorg custom script with the original version. Just repeat this entire section if this happens, and Xvnc will work again as Xorg replacement.

  4. Finally, we set the correct permissions for the file.

    sudo chmod 0755 /usr/bin/Xorg

Running systemd-genie

Finally, it's time to put everything together.

genie -i > /dev/null &
genie -s

Doing this is like booting Linux again, this time with systemd. Because of systemd, gdm will start automatically, and will create a X 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 5900, the second instance will listen to port 5901, and so on.

Accessing the VNC screen

After a while (usually 30 seconds, but it can take more if you don't have a SSD, maybe one minute or two), you can test if it's working properly. Use your favorite VNC Viewer to connect to your localhost port 5900. Use the VNC password set for user gdm. The login screen must appear.

After logging in, the screen will be blank. This is because a new instance of Xvnc was created for user desktop, listening to port 5901. Connect to this screen now. The logged user's desktop must appear. When you log out, the screen at port 5900 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:

     AutomaticLogin=[your username without the brackets]

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

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

  • If you are trying to use Debian, you need to do the following or some apps will not launch (at least in Buster):

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


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

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

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

  3. 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 5900, the user is gdm). Try to repeat the steps described in section Creating VNC Server passwords and try to connect again.

  4. If it still doesn't work, you can try to restart WSL. Open a Windows PowerShell command prompt, and execute the following command (don't forget to save everything that is unsaved before, because WSL will shut down completely):

    PS > wsl --terminate <your distro name>

    After that, open your distro shell again and repeat the steps of section Running systemd-genie.

Sample screenshots











Budgie Desktop

Budgie Desktop


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

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