WSL 2's Shared Network Namespace: Cross-Distro Ports and Services
Operate WSL 2's shared cross-distro network namespace: explain port collisions, inspect interfaces and listeners, and design predictable service ownership.
Two WSL 2 distributions look like separate Linux machines in some ways: each has its own root filesystem, mount namespace, PID namespace, user namespace, and init process. They are not separate network stacks by default. Microsoft documents that WSL 2 distributions share a network namespace, along with the VM’s kernel, CPU, memory, swap, and most of its device tree.
That architectural detail explains a family of symptoms that are otherwise confusing: a port opened by a service in one distro can collide with the same port in another; localhost can reach a listener across distros; ip addr reports common interfaces; and a listener may be visible in ss even though the owning process is absent from the current distro’s process list. This article is specifically about WSL 2. Do not project the WSL 2 utility-VM model onto WSL 1.
What “shared network namespace” does and does not mean
A Linux network namespace contains network devices, addresses, routes, firewall context, and related network state. When two distro processes use the same namespace, they use the same Linux network interface set and network port space. They do not get independent copies of lo, separate TCP bind tables, or private NAT addresses just because they have separate distro names.
The namespace does not merge the distributions’ filesystems or process tables. Microsoft documents distinct mount and PID namespaces for each distro. A process started in one distro’s PID namespace will not necessarily appear by name in the other distro’s ps; yet its socket still belongs to the shared network stack. This is why a port collision can happen when the second distro cannot identify the first distro’s daemon with an ordinary process listing.
Likewise, WSL networking mode and distro network namespace are different layers. NAT and mirrored modes describe how the shared WSL 2 network connects to Windows and external networks. They do not create a separate network namespace per distribution. networkingMode in the Windows-side .wslconfig is a VM-level choice; changing it can affect all WSL 2 distributions that use the VM.
Confirm the namespace and inventory the shared stack
Run the same read-only probes in each WSL 2 distro. The namespace identifier from /proc/self/ns/net, the network interfaces, and the route table should agree while the same WSL 2 VM is active:
printf 'network namespace: '
readlink /proc/self/ns/net
ip -brief address
ip route
ss -ltn
readlink prints a namespace handle such as net:[4026531840]; compare the values rather than assuming each distro’s /proc path is unique. The numeric inode is an observation for this running namespace, not a stable identifier to store in configuration. The ss command lists listening TCP sockets without requiring a process name, which is useful when a listener is owned by another distro’s PID namespace.
If one distro can bind a port but another receives EADDRINUSE on the same address and port, inspect the shared listener set before changing distro configuration. Do not infer “nothing is using it” from an empty ps result inside only the second distro. Query the socket table, stop the intended owner using its own service manager, and retest the bind.
Prove cross-distro loopback deliberately
Use a disposable port and a harmless test server to demonstrate the boundary on the Windows build and WSL package you actually deploy. In distro A, run:
python3 -m http.server 18765 --bind 127.0.0.1
While that process is running, query it from distro B:
curl --fail --silent --show-error http://127.0.0.1:18765/
The successful local request is evidence that both distro processes are using the same loopback network namespace in this configuration. Stop the server with Ctrl+C and confirm ss -ltn no longer reports port 18765. Do not use a production service or an occupied port for this experiment.
This probe is not a complete Windows-host networking test. NAT-mode Windows-to-WSL behavior and mirrored-mode localhost behavior are separate host/VM paths described in Microsoft’s networking guide. A successful distro-to-distro 127.0.0.1 request does not prove that a Windows application, another LAN machine, or a VPN peer can reach the same service.
Design services around one owner
Assign one distro as the owner of each local service and document its bind address and port. Clients in other distros can use the shared local network path when that is intended, but avoid starting redundant daemons that assume every distribution has an isolated port table. A duplicate development web server, database, container port publication, or test fixture can fail even though it runs in another distro.
Avoid making a service listen on every address just to work around cross-distro confusion. 127.0.0.1 limits the listener to loopback in the shared network namespace; 0.0.0.0 binds all IPv4 interfaces and changes the exposure boundary for the entire VM network. Select the address according to the intended clients, then validate from each required origin: the owning distro, a peer distro, Windows, and any external host that is explicitly in scope.
If two separate distro workloads genuinely need the same port, choose among separate port numbers, a single shared service with distinct virtual hosts or paths, or an actual isolation boundary such as another VM. Do not use network namespace tricks inside one distro and assume WSL will preserve them across reboot or networking-mode changes unless the exact supported configuration is documented and tested. For test suites, serialize processes that use fixed ports or allocate ephemeral ports and pass them through an explicit test contract.
\wsl.localhost\DistroName is a Windows filesystem access path, not the network namespace. A mounted path can expose files without creating a separate network interface. Conversely, shared sockets do not make one distro’s root filesystem a safe shared data directory. Keep file ownership, service ports, and process visibility as three different concerns.
Read listener ownership without relying on ps
When an unexpected port is occupied, collect evidence from each distro and from the Windows host. Inside a distro, ss -ltn confirms whether a listening socket exists and ss -ltnp may add process details that are visible in the caller’s PID namespace. If the owner process belongs to a different distro, process metadata can be incomplete even though the socket remains visible. Use each candidate distro’s service manager (systemctl status, if systemd is enabled) or inspect the application’s own supervisor rather than killing an unrelated PID number.
On Windows, wsl.exe --distribution <DistroName> --exec ss -ltn starts the selected distro and runs the read-only query. This is useful for collecting a cross-distro inventory, but remember that invoking a distro may start it. Capture the distro name beside each output, and do not run a privileged cleanup command simply because a port is in use.
Check address family too. A service bound only to IPv6 may present differently from an IPv4 listener, and dual-stack behavior depends on the socket options and Linux implementation. Compare ss -ltn and ss -ltn6 as needed; test the exact destination literal used by the client. Also record whether the app binds to localhost, 127.0.0.1, ::1, a specific interface address, or a wildcard.
Lifecycle and networking-mode transitions
The network namespace belongs to the shared WSL 2 VM rather than to a durable distro-specific virtual adapter. wsl --shutdown stops WSL instances globally, so it is not a safe way to restart just one service while another distro is performing work. A normal service stop should be targeted at the owning distro and service. After a VM-wide shutdown, repeat the namespace and port tests because interface addresses, dynamic state, and listeners are runtime observations, not persistent identifiers.
NAT and mirrored mode can change Windows reachability, DNS handling, localhost forwarding, and firewall behavior. They do not eliminate collisions among distro processes that still share a namespace. If a port is reachable from Windows in one mode and not another, debug the Windows/VM boundary separately from the cross-distro socket table. Keep .wslconfig changes scoped to a controlled test, restart WSL as documented, and capture the mode and WSL version with the results.
Acceptance checks for a multi-distro environment
Test at least two WSL 2 distributions with both cold and warm starts. Compare /proc/self/ns/net, interface listings, routes, and listening sockets; create a temporary listener in one distro and confirm the intended peer can connect; try a deliberate duplicate bind and confirm the failure is understood; then stop the owner and verify the port is free. Repeat with the required NAT or mirrored mode and after a full WSL VM shutdown.
Record the Windows build, WSL package version, each distro’s WSL version (wsl --list --verbose), networking mode, namespace handle, listener addresses/ports, and which distro owns each service. Keep service ownership in source-controlled operational documentation so a teammate does not debug an EADDRINUSE condition as a per-distro routing problem.
The practical rule is simple: WSL 2 gives each distro useful filesystem and process separation, but its network namespace is shared. Treat network listeners as VM-wide resources, test from the actual client boundary, and keep the owning distro explicit. That model explains port collisions without confusing them with WSL’s NAT, mirrored networking, or Windows filesystem integration.
Related:
- How to Install and Manage Multiple Linux Distros in WSL
- WSL2’s Networking Modes: NAT and Mirrored, Explained
Sources: