Skip to content
WSLDeep Dive Published Updated 7 min readViews unavailable

Python Environments in WSL: Keep Interpreters, Wheels, and Projects Native

Build reproducible Python environments in WSL by separating Windows and Linux interpreters, using project virtual environments, and validating native dependencies.

Python installed on Windows and Python installed inside a WSL distribution are separate runtimes. They have different executable formats, package directories, native library dependencies, path conventions, and environment markers. A Windows Python process can access WSL files through a network path, and a Linux process can invoke Windows programs through interop, but neither fact turns one interpreter’s environment into the other’s.

The most reliable default for Linux-targeted Python work is to keep the source tree, interpreter, virtual environment, and native build dependencies on the Linux side. This matches the operating system and architecture that the application will target and avoids copying interpreter-specific files between filesystems.

Prove which Python is running

Begin with an explicit distro and inspect the interpreter from inside it:

wsl.exe --distribution Ubuntu-24.04 --exec python3 --version
wsl.exe --distribution Ubuntu-24.04 --exec python3 -c 'import sys; print(sys.executable); print(sys.prefix); print(sys.base_prefix)'

In a Linux shell, compare the resolved commands and pip’s binding:

command -v python3
python3 -m pip --version
python3 -c 'import platform, sys; print(sys.executable); print(platform.platform()); print(platform.machine())'

Prefer python -m pip over an unqualified pip command. It binds package installation to the interpreter named by python, reducing the chance that a shell resolves pip from a different environment. On distributions where python is not defined, use python3.

Keep a separate Windows installation if Windows-native applications need it. From PowerShell, py -0p can inventory Windows Python installations; from Linux, command -v python3 identifies a Linux executable. Do not use a Windows interpreter path as the interpreter for a Linux venv or attempt to activate a Linux venv from PowerShell.

Create disposable project environments

Python’s standard library venv creates an environment based on the selected interpreter. In a WSL project located under the Linux home directory:

cd ~/src/example-api
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip check
python -c 'import sys; print(sys.executable); print(sys.prefix != sys.base_prefix)'

The environment directory should be ignored by version control. It contains a path to its base interpreter and is not a portable artifact. If the distro is recreated, Python changes, or the project moves to a different location, recreate the environment from the declared dependency metadata rather than copying .venv.

Keep project dependencies explicit. A requirements file can support repeatable installs, but its contents need intentional version policy; a simple list of unconstrained names does not make builds reproducible. For modern projects, the project metadata and lock-file workflow chosen by the team should be the source of dependency intent. Do not confuse a virtual environment with a lockfile or a container image.

Protect the distribution-managed Python

Linux distributions may use their system Python for operating-system tooling. Package-managed files and Python packages installed globally with pip can conflict. Follow the distribution’s documented packaging model and use isolated environments for application dependencies. If pip reports that the environment is externally managed, that is a boundary to respect, not a reason to force installation into the system interpreter.

Install missing system tools through the distro package manager, such as the venv module package or compiler headers required by a native dependency. Install application libraries into the project’s venv. This distinction keeps system updates and per-project Python dependencies under their proper owners.

Do not use sudo pip install for routine project dependencies. It can write into system directories, give untrusted package build steps elevated privileges, and leave the distro in a state that its package manager does not describe.

Native wheels and compilation belong to the Linux environment

Python packages can include compiled extension modules. A wheel encodes compatibility tags for Python version, ABI, and platform. A Windows wheel cannot be loaded into Linux Python simply because both interpreters can see the same project directory. Likewise, a compiled extension in a .venv is not a shareable cache across WSL, Windows, ARM64, and x86-64.

When pip builds a package from source, the Linux environment may need a compiler, Python development headers, and system libraries. Diagnose build failures by reading the first compiler or linker error, then install the missing Linux development package for the current distro. Do not copy a .pyd from Windows into the venv or point Linux’s LD_LIBRARY_PATH at random Windows DLL directories.

If a dependency offers no wheel for the target combination, confirm whether a source build is supported and whether system packages are available. Reproducible deployment may require recording the Linux distro image and native dependencies in addition to Python package metadata.

Keep projects on the filesystem that owns the workload

Microsoft recommends storing Linux-tool projects in the WSL filesystem for performance. A project under /home gives Linux-native metadata and I/O semantics. /mnt/c is useful when Windows tools need direct ownership of the source tree, but cross-boundary metadata operations and large dependency trees can change performance and behavior.

Choose one workspace owner. If Linux Python, pytest, compilers, and language servers build the project, keep it in the distro filesystem and open it through a WSL-aware editor connection. If Windows Python and Windows tools own the project, keep the venv and native build artifacts on the Windows side. Avoid sharing one venv between both worlds.

Reproducible requirements and cache boundaries

A virtual environment is disposable, so the repository should contain enough metadata to rebuild it. For a team that uses a requirements file, a common verification sequence is:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python -m pip check
python -m pytest

Pinning strategy belongs to the project. Fully pinned requirements can aid application deployment, but library authors may choose ranges or constraints for compatibility testing. Record the Python version and distro package prerequisites alongside that policy. If native modules are involved, test the install in a clean Linux environment instead of relying on one developer’s preexisting cache.

Caches are not the environment itself. Pip’s cache can be cleared or rebuilt; .venv should be recreated rather than treated as an exportable system image. Keep the package index, proxy, and certificate configuration explicit when enterprise connectivity changes installation behavior.

For controlled or offline builds, preserve the approved wheel artifacts and use pip’s documented local archive options rather than copying an activated environment between hosts. A wheelhouse still needs compatibility review: a wheel’s tags must match the Linux interpreter, ABI, and architecture that will install it. A Windows wheel in the same directory is not a fallback for a Linux wheel. Record where artifacts came from and validate their hashes through the repository or artifact-management process before consuming them.

Debug common boundary failures

If a module import fails, print sys.executable, sys.prefix, and python -m pip –version in the failing command. If an IDE runs the wrong interpreter, compare the interpreter shown by the editor with the one returned by the integrated WSL terminal. Install any editor extension that executes Python on the WSL side, but do not assume that installing a Windows extension also installs Python packages in Linux.

If a package installs but fails at import time, check architecture, Python ABI, and dynamic shared-library dependencies. If file watchers or installs are slow, verify project placement before changing pip settings. If a system package update breaks a venv, recreate it and verify that native dependencies still match the distro.

Acceptance criteria

A production-ready WSL Python setup is demonstrated when a clean clone can create the intended Linux venv, install declared dependencies without writing into system Python, pass pip check and project tests, and report the expected interpreter path. Verify separately that Windows Python, if installed, remains a distinct executable and does not enter the WSL process path unexpectedly.

Record the distro release, Python minor version, architecture, native development packages, and dependency source policy. That information makes a defect reproducible without leaking the entire environment or relying on a developer’s interactive shell state.

Related:

Sources:

Comments