Skip to content

Windows 11 WSL2 DNS and OpenVPN Split Tunnel#

On modern Windows 11 + WSL 2, the reliable VPN-friendly DNS path is WSL DNS tunneling. If you want Linux tools inside WSL to follow the same DNS policy as Windows apps, keep /etc/resolv.conf auto-generated and let the Windows DNS Client make the routing and suffix decisions.

This post focuses on the current Windows 11 + WSL 2 model, not the older manual resolv.conf workaround. It also shows how to keep OpenVPN in split-tunnel mode while preserving internal DNS resolution in both Windows and WSL.

Modern WSL2 DNS on Windows 11#

In Windows, configure %UserProfile%\.wslconfig:

[wsl2]
# Optional, already the default on current WSL releases
# dnsTunneling=true

In WSL, keep /etc/wsl.conf simple:

[network]
# Optional, already the default; keep it enabled for WSL DNS tunneling
# generateResolvConf=true

If you also want systemd, enable it separately:

[boot]
systemd=true

Then restart WSL from PowerShell:

wsl --shutdown

Notes:

  • dnsTunneling=true is the feature that makes WSL DNS follow Windows DNS Client behavior. Microsoft specifically recommends it when you use a VPN, because many VPN clients rely on NRPT rules, and those rules are only applied to WSL DNS queries when DNS tunneling is enabled.
  • What matters for DNS tunneling is that you do not disable generateResolvConf, because Microsoft notes DNS tunneling will not be successfully enabled if generateResolvConf=false.
  • systemd=true is optional. It is useful for services and timers, but it does not need to "own" /etc/resolv.conf in this setup.

What /etc/resolv.conf Should Look Like#

With DNS tunneling enabled, WSL generates resolv.conf and points it at the Windows-side DNS tunnel IP:

$ cat /etc/resolv.conf
# This file was automatically generated by WSL.
nameserver 10.255.255.254
search home.lan corp.example

Notes:

  • 10.255.255.254 is WSL's dnsTunnelingIpAddress.
  • In NAT mode with DNS tunneling enabled, WSL brings over Windows DNS suffixes into the search line. The order follows Windows DNS behavior: global suffixes first, then supplemental suffixes, then per-interface suffixes.
  • The exact search values change when your network or VPN state changes.

The Resolution Path#

When a WSL app resolves app.corp.example, the flow is:

  1. The Linux app reads /etc/resolv.conf and sends the query to 10.255.255.254.
  2. WSL tunnels the request directly to Windows instead of sending a normal DNS packet from the VM to the host.
  3. The Windows DNS Client applies Windows DNS policy: NRPT rules, DNS suffix search order, per-interface DNS servers, and Windows-side DoH or DoT settings.
  4. Windows forwards the query to the appropriate resolver, such as your home router or the VPN's internal DNS servers.

This is the key change on modern WSL 2: Windows, not a hand-configured Linux stub resolver, makes the final DNS policy decision.

Manual resolv.conf Mode (generateResolvConf=false)#

You can still disable WSL's generated resolv.conf and point Linux at systemd-resolved or another local resolver, but for modern Windows 11 + WSL 2 + VPN setups that is an advanced custom path, not the recommended one.

If you set generateResolvConf=false:

  • you opt out of the WSL-generated DNS tunnel configuration;
  • you become responsible for upstream DNS servers and suffix search behavior inside Linux;
  • WSL will no longer automatically mirror Windows VPN DNS behavior for you.

So if your main goal is "WSL should behave like Windows when the VPN connects," keep generateResolvConf=true.

OpenVPN Split Tunnel with DNS#

A split tunnel means only company routes go through the VPN. Your normal internet traffic keeps using the local connection, while internal company names still resolve correctly.

Here is the official OpenVPN Connect split-DNS documentation for mobile clients. The same DNS behavior also matches what this post relies on for Windows split DNS: https://openvpn.net/connect-docs/dns-servers-mobile-behavior.html

OpenVPN Client Configuration#

Use a client config like this:

part of split-tunnel.ovpn for OpenVPN Community version
# Prevent the VPN from replacing your normal internet route
route-nopull

# Send only company networks through the VPN
route 10.0.0.0       255.0.0.0       vpn_gateway
route 172.16.0.0     255.240.0.0     vpn_gateway

# Use company DNS on the VPN adapter
dhcp-option DNS 10.0.0.1
dhcp-option DNS 10.0.0.2

# Send DNS queries for company domains to the above VPN DNS servers
dhcp-option DOMAIN corp.example
dhcp-option DOMAIN internal.example

Option route-nopull#

redirect-gateway is a common default company VPN config. It pushes a full-tunnel default route, which means it forces to send all traffic through the VPN gateway instead of only company traffic. But that usually makes normal internet access slower and more fragile. Public traffic such as Microsoft Teams calls, public Docker image pulls, package downloads, and ordinary web browsing can all end up going through the corporate VPN path even though they do not need to.

route-nopull tells the client not to install server-pushed routes. That is what prevents the VPN from silently turning your split tunnel back into a full tunnel. In a typical split-tunnel setup, this already makes a pushed redirect-gateway ineffective, because the server can no longer replace your default route.

Manual routes required

The tradeoff is that you need to know the company private subnets in advance and add them manually. If you miss one, that internal network will stay on your normal local route and will not be reachable through the VPN. Depending on your environment, you may be able to mitigate part of this caveat with domain-based routing instead of relying only on static subnet routes. OpenVPN Access Server documents this here: Tutorial: Configure Domain Routing in Access Server. This requires Access Server 3.1+ and DNS server proxy support.

Option dhcp-option DNS and dhcp-option DOMAIN#

In split-tunnel mode, dhcp-option DNS and dhcp-option DOMAIN work together. dhcp-option DNS 10.0.0.1 tells the client which VPN DNS server to use, and repeated dhcp-option DOMAIN ... lines tell the client which domain suffixes should be resolved through that VPN DNS server instead of your local DNS path.

This is the OpenVPN Connect split-DNS behavior described in the official docs. In other words, a config like this:

dhcp-option DNS 10.0.0.1
dhcp-option DNS 10.0.0.2

dhcp-option DOMAIN corp.example
dhcp-option DOMAIN internal.example

means "send DNS for *.corp.example and *.internal.example to 10.0.0.1 and 10.0.0.2", all other DNS queries go through the local DNS path.

That matters for two reasons:

  • app.corp.example can be resolved through the VPN DNS servers while unrelated public domains keep using your normal local DNS path;
  • if the first domain is also used as the primary DNS suffix on Windows, short names like app can expand to app.corp.example, and WSL DNS tunneling can inherit that suffix into WSL's search line.

In practice, repeated dhcp-option DOMAIN lines can be used for multiple company domains. That matches the OpenVPN Connect split-DNS behavior described in the official docs, where added domains cause only those domains to use the VPN DNS servers.

OpenVPN Access Server docs note that Windows clients might only use the first domain provided in DNS Resolution Zones as the DNS domain suffix. So if short-name expansion matters, put your primary suffix first and verify it with ipconfig /all or Get-DnsClient.

OpenVPN Connect docs describe split-tunnel DNS this way: if a pushed VPN DNS server is present with no added domains, all DNS requests go to that VPN DNS server; if added domains are present, only DNS requests for those domains go there.

This is also why .local is a poor example for corporate DNS. .local is typically treated as mDNS, not normal unicast DNS, so it can make a healthy VPN setup look broken.

How to find company VPN DNS servers#

To get the correct internal DNS IPs:

  1. Connect to the VPN using the original working profile.
  2. In PowerShell, run ipconfig /all.
  3. Find the OpenVPN adapter.
  4. Copy the IPs listed under DNS Servers.
  5. Reuse those IPs in your split-tunnel config.

OpenVPN Community Client 2.7 and pull-filter#

This section is about OpenVPN Community Client 2.7 (the latest version as of March 2026) config syntax, not OpenVPN Connect.

pull-filter ignore is the cleanup step: it drops matching server-pushed directives before OpenVPN processes them. In practice, that keeps the OpenVPN Community client logs quieter and makes it explicit that your local client config is authoritative.

pull-filter ignore "dhcp-option"
pull-filter ignore "route "

Notes:

  • pull-filter uses prefix matching.
  • With route-nopull in place, you normally do not need pull-filter ignore "redirect-gateway", because route-nopull already prevents the pushed full-tunnel route from being installed. We still add pull-filter ignore "dhcp-option" and pull-filter ignore "route " because they stop the OpenVPN Community client from trying to process those pushed options and cluttering the logs with warnings such as:
Options error: option 'dhcp-option' cannot be used in this context ([PUSH-OPTIONS])
Options error: option 'route' cannot be used in this context ([PUSH-OPTIONS])

pay attention to trailing whitespace in "route "

Keep the trailing space in pull-filter ignore "route ". If you change it to pull-filter ignore "route", it also matches pushed options such as route-gateway, which can break manual routes that rely on vpn_gateway.

Verification#

Inside WSL, confirm that DNS tunneling is active:

grep -E '^(nameserver|search)' /etc/resolv.conf
# nameserver 10.255.255.254
# search home.lan corp.example

If you use systemd-resolved, /etc/resolv.conf may instead point to the local stub at 127.0.0.53. In that case, check the effective upstream DNS server:

 resolvectl status | grep -A 5 'Global'
Global
           Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
    resolv.conf mode: foreign
         DNS Servers: 10.255.255.254
Fallback DNS Servers: 1.1.1.1

That means Linux apps talk to the local stub first, but the real upstream is still WSL's Windows-side DNS tunneling IP (10.255.255.254).

Then test name resolution and routing:

  • getent hosts app.corp.example: confirms that a fully qualified internal name resolves.
  • getent hosts app: if you rely on short names, this confirms that the DNS suffix search list is working.
  • nc -zv app.corp.example 443: confirms both DNS resolution and reachability over the VPN, if the target app is actually listening on TCP port 443.
  • curl ifconfig.me: can be a quick sanity check that public traffic is not being forced through the VPN, but verify that the resolved ifconfig.me IP is not itself inside one of your VPN-routed prefixes.

If /etc/resolv.conf points directly at 10.255.255.254, you are using the simple WSL DNS tunneling layout. If it points at 127.0.0.53 but resolvectl status still shows 10.255.255.254 upstream, you are still using WSL DNS tunneling underneath a local systemd-resolved stub.

Comments