Reading ZFS drives on Windows with the power of WSL
zfs windows wsl linuxI recently found myself in a situation where I needed to read a ZFS-formatted drive, but with only a Windows machine to-hand.
Now I could walk across the room and simply grab a device running a more penguin-oriented operating system. But that’s no fun. So I decided the optimal course of action would be to spend the afternoon learning about WSL internals and recompiling the Linux kernel several times until I had the drive working directly on Windows.
With some minor effort, it’s definitely possible to get Windows reading and writing ZFS drives, and exposing their contents through Windows Explorer as if they are native:

We can even use snapshots, bookmarks and all the other features ZFS normally provides!
OpenZFS on Windows
There is a native port of ZFS for Windows which is actively developed, called OpenZFS on Windows. I can’t comment on the quality of this code as I haven’t tried it, but it seems to be in quite an early ‘alpha’ stage to me. I decided that, if possible, I’d rather use the much more mature Linux codebase to mount my drive.
zfsutils-linux
My first brainwave was simply to try installing the zfsutils-linux
package inside a standard WSL Ubuntu session and try and mount the ZFS pool inside of WSL. On a full Ubuntu install, this package is usually all we need to get full ZFS functionality.
Unfortunately, this doesn’t work for WSL. As it turns out, the zfsutils-linux
package only contains the userspace binaries that allow interacting with ZFS pools, such as zpool
, zfs
, zdb
, etc. It doesn’t contain the actual kernel code required to support ZFS filesystems.
On Debian systems, these modules get built on the end user machine automatically by the zfs-dkms
package. In Ubuntu things are sightly different. Canonical build the modules themselves in their build infrastructure and ship them with kernel updates directly. This allows them to do things like sign the kernel image for Secure Boot after the ZFS modules are included. From what I can tell, Canonical’s modules get built form the same zfs-dkms
code, as you can see where they include it in their kernel build here.
Unfortunately for us, when we install the default Ubuntu distribution inside WSL, it doesn’t use Canonical’s kernel, it uses a Microsoft one, tailored specifically for WSL. So no ZFS modules for us.
Solution
Overall, the solution to this ends up being relatively simple. WSL2 allows us to specify a path to a custom-built kernel using the .wslconfig
file. With that in mind, we can pull the sources from the Microsoft WSL2 kernel repo above, rebuild them with OpenZFS support statically baked-in, and then reconfigure WSL to use our new custom kernel.
Build instructions
I built a script to automate this process, which is hopefully fairly well commented. You can find that over on GitHub here.
Once the script is downloaded into the WSL Ubuntu environment, execute it with:
./zfs-on-wsl.sh
This script will build and install the ZFS userspace utilities inside the WSL environment, and will also build a kernel for us.
Kernel installation instructions
Once our new ZFS-powered kernel has been built, the script will output it to C:\ZFSonWSL\bzImage-new
. It does this to avoid overwriting a running kernel if we’re running the script inside a WSL instance that’s already using a kernel we built with this script.
We need to shutdown WSL, move the new kernel to C:\ZFSonWSL\bzImage
, and then update our .wslconfig
before starting it up again.
Stop the WSL2 VM if it is currently running:
wsl --shutdown
Edit the .wslconfig
file in your home directory to point to the downloaded kernel:
[wsl2]
kernel=C:\\ZFSonWSL\\bzImage
localhostForwarding=true
swap=0
Start up WSL again by opening a new WSL session and check that our custom kernel is being used:
$ uname -a
Linux PrecisionT1700 5.15.167.4-microsoft-standard-WSL2-penguins-rule #4 SMP Sat Nov 23 19:15:47 GMT 2024 x86_64 x86_64 x86_64 GNU/Linux
Passing through drives (native)
At this point, you probably want to pass through some drives to make a pool with using zpool create
. For non-USB storage devices, you can achieve this as below.
List your drives:
GET-CimInstance -query "SELECT * from Win32_DiskDrive"
Pick the drive you want, and mount it into the WSL2 VM:
wsl --mount --bare \\.\PHYSICALDRIVE1
Passing through drives (USB Mass Storage)
If you want to use USB drives instead, you will get an error if you try to use the above method. Instead, you need to use usbipd
to make this work.
Install usbipd-win
as per Microsoft’s guide here:
winget install --interactive --exact dorssel.usbipd-win
List your USB devices:
usbipd list
Bind the device you want to use with usbipd
, based on the bus ID shown in the list:
usbipd bind --busid 7-2
Attach the bound device to the WSL2 VM:
usbipd attach --wsl --busid 7-2
Microsoft kind of hand-waves away the complexity lurking under the hood here but this method seems to use USB/IP to turn the USB packets(?) into network packets and pass them into the WSL2 VM. Given that the WSL2 VM is running as a fairly regular Hyper-V guest (or so I thought), I’m really not sure why we can’t just pass through USB devices to WSL directly, but that’s probably a blog post for another day. (If you know why it works this way, please do let me know!)
Interacting with mounted drives
Using the methods above, you can interact with mounted drives as you would any block device on a standard Linux system. Try lsblk
to inspect mounted drives to make sure they’re working as expected.
Using ZFS
Once you’ve got drives passed through as block devices, all the regular ZFS commands you’re used to will work.
Try creating a pool with:
sudo zpool create poolname /path/to/passed/through/disk
And then:
zpool status
or maybe:
zfs list
Here be dragons
This post (and associated script!) was mostly a learning adventure for me, delving into some new areas of Ubuntu kernel packaging, Windows, ZFS, and WSL. I had backups of everything on the drive I was trying to read.
If you use this for all your important documents, family photos, and favourite Linux ISOs and it all goes bang then please don’t go blaming me!