WSL GPU Library Resolution: PATH, ldconfig, and Driver Stubs Are Different Layers
Trace WSL GPU library discovery correctly by separating executable PATH, the dynamic linker cache, WSL-provided libraries, and host drivers.
GPU failures in WSL are often debugged by changing the wrong path. PATH locates executables such as nvidia-smi or nvcc. The Linux dynamic linker resolves shared objects such as libcuda.so.1 using its configured search rules, cache, and environment. A Windows GPU driver supplies the host-side support that WSL’s paravirtualized GPU path depends on. Those layers interact, but a command being found on $PATH does not prove that a required shared library can be loaded, and a visible library does not prove that the host driver and Linux application are compatible.
The WSL configuration reference exposes two relevant settings: [automount] ldconfig, which adds Windows-provided GPU libraries to the dynamic linker’s search path and runs ldconfig when WSL 2 GPU support is enabled, and [gpu] appendLibPath, which adds /usr/lib/wsl/lib to $PATH when GPU support is enabled. The names and effects are easy to conflate. Measure each layer explicitly before installing packages or changing library paths.
The four locations that commonly get mixed up
First, the shell’s PATH is a colon-separated list of directories for executable lookup. command -v nvidia-smi answers where the shell finds a program; it says nothing about a shared object needed after that program starts. Second, LD_LIBRARY_PATH is an environment variable the dynamic linker can consult, but setting it globally can override system library choices and create difficult-to-reproduce behavior. Third, ldconfig builds a cache from configured library directories and links, which can be inspected with ldconfig -p. Fourth, WSL’s /usr/lib/wsl/lib is a WSL-provided location for host-integrated GPU-related libraries on supported configurations.
Do not copy a Windows DLL into Linux or replace a WSL-provided driver-facing library with an arbitrary Linux driver package. The GPU execution path relies on coordinated Windows driver and WSL components. NVIDIA’s CUDA-on-WSL guide and Microsoft’s WSL GPU documentation describe the supported setup and host driver prerequisites. A package-manager installation of a full Linux display driver inside a WSL distro is not a generic repair for a missing WSL integration library.
Confirm that the feature path is enabled
Begin by recording platform versions and the relevant Linux view:
wsl.exe --version
wsl.exe --status
wsl.exe --list --verbose
Get-CimInstance Win32_VideoController | Select-Object Name, DriverVersion
Inside the target WSL 2 distribution:
uname -a
printf 'PATH=%s\n' "$PATH"
printf 'LD_LIBRARY_PATH=%s\n' "${LD_LIBRARY_PATH-<unset>}"
command -v nvidia-smi || true
test -d /usr/lib/wsl/lib && ls -la /usr/lib/wsl/lib || true
This captures whether the guest is WSL 2, what the Linux kernel reports, which executable directories are searched, and whether the WSL library directory is visible. It does not prove the GPU is available to a specific framework or that a kernel module is correctly integrated. On systems without NVIDIA hardware or software, nvidia-smi is not an applicable universal health check.
Check shared-library resolution separately:
if command -v ldconfig >/dev/null 2>&1; then
ldconfig -p | grep -E 'lib(cuda|nvidia|vulkan)' || true
fi
ldconfig -p reports entries in the dynamic linker cache; not every library in /usr/lib/wsl/lib must be represented in a way that matches a particular application’s expected soname. For a specific executable, use readelf -d /path/to/program to inspect its declared NEEDED libraries, and use the distribution’s ldd only on trusted binaries. Avoid treating ldd on an untrusted executable as a harmless static parser because implementations can invoke a program’s loader.
Understand the WSL configuration keys precisely
ldconfig is documented under the per-distribution [automount] section in Microsoft’s current wsl.conf reference. The documented default is true; its note says it adds Windows-provided GPU libraries to the dynamic linker’s search path and runs ldconfig, and that it applies only to WSL 2 with GPU support enabled. A targeted configuration example is:
[automount]
ldconfig=true
[gpu]
enabled=true
appendLibPath=true
This example shows distinct sections, not one combined setting. Preserve existing automount options and any other [gpu] values. The reference describes [gpu] enabled as allowing Linux applications to access the Windows GPU through paravirtualization. It describes appendLibPath as adding /usr/lib/wsl/lib to $PATH. Despite its name, that documented effect is about the shell’s executable search path; do not say it changes LD_LIBRARY_PATH or the dynamic linker cache.
If a global WSL GPU setting is also changed, remember that .wslconfig applies to all WSL 2 distributions, whereas /etc/wsl.conf is per distro. The currently documented gpuSupport global setting and the per-distro GPU behavior have different scope. Verify the active package’s reference before relying on a particular key because options evolve. After changing configuration, stop and restart the relevant distribution or VM as required so the new value is actually read.
Trace a missing-library failure
Start with the exact error and executable. If the shell says a command is not found, inspect PATH and command -v; if the program starts but the loader reports a missing .so, inspect the executable’s dynamic section and the loader cache. Do not fix a library error by appending arbitrary directories to PATH.
For a trusted binary, this diagnostic sequence makes the distinction visible:
command -v nvidia-smi || true
printf '%s\n' "$PATH" | tr ':' '\n' | nl -ba
ldconfig -p | grep -F 'libcuda.so' || true
readelf -d "$(command -v nvidia-smi)" 2>/dev/null | grep NEEDED || true
nvidia-smi may be a WSL-provided executable or wrapper depending on the installed environment; if it is absent, skip the readelf line rather than treating the missing utility as proof that GPU support is broken. A deep framework check should use the framework’s own supported device enumeration and a small, deterministic test kernel, not just a command-line utility.
When the cache lacks a library that exists in the expected directory, inspect the distribution’s linker configuration and ldconfig output. Avoid manually symlinking a driver-facing soname until the vendor’s WSL documentation calls for that exact operation. Symlinks can hide version mismatches and break after a WSL or driver update. When a library is present but loading fails, inspect architecture, soname, ABI version, and the application’s container or environment isolation.
For a trusted test binary, the dynamic loader can emit a temporary resolution trace:
LD_DEBUG=libs /path/to/trusted-program --version 2>&1 | sed -n '1,120p'
This is a diagnostic invocation, not a permanent environment setting. Loader diagnostics can be verbose and may reveal directory names, so capture them deliberately and avoid sharing unrelated environment data. Compare the directories searched with the binary’s RPATH or RUNPATH, distro ld.so.conf entries, /etc/ld.so.cache, and WSL-provided library path. If the executable is setuid or otherwise has special loader behavior, do not infer its production lookup solely from a normal shell test.
The application can also load libraries dynamically by plugin name after startup, which may not appear in its ELF NEEDED entries. For these cases, inspect the framework’s own plugin diagnostics or trace a minimal test using the application’s supported logging. Do not add a global LD_LIBRARY_PATH to make one plugin visible unless you understand which other library versions that variable may shadow. A private wrapper scoped to a single test process is safer than changing every distro login environment.
Container and environment boundaries
Containers add another filesystem and process environment boundary. A host WSL distribution can see /usr/lib/wsl/lib, while a container may not inherit that directory, the relevant device access, or the library cache. Docker or Podman integration has its own GPU configuration requirements. Do not assume that enabling ldconfig in the host distro automatically configures every container image. Validate inside the actual container using its PATH, linker cache, visible devices, runtime, and a small application test.
Likewise, a Python virtual environment typically changes executable and package lookup, not the system dynamic linker policy. Conda, custom runtime loaders, and manually set LD_LIBRARY_PATH can alter the result. Capture environment variables from the process that fails, not only from a login shell. Remove temporary overrides one at a time during diagnosis.
Safe change and acceptance process
Save the current /etc/wsl.conf, .wslconfig, Windows driver version, WSL version, and Linux kernel version. Change one setting. Restart the distro or shared WSL VM with awareness that wsl.exe --shutdown interrupts all WSL 2 distributions. Re-run the shell path check, dynamic linker cache check, and exact framework or application test. For a production GPU workload, record expected device name, library version, application version, and a known-good result before updating WSL or the Windows driver.
An acceptance test passes only if the intended executable resolves, the required shared objects resolve for the target process, the GPU runtime reports the expected device, and a small supported workload completes. nvidia-smi alone does not prove CUDA compilation, graphics rendering, or framework execution. For OpenGL, Vulkan, DirectML, CUDA, and other paths, test the relevant API independently and use the vendor’s current WSL-specific guidance.
Keep ownership clear after upgrades
WSL-provided libraries are coupled to the WSL runtime and Windows driver support path. Let supported Windows and WSL update mechanisms maintain those components. Avoid baking a copy of /usr/lib/wsl/lib into a portable Linux image or a container layer unless the vendor documents that approach. Such a copy can become stale relative to the host and can create a false sense of reproducibility.
When a workload changes from working to failing, compare the Windows GPU driver, WSL package, kernel, distro linker cache, and process environment. A change in any one of them can affect a different layer. The useful diagnostic is a small comparison matrix, not a global path mutation: executable lookup, library lookup, device visibility, and actual GPU execution each need their own evidence.
Related:
- How GPU Compute Actually Reaches WSL2’s Linux Environment
- OpenGL Acceleration in WSLg: From Mesa to the Windows vGPU
Sources: