The internal wiki loads in Edge, and git clone inside the distribution times out. That single observation is usually read as "the VPN is blocking WSL2", and the next hour goes into editing /etc/resolv.conf. The more useful reading is that WSL2 never had the host's network path to begin with: it runs in a utility VM with its own adapter, its own routes and a resolver file that Windows generates for it. Everything that follows depends on which of those three you are actually missing.

The VM has its own stack, metric and resolver

In the default NAT mode, the distribution sits behind a virtual switch and reaches the internet through the host, but it does not see the host's adapters. ip addr shows one interface with a private address, ip route shows a default through that switch, and /etc/resolv.conf is a generated file pointing at a stub resolver Windows chose. None of that changes because a VPN client came up on the Windows side.

This is why "the browser works" proves so little. The browser uses the host stack, the tunnel and the host's resolver policy. Your shell uses a different stack that happens to share a physical NIC. They agree in the happy case and diverge exactly when the VPN installs a filter, rewrites the DNS list, or adds a route for the virtual switch's subnet.

Two tests settle which world you are in, and they have to run inside the distribution:

getent hosts example.com
curl -sS -m 8 -o /dev/null -w "%{http_code}\n" 1.1.1.1
ip route
cat /etc/resolv.conf

Names failing while a literal address answers is a resolver problem. Names and addresses failing together is a path problem, and no amount of resolver editing will touch it. Windows-side evidence comes next: Get-NetAdapter | Get-DnsClientServerAddress and route print -4 show whether the tunnel claimed the subnet your VM lives on.

Myth one: the corporate DNS servers fix it

Editing the resolver file is the classic advice, and it is right about half the time. When it is right, it is right for a specific reason: the generated file points at a resolver that only knows public names, so a query for intranet.example.internal comes back as nonexistent even though the path is fine.

Pinning the VPN's servers means stopping Windows from regenerating the file, because any file you write by hand is replaced at the next start:

[network]
generateResolvConf = false

Save that as /etc/wsl.conf, run wsl --shutdown from Windows, then start the distribution and write /etc/resolv.conf with the tunnel's servers first and a public resolver only as a fallback. This is a durable change with a maintenance cost: the file no longer follows the host, so when the corporate DNS addresses change the distribution keeps asking the old ones. Someone has to own that file.

It also does nothing when the failure is broader. If curl to 1.1.1.1 times out, the tunnel client is filtering the virtual switch or routing the VM's subnet into the tunnel, and a correct resolver just gives you names that still cannot be reached.

Myth two: the metric change sticks

When names resolve but traffic leaves by the physical adapter, interface metrics are the lever. Give the VPN adapter a high cost and the WSL switch a low one:

Get-NetAdapter | Where-Object InterfaceDescription -Match "AnyConnect" | Set-NetIPInterface -InterfaceMetric 4000
Get-NetIPInterface -InterfaceAlias "vEthernet (WSL)" | Set-NetIPInterface -InterfaceMetric 1

Then remember that on many client builds the tunnel adapter is recreated on every connect and the metric returns to its default. A one-off command is a fix that expires quietly, usually over a weekend, and the ticket comes back with "it worked last week". A scheduled task triggered by the VPN event, or a script the user runs after connecting, is the honest version of this repair.

The same expiry applies to host routes added for a single stubborn prefix. They are worth having as a diagnostic, because they tell you whether the problem is one destination or the whole path, but they are not an architecture.

Myth three: one fix covers every Windows build

ApproachWhat it actually does
Resolver pinFixes private names only; no effect on path failures
Sidecar distributionForwards traffic over a socket to a helper VM, so the switch's filter no longer matters
Mirrored networkingGives the distribution the host's interface set; also enables DNS tunnelling

wsl-vpnkit is the sidecar pattern: a small distribution owns the host-side path and forwards for other distributions over a virtual socket, so application traffic stops depending on the filtered switch. It needs the stock path to work, which means removing networkingMode and dnsTunneling overrides from %UserProfile%\.wslconfig, restoring generateResolvConf = true in each working distribution, and starting the service at login.

Mirrored mode is the built-in alternative on Windows 11 22H2 and later, and it is one setting plus a shutdown:

[wsl2]
networkingMode=mirrored

Inside the distribution ip addr then lists the host adapters, the tunnel may appear among them, and DNS tunnelling forwards lookups through the Windows resolver even when the client rewrites the DNS list. It is not universal: some filter drivers still drop the mirrored path, and Hyper-V firewall rules may need an explicit allow. Microsoft's WSL networking guide is the page to check against your build before applying a recipe written for a newer one.

Test three things after every change

Pick one mechanism per machine and write it down, because the two combine badly. A sidecar on top of mirrored mode doubles the forwarding, and a mirrored host with a stale sidecar entry in .wslconfig tends to fail after an upgrade rather than before it.

Whatever you chose, the acceptance test is the same three requests inside the distribution: an internal name, a public name and a raw address. Then repeat them after disconnecting and reconnecting the tunnel, and after wsl --shutdown. Internal name failing alone means resolver policy; internal and public names failing together with a working address still means resolver policy; everything failing means the path.

If one internal host fails while the rest work, stop looking at the resolver and look for a missing route for that prefix. If everything works until the next reconnect, you have found a fix that depends on runtime state, and it needs to be re-applied automatically or replaced with one of the two mechanisms above.

Inyandiko zihuye