Never Install an NVIDIA Driver Inside WSL
I lost an afternoon to a GPU that would not work, and the thing that broke it was me installing the driver. That sentence sounds backwards, and that is exactly why this trap is so mean. I was setting up GPU-accelerated work inside WSL (Windows Subsystem for Linux, which lets you run a real Linux environment inside Windows) on a machine with an NVIDIA card. My Linux instincts told me to install the GPU driver. Those instincts were wrong, and following them is what took the GPU offline.
If you are doing GPU work in WSL, read this before you type apt install. It will save you the afternoon I burned.
How GPU access actually works in WSL
On a normal Linux machine, you install an NVIDIA driver. The driver is the low-level software that talks directly to the physical GPU. On top of that you install the CUDA toolkit, which is the set of libraries and compilers you use to build and run GPU code. Driver talks to hardware; toolkit builds and runs your programs. Two different layers.
WSL does not work like a normal Linux machine, and this is the whole point. Inside WSL you do not install a Linux GPU driver at all. Instead, the Windows host owns the one real GPU driver, and it shares (projects) the GPU into the Linux guest. This sharing is called GPU paravirtualization, or passthrough: the host lends its GPU to the guest instead of the guest driving the hardware itself.
Two pieces make that passthrough work, and Windows provides both automatically:
/dev/dxgis the special device inside WSL that the Linux side uses to reach the GPU. Think of it as the doorway from the guest to the host’s real driver.libcuda.soin WSL is a stub library. A stub is a placeholder that does not do the real work itself; it just forwards every call through to the Windows host driver, which does the actual GPU work. Your Linux programs call this stub thinking it is a real CUDA driver library, and it quietly hands the work off to Windows.
So the picture is: your Linux code calls the stub libcuda.so, the stub talks through /dev/dxg, and the real Windows driver runs the work on the GPU. The Linux guest never touches the hardware directly. That design is elegant, but it sets a trap for anyone with normal Linux habits.
The mistake, and why it is silent and confusing
Here is what I did. GPU was not working yet, I opened WSL, and my muscle memory said “you need the NVIDIA driver.” So I ran the normal Linux thing and installed a full NVIDIA driver package inside the guest.
# WRONG. Do not do this inside WSL.
# Installing a full Linux NVIDIA driver in the guest
# overwrites the stub libcuda.so and breaks passthrough.
sudo apt install nvidia-driver-550
# (any full driver package does the same damage)
libcuda.so that Windows provided. That stub was the forwarder to the host driver. Once you overwrite it, the passthrough is broken. The GPU stops working inside WSL, usually with vague CUDA errors that do not say “you overwrote the stub.” You stare at it thinking “I installed the driver, why is the GPU still broken?” The answer is that installing the driver is precisely what broke it.This is the worst kind of bug because the failure is silent and it points you in the wrong direction. The errors look like a missing or misconfigured driver, so your instinct is to reinstall the driver, which digs the hole deeper. I spent a long time convinced I had a version mismatch when the real problem was that I should never have installed a driver at all.
The correct setup: toolkit only
The rule is short: put the toolkit in the guest and keep the driver on the host. Never put a driver in the guest.
Inside WSL you install only the CUDA toolkit, and specifically the WSL build of it. NVIDIA ships a package called cuda-wsl-ubuntu that is made for exactly this situation: it installs the CUDA libraries and compilers but deliberately does not ship a display driver. That is the whole point. It leaves the stub libcuda.so and the host driver alone.
# RIGHT. Inside WSL, install the WSL CUDA toolkit only.
# This package ships NO driver, so it does not touch the stub.
sudo apt install cuda-toolkit-12-6
# or the meta-package that pulls the WSL-specific toolkit:
sudo apt install cuda-wsl-ubuntu
cuda-wsl-ubuntu (the WSL-specific CUDA toolkit) inside Linux, and keep the actual NVIDIA GPU driver installed only on Windows. The WSL toolkit does not include a display driver on purpose, so it leaves the stub libcuda.so and /dev/dxg passthrough intact. Update the driver on the Windows side; update the toolkit on the Linux side.Here is the same idea as a table, since “where does each piece belong” is the whole lesson:
| Component | Where it belongs |
|---|---|
| GPU driver (talks to the hardware) | Windows host only. Never install it in the WSL guest. |
| CUDA toolkit (libraries and compilers to build/run GPU code) | WSL guest. Use the WSL build (cuda-wsl-ubuntu), which ships no driver. |
/dev/dxg and the stub libcuda.so |
Provided automatically by the Windows host. Do not overwrite them. |
Matching your build to the GPU: compute capability
Once the passthrough is healthy, there is one more thing that trips people up when they compile GPU code: targeting the right architecture. Every NVIDIA GPU generation has a “compute capability,” which is a version number that identifies the feature set of that GPU’s architecture. When you compile GPU code, you tell the compiler which compute capability to build for. Build for the wrong one and your code may run poorly or not run on that card at all.
For an Ada Lovelace generation card (the RTX 40-series, for example an RTX 4060), the compute capability is 8.9, written as sm_89 in build flags. If you are using CMake, you set it like this:
# Target Ada Lovelace / RTX 40-series (compute capability 8.9)
cmake -DCMAKE_CUDA_ARCHITECTURES=89 ..
The number is the compute capability with the dot removed, so 8.9 becomes 89. If you are on a different generation, look up your card’s compute capability and use that number instead. This flag has nothing to do with the driver trap above, but it is the other place a working GPU can look broken (or slow) because the build did not target the real hardware.
How to check it is actually working
After you install the WSL toolkit and leave the driver alone on Windows, verify the passthrough from inside WSL:
- Run
nvidia-smifrom inside the WSL shell. On WSL this command reaches the GPU through the host passthrough, so if it lists your card, the projection is working. If it errors out or shows nothing after you installed a driver in the guest, that is the classic symptom of a clobbered stub. - Confirm
/dev/dxgexists inside WSL. If the doorway device is present, the host is projecting the GPU into the guest. - Check that
nvcc --versionreports your CUDA toolkit version. That confirms the toolkit installed without dragging in a driver.
Caveats
A few honest notes. Exact package names and versions drift over time, so treat cuda-wsl-ubuntu and the toolkit version numbers here as the shape of the answer, not a frozen recipe. Check NVIDIA’s current WSL CUDA instructions for the exact package for your Ubuntu and CUDA version. The sm_89 flag is specific to Ada / 40-series hardware; match the compute capability to your own GPU. And the one rule that does not drift, the rule this whole post exists for: in WSL, install the toolkit, never the driver. The driver lives on Windows. Break that rule and you will spend an afternoon debugging the thing you just installed.