Skip to content

Instantly share code, notes, and snippets.

@onomatopellan
Last active September 15, 2025 23:48
Show Gist options
  • Save onomatopellan/c5220c0efddaff69aaff77cca80b7b8e to your computer and use it in GitHub Desktop.
Save onomatopellan/c5220c0efddaff69aaff77cca80b7b8e to your computer and use it in GitHub Desktop.
Waydroid in WSL2 with sound (Weston on top of Weston approach)

Waydroid in WSL2 with sound

Requirements

Recommended to install Waydroid in a brand new Ubuntu 25.04 install. Waydroid needs a custom linux kernel. Actually just needs latest kernel for WSL2 with just these changes before compiling:

CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"

Make sure WSL2 uses the custom kernel and modules modifying .wslconfig (or using WSL Settings)

[wsl2]
kernel=D:\\mykernel\\bzImage-6.6.87.ANDROID
kernelModules=D:\\mykernel\\modules.vhdx
Compiling the 6.6.x kernel: Step-by-step guide Instructions for building an x86_64 WSL2 6.6.x kernel with an Ubuntu distribution using bash are as follows:
  • Install the build dependencies:
    sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev cpio qemu-utils

  • Download de kernel (always in a /home/user/ directory)
    git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1

  • Change directory to kernel's directory root
    cd WSL2-Linux-Kernel

  • Modify WSL2 kernel configs:
    make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl

Device Drivers --> Android --> Select "Android Binder IPC Driver" -> Save

  • Build the kernel using the WSL2 kernel configuration and put the modules in a modules folder under the current working directory:
    make -j$(nproc) KCONFIG_CONFIG=Microsoft/config-wsl && make INSTALL_MOD_PATH="$PWD/modules" modules_install

  • Strip symbols from modules (skip this step if you are insterested in debugging kernel's memory dumps)
    find ./modules/lib/modules/$(make -s kernelrelease) -name '*.ko' -exec strip --strip-unneeded {} \;

  • Then, you can use a provided script to create a VHDX containing the modules:
    sudo ./Microsoft/scripts/gen_modules_vhdx.sh "$PWD/modules" $(make -s kernelrelease) modules.vhdx

  • Copy de generated kernel and modules to the desired folder
    cp arch/x86/boot/bzImage /mnt/d/mykernel/bzImage-6.6.87.2.ANDROID
    cp modules.vhdx /mnt/d/mykernel/modules.ANDROID.vhdx

  • Open WSL Settings -> Developer -> Select Custom kernel, Custom modules

  • Shutdown WSL2 VM
    wsl.exe --shutdown

Setup

Download ubuntu-25.04-wsl-amd64.wsl distro from https://releases.ubuntu.com/plucky/

Double click on the .wsl file to install.

Run Ubuntu 25.04 and make sure is updated

sudo apt update && sudo apt upgrade -y

Install weston

sudo apt install weston

Install Waydroid

curl -s https://repo.waydro.id | sudo bash
sudo apt install waydroid

Configure Waydroid to only use software rendering (SwiftShader)

sudo nano /var/lib/waydroid/waydroid_base.prop

    ro.hardware.gralloc=default
    ro.hardware.egl=swiftshader

Make weston detect gpu for some acceleration

export GALLIUM_DRIVER=d3d12

Run weston

weston --backend=wayland-backend.so 

Inside weston desktop shell open a terminal and run

waydroid session start

When it shows 'Android with user 0 is ready' then open another terminal inside the weston desktop and run

waydroid show-full-ui

Tips:

  • If Waydroid shows some weird dbus errors make sure you shutdown the WSL2 VM (wsl.exe --shutdown) and try again.

  • By default Waydroid only launches correctly if the WSL2 networking mode is not MIRRORED. This is because dnsmasq tries to reserve port 53 that is already used in Windows. If you want to keep using MIRRORED networking you will need to add this to your .wslconfig (or using WSL Settings)

    [experimental]
    ignoredPorts=53
    
  • Every time the WSL2 kernel changes significantly (from ASHMEM support in 5.15.x to none in 6.6.x) you need to reconfigure Waydroid or it wont boot.

      sudo waydroid upgrade --offline
    
@onomatopellan
Copy link
Author

onomatopellan commented Jun 2, 2025

@Timbo303 Did you try the Weston on Weston approach? Since WSLg uses Weston as a Wayland compositor you can run another Weston window where you can launch Waydroid. You won't need the full Ubuntu desktop and sounds works inside the Weston desktop shell.
I made a quick guide here https://gist.github.com/onomatopellan/c5220c0efddaff69aaff77cca80b7b8e

I tried to follow your instructions. However weston doesn't start up for some reason giving me this error:

Error: Failed to connect to parent Wayland compositor: No such file or directory display option: (none), WAYLAND_DISPLAY=wayland-0 [18:39:48.523] fatal: failed to create compositor backend

The correct way to run it is weston --backend=wayland-backend.so or just weston

But it seems some env variable is not defined.

What's the content of your env variables? Run env and see if you have something like these:

SHELL=/bin/bash
WSL2_GUI_APPS_ENABLED=1
WSL_DISTRO_NAME=Ubuntu-25.04
GALLIUM_DRIVER=d3d12
WAYLAND_DISPLAY=wayland-0
DISPLAY=:0
XDG_RUNTIME_DIR=/run/user/1000/
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
HOSTTYPE=x86_64
PULSE_SERVER=unix:/mnt/wslg/PulseServer

ls -la /run/user/1000/ should show the wayland-0 socket

$ ls -la /run/user/1000/
total 0
drwxr-xr-x 6 onoma onoma 260 Jun  2 11:37 ./
drwxr-xr-x 3 root  root   60 Jun  2 11:36 ../
srw-rw-rw- 1 onoma onoma   0 Jun  2 11:36 bus=
drwxr-xr-x 4 onoma onoma  80 Jun  2 11:37 dbus-1/
drwx------ 2 onoma onoma 160 Jun  2 11:36 gnupg/
srw-rw-rw- 1 onoma onoma   0 Jun  2 11:36 pk-debconf-socket=
drwxr-xr-x 2 onoma onoma  80 Jun  2 11:36 pulse/
srw-rw-rw- 1 onoma onoma   0 Jun  2 11:36 snapd-session-agent.socket=
drwxr-xr-x 6 onoma onoma 160 Jun  2 11:36 systemd/
lrwxrwxrwx 1 root  root   31 Jun  2 11:36 wayland-0 -> /mnt/wslg/runtime-dir/wayland-0=
lrwxrwxrwx 1 root  root   36 Jun  2 11:36 wayland-0.lock -> /mnt/wslg/runtime-dir/wayland-0.lock
srwxr-xr-x 1 onoma onoma   0 Jun  2 11:37 wayland-1=
-rw-r----- 1 onoma onoma   0 Jun  2 11:37 wayland-1.lock

Also make sure you have latest WSL version 2.5.7 (wsl.exe --version) and your .wslconfig file loads the generated kernel modules too:

[wsl2]
kernel=D:\\mykernel\\bzImage-6.6.87.ANDROID
kernelModules=D:\\mykernel\\modules.vhdx

@Timbo303
Copy link

Timbo303 commented Jun 2, 2025

You are a Pure Genius. I don't know how this wasn't figured out sooner these instructions are way easier. I got minecraft for android running at 1-2 fps (I expected minecraft to not work. Cut the rope works but with audio issues). I will make sure to update everyone and give you credit.

Screenshot 2025-06-02 125730

@onomatopellan
Copy link
Author

You are welcome. And yes, right now Waydroid only works with software rendering. I'm looking at the Lineage image Waydroid uses and their mesa actually is compiled with d3d12 support. That means Waydroid should have gpu acceleration in WSL2 through mesa but it doesn't work. I guess the problem is Waydroid is inside a container so it can't access /lib/wsl/lib and lib/wsl/drivers.

@onomatopellan
Copy link
Author

onomatopellan commented Jun 7, 2025

Well, I think it's not possible for now. I just compiled a vendor.img with mesa and it never enables d3d12. Inside Waydroid every binary and lib are android binaries that normally won't be ready for read external linux libraries.

In order to get gpu acceleration (OpenGL to D3D12) in Waydroid we would need:

  1. compiled android mesa with d3d12 gallium support enabled.
  2. compiled android d3d libraries from /usr/lib/wsl/lib
  3. compiled android libraries from /usr/lib/wsl/drivers

Step 1 is possible but 2 only can happen if Microsoft opensources libd3d12.so, libd3d12core.so and libdxcore.so. so we can compile them for android. Step 3 would need every gpu vendor compiling their drivers to android and bundle it in the windows drivers package. (like they already made for linux binaries)

@soybeans-generator
Copy link

@onomatopellan
Copy link
Author

@soybeans-generator Thanks for the link, it's a very nice project. Unfortunately it still has same problem as this gist, it only works with software rendering (swiftshader). In a real linux it uses gpu acceleration because mesa is able to access to the real gpu. In WSL2 we only get a gpu abstraction (/dev/dxg) so we would need all the android versions of the libraries from my previous post.

@PEMessage
Copy link

PEMessage commented Jul 17, 2025

For those who encounter

failed to connect to parent wayland compositor: no such file or directory display option: (none), wayland_display=wayland-0

You may need this

microsoft/WSL#11261

using workaround this post provided but need a slide tweak (we may also link pulse dir into XDG_RUNTIME_DIR)

The script I used are list below

#!/bin/bash
set -x
mkdir -p ~/.config/systemd/user
cat  <<EOF > ~/.config/systemd/user/symlink-wayland-socket.service
[Unit]
Description=Symlink Wayland socket to XDG_RUNTIME_DIR

[Service]
Type=oneshot
ExecStart=/usr/bin/ln -s /mnt/wslg/runtime-dir/wayland-0      \$XDG_RUNTIME_DIR
ExecStart=/usr/bin/ln -s /mnt/wslg/runtime-dir/wayland-0.lock \$XDG_RUNTIME_DIR
ExecStart=/usr/bin/ln -s /mnt/wslg/runtime-dir/pulse \$XDG_RUNTIME_DIR

[Install]
WantedBy=default.target
EOF


systemctl --user enable symlink-wayland-socket.service

@Valcyclovir
Copy link

Having trouble with the requirements. First time doing this but have experience with Ubuntu.

Any instructions on how to apply changes to the latest kernel, and run the latest kernel with wsl2?

@PEMessage
Copy link

PEMessage commented Aug 3, 2025

Having trouble with the requirements. First time doing this but have experience with Ubuntu.

Any instructions on how to apply changes to the latest kernel, and run the latest kernel with wsl2?

You could check this link.

https://gist.github.com/oleksis/eb6d2f1cd2a6946faefb139aa0e38c35

and i also create a automatic script to base on above link

https://gist.github.com/PEMessage/a527ac647ea44514dbe3fbcbe433c3b3

already teston ubuntu24.04 and ubuntu 22.04

@onomatopellan
Copy link
Author

onomatopellan commented Aug 3, 2025

@PEMessage Thanks for the links. Those work great only for kernel 5.x though.

@Valcyclovir Instructions for building an x86_64 WSL2 6.6.x kernel with an Ubuntu distribution using bash are as follows:

Install the build dependencies:
$ sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev cpio qemu-utils

Download de kernel (always in a /home/user/ directory)
$ git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1

Change directory to kernel's directory root
$ cd WSL2-Linux-Kernel

Modify WSL2 kernel configs:
$ make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl

  • Device Drivers --> Android --> Select "Android Binder IPC Driver" -> Save

Build the kernel using the WSL2 kernel configuration and put the modules in a modules folder under the current working directory:
$ make -j$(nproc) KCONFIG_CONFIG=Microsoft/config-wsl && make INSTALL_MOD_PATH="$PWD/modules" modules_install

Then, you can use a provided script to create a VHDX containing the modules:
$ sudo ./Microsoft/scripts/gen_modules_vhdx.sh "$PWD/modules" $(make -s kernelrelease) modules.vhdx

Copy de generated kernel and modules to the desired folder
$ cp arch/x86/boot/bzImage /mnt/d/mykernel/bzImage-6.6.87.2.ANDROID
$ cp modules.vhdx /mnt/d/mykernel/modules.ANDROID.vhdx

Open WSL Settings -> Developer -> Select Custom kernel, Custom modules
Shutdown WSL2 VM
$ wsl.exe --shutdown

@SimBacon
Copy link

Thank you for the instructions! After following your steps in the post above to create the kernel and modules.ANDROID.vhdx, then following the steps in the original post, I was able to get Waydroid running in Weston (in Windows)!

I am not getting audio through Windows though, even though a music player inside Waydroid appears to be playing. I followed your directions exactly.

Do you have any suggestions?

@onomatopellan
Copy link
Author

onomatopellan commented Aug 25, 2025

@SimBacon For audio make sure you have latest WSL installed (wsl.exe --update --pre-release), you have systemd enabled in /etc/wsl.conf and finally you have the variable PULSE_SERVER=unix:/mnt/wslg/PulseServer when you run env.

@wray-lee
Copy link

So, there's currently no method for GPU acceleration?

@ausarhuy
Copy link

Can I use ubuntu 24.04 or must it be ubuntu 25.04?

@onomatopellan
Copy link
Author

@wray-lee No. But I'm keeping an eye on this thread waydroid/waydroid#564
Capturing the opengl calls and sending them to WSL2 mesa sounds like a plausible solution, on paper.

@ausarhuy Yes, if systemd is enabled it works even in Ubuntu 20.04.

@pilafov
Copy link

pilafov commented Sep 6, 2025

Thanks for this guide. It worked very well for me, including the custom kernel and modules.
After few days of using it I wanted to adjust the main window size by "waydroid prop set persist.waydroid ..." options.
At one point, I noticed waydroid switched to full screen but it was connecting directly to WSLg.

To confirm it, I fully uninstalled weston and waydroid is still working as it should.

Is there any advantage of using (second) weston (on top of weston/WSLg) ?

It's also likely that we can get better hardware support under WSLg.

@onomatopellan
Copy link
Author

@pilafov I was unable to launch it in full screen (1920x1080) that's why I had to launch weston first. Can you please post the props you changed? You should be able to get the applied values with waydroid prop get ....

Indeed WSLg is running over weston already. The additional weston uses wayland-1 as WAYLAND_DISPLAY when you launch it inside the weston desktop and WSLg uses wayland-0. Hardware acceleration should be the same on both, since it only accelerates the 2D presentation, not the 3D OpenGL calls.

@pilafov
Copy link

pilafov commented Sep 6, 2025

This is what I got

$ waydroid prop get waydroid.display_width
1366
$ waydroid prop get waydroid.display_hight
768

$ waydroid prop get persist.waydroid.width
$ waydroid prop get persist.waydroid.hight
$

My exact screen dimensions are indeed 1366x768.
I don't know why "waydroid prop get persist.*" is not returning any value but I think I used these with "set".
I didn't play with other "prop set" settings.

@onomatopellan
Copy link
Author

onomatopellan commented Sep 6, 2025

@pilafov Thanks. I did set

waydroid prop set persist.waydroid.width 1920
waydroid prop set persist.waydroid.height 1080

and I had a black screen but after running waydroid prop set persist.waydroid.no_background_subsurface true I finally can use the weston from WSLg for full screen too.

@pilafov
Copy link

pilafov commented Sep 6, 2025

Well done !
I was beginning to wonder if I did something else meanwhile and if I might not be able to reproduce it.
Now that I tried to remember, I did something funny on the Android's side which made me re-init Waydroid.

waydroid init -f

No, this is the first time I have seen this "persist.waydroid.no_background_subsurface" property, so I don't think I did that.
Again, the "get" option won't give us the current value.

$ waydroid prop get persist.waydroid.no_background_subsurface
$

Another (small) improvement I made was to automate your playlist (the startup + the waiting part).
Take a look at this script "android-startup.sh".

#!/bin/bash

if [[ $(pgrep -u $UID -f "dbus-daemon --session") ]]; then
    echo "Found a running instance of 'dbus-daemon'"
else
    dbus-daemon --session --address=$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &
    echo "Spawned a new instance of 'dbus-daemon'"
fi

export GALLIUM_DRIVER=d3d12
WAYDROID_SESS_LOG=/tmp/waydroid_session_start.out
waydroid session start >$WAYDROID_SESS_LOG 2>&1 &

WAYDROID_SESS_PID=$!
echo WAYDROID_SESS_PID=$WAYDROID_SESS_PID

while [[ true ]]; do
    echo "Waiting for 10s longer ..."
    sleep 10
    WAYDROID_SESS_ACTIVE=$(ps --no-headers -o pid -p $WAYDROID_SESS_PID)
    #echo "PID: [$WAYDROID_SESS_ACTIVE] is running"

    grep --quiet --no-messages --regexp 'Android.*is ready$' $WAYDROID_SESS_LOG
    if [[ "$WAYDROID_SESS_ACTIVE" -eq "$WAYDROID_SESS_PID" && "$?" -eq 0 ]]; then
        waydroid show-full-ui
        break
    fi
    #ps -p $WAYDROID_SESS_PID
done

@onomatopellan
Copy link
Author

@pilafov Ok so I was running an old image with Lineage 18. After uninstalling waydroid and reinstalling it downloaded Lineage 20 image and now everything seems to work from the beginning without the need to change the persist.waydroid.no_background_subsurface prop.

@pilafov
Copy link

pilafov commented Sep 6, 2025

@onomatopellan I've only used Lineage 20 (based on Android 13) from the beginning. It's possible that I just accidentally forgot to bring up weston.

@pilafov
Copy link

pilafov commented Sep 10, 2025

One more question of behalf of everybody who noticed.
After re-building the custom kernel I got the following two.

File Size
bzImage-6.6.87.2.ANDROID 15,704 KB 15.3 MB
modules.ANDROID.vhdx 2,236,416 KB 2.13 GB

Did you get similar numbers ?
For which version of the kernel ?

I'm questioning the 2.13GB for modules which feels excessive. Is there a way to shrink it?

@onomatopellan
Copy link
Author

onomatopellan commented Sep 12, 2025

@pilafov Same numbers here with same version. It can't be compacted because that's the actual uncompressed size of those .ko modules. Just the gpu modules size are already 720Mb and the network modules 400Mb. Compressed in 7z everything is ~600Mb.

I don't know if the way is compiled has lots of debug symbols or something. I just know the official WSL2 release includes a 15Mb kernel file and a 190Mb modules.vhd file.

@pilafov
Copy link

pilafov commented Sep 14, 2025

@onomatopellan

... I just know the official WSL2 release includes a 15Mb kernel file and a 190Mb modules.vhd file.

That's exactly why the new/custom size of "modules.vhd" (2.13GB) feels very excessive. It's over 10 times bigger.

In addition, the standard WSL2 contains "system.vhd" (388 MB) which is a separate distro where WSLg runs. Isn't that where all GPU drivers and such are expected to live ? At least, that would be a good explanation for the 388MB size.

@onomatopellan
Copy link
Author

onomatopellan commented Sep 15, 2025

@pilafov I asked Copilot and it recommended to strip the debug symbols of the modules. Now the modules.vhdx size is 192Mb. I updated the guide with the line find ./modules/lib/modules/$(make -s kernelrelease) -name '*.ko' -exec strip --strip-unneeded {} \;

The system.vhd is the system distro (Common Base Linux Mariner) that indeed contains WSLg. You can access to that system distro with wsl.exe -d Ubuntu-25.04 --system. There is no kernel or modules, just the files it needs for running Pulseaudio, wayland and mesa.

system

@pilafov
Copy link

pilafov commented Sep 15, 2025

@onomatopellan
This is great news, thanks!
I can't wait to try it out myself but I'm out of luck as I already removed the whole kernel source tree.
I'll have to start from the beginning.
I'm sure this will greatly improve WSL2 startup time.

What about Waydroid startup ?
Did you notice any difference ?

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