Workaround for broken DNS in WSL2

Posted by Valentin Heidelberger on April 29, 2022 Tags: linux wsl windows technology

WSL2 uses the Windows host’s DNS - so if DNS is working on Windows, normally WSL2 should be fine as well! Unfortunately, DNS in WSL2 just randomly stopped working for me at some point. The /etc/resolv.conf file generated by WSL2 was still being correctly created but DNS just wouldn’t work.

Luckily there are plenty of ways to work around this, because we can just do DNS ourselves inside the Linux running in WSL2.

Prerequisite: Deactivate auto-generation of /etc/resolv.conf

For any of the following scenarios we need to stop WSL2 from writing to /etc/resolv.conf first, because we need to persistently modify it.

In WSL2, open a file /etc/wsl.conf, paste the following content and save:

[network]
generateResolvConf = false

If you also want to stop WSL2 from overwriting your /etc/hosts.conf, you need to add another line like so:

generateHosts = false

To have these changes take effect, you need to restart WSL2. Close your WSL2 window, start PowerShell as admin and run the following command to shutdown WSL2.

wsl.exe --shutdown

See here for reference, if you want to know in detail why this is necessary.

After the command has finished successfully, you can start WSL2 again.

Next, we’ll set up DNS inside Linux. There are multiple ways of varying complexity depending on your use case.

One remote DNS server

If you just need to use one DNS server like a public DNS or a server provided by your organization that can resolve everything you need, this is the most simple setup.

In WSL2, open the file /etc/resolv.conf, paste the following content and save:

nameserver  <your dns server>

If you wanted to use CloudFlare’s public DNS for example, it’d look like this:

nameserver  1.1.1.1

Multiple remote DNS servers

If you need to use multiple DNS servers, you’ll have to setup your own DNS server, which in turn uses the remote DNS servers you need. Don’t worry, it’s easy!

For example, in my WSL2 Ubuntu environment I need to use a specific DNS server for an internal domain and a public DNS for everything else.

Install dns server software dnsmasq

Firstly, install the dns server dnsmasq. You might need to use a different package manager depending on your Linux distro. This works on Ubuntu and other Debian-based distros:

sudo apt update && sudo apt install -y dnsmasq

Configure dnsmasq

After dnsmasq has been installed, open it’s config file /etc/dnsmasq.conf, add the following and save.

dnsmasq comes with a big config file full of deactivated settings as reference. If you want to keep all that, that’s no problem - I did it as well and just appended the following:

server=/<your internal domain>/<your internal DNS server>
server=<your external DNS server>
no-dhcp-interface=

<your internal DNS server> will only be used for domains under <your internal domain>. You can add as many of these as you want, if you have multiple internal domains for example.

<your external DNS server> is the public DNS server and will be used for everything else.

no-dhcp-interface= deactives the DHCP and TFTP capabilities of dnsmasq, because we only want it to provide DNS.

So if I have an internal domain called mycompany.internal, an internal DNS server 192.168.1.53 and the public CloudFlare DNS 1.1.1.1, it’d look like this:

server=/mycompany.internal/192.168.1.53
server=1.1.1.1
no-dhcp-interface=

Get dnsmasq ip address

Now that we have installed and configured dnsmasq, we need to configure our Linux to use it as for DNS. We need to find the IP address dnsmasq is listening on first.

This can be accomplished with netstat, which you can install with the package net-tools.

sudo apt install -y net-tools

This command should give us just the IP we need:

netstat -tulpen | grep dnsmasq | awk '{ print $3 }'

If it doesn’t, make sure dnsmasq is up and running. If it’s not, check for errors in the config file.

Set dnsmasq as DNS server to be used

Open /etc/resolv.conf and add the IP you’ve just found. In my case it was 127.0.0.53. Save the file to start using dnsmasq as DNS server.

nameserver  127.0.0.53

Start dnsmasq automatically at WSL start (hacky)

Since WSL2 is not a real Linux, we can’t use systemd or similar good ways to automatically start our newly configured DNS server for now (systemd is supposed to be added to Ubuntu WSL in the future).

I didn’t dig for a perfect solution here tbh, because I’m using Windows and WSL2 involuntarily.

That’s why I just wrote a tiny Shell script which checks whether dnsmasq is running and, if it doesn’t, starts it. This runs every time I open a shell in WSL2.

It’s not pretty and in a real Linux environment I’d never do it this way but for this specific use-case it’s totally fine imho.

Open your ~/.bashrc and append the following at the end of the file. If you’re using a different shell, such as ZSH, use the corresponding rc file like ~/.zshrc instead of ~/.bashrc.

service dnsmasq status > /dev/null
if [ $? -eq 3 ]; then echo "Dnsmasq not running, please provide sudo pw to start it..."; sudo service dnsmasq start; fi

Next time you start a shell, you’ll be prompted to enter your password to start dnsmasq, if it’s not yet running.