Troubleshooting WireGuard DNS Issues

When your WireGuard tunnel is up, and you can access websites by IP address — but not by DNS name — you have a WireGuard DNS problem.

For example, if this first cURL command returns a response (even a 404 response):

$ curl -I https://9.9.9.9
HTTP/2 404
server: h2o/dnsdist
date: Thu, 07 Sep 2023 21:33:27 GMT
content-type: text/plain; charset=utf-8
content-length: 9

But this second command returns a DNS-resolution error:

$ curl -I https://quad9.net
curl: (6) Could not resolve host: quad9.net

You have a WireGuard issue with DNS. This article will show you how to diagnose and fix it.

There are two flavors of this problem:

  1. The first is when you’re trying to use a WireGuard-Specific DNS Resolver to which you can’t connect;

  2. The second is when you can’t connect to your Default DNS Resolver while the WireGuard tunnel is up.

Tip
If DNS works when using cURL, but not when using a web browser, check your browser’s DNS settings — you may have customized your browser to override your system’s DNS settings.

WireGuard-Specific DNS Resolver

If you’ve added a DNS setting to your WireGuard config file, like the following, you’re trying to use a specific DNS resolver with your WireGuard interface:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
DNS = 10.10.0.2

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.10.0.0/16

When this is the case, ask these five questions:

Do I even want a WireGuard-specific DNS resolver?

If you simply copied-and-pasted your WireGuard DNS setting from somewhere else, ask yourself if you actually want to use a different DNS resolver than normal when your WireGuard tunnel is up.

If the answer is no, remove the DNS setting from your WireGuard configuration — you don’t want it, and you don’t need it.

Tip

There are two reasons why you might want to use a WireGuard-specific DNS resolver:

  1. You’re using the WireGuard tunnel to access a private network, and you want to use a private DNS resolver from that network to resolve DNS names to private resources on that network.

  2. You’re using the WireGuard tunnel to access the Internet, and you want to use a DNS resolver that matches your WireGuard exit point to resolve DNS names to resources on the Internet.

Is the DNS resolver up?

The next question to ask is whether or not the DNS resolver you’ve set in your WireGuard configuration (like 10.10.0.2 in the above example) is up and listening for external connections on UDP port 53.

If you have admin access to the DNS resolver, log into it, and check that it’s listening on UDP port 53:

$ sudo ss -punl sport :53
State   Recv-Q  Send-Q            Local Address:Port   Peer Address:Port  Process
UNCONN  0       0                             *:53                *:*      users:(("coredns",pid=345,fd=8))

In the above example, the ss command shows that the CoreDNS daemon is listening on UDP port 53 for all the server’s IP addresses (indicated by *:53 in the Local Address:Port column).

However, in the below example, while the ss command shows a DNS resolver running, this resolver is actually the systemd-resolved daemon listening on the server’s own loopback interface — which isn’t accessible to external connections:

$ sudo ss -punl sport :53
State   Recv-Q  Send-Q            Local Address:Port   Peer Address:Port  Process
UNCONN  0       0                 127.0.0.53%lo:53          0.0.0.0:*      users:(("systemd-resolve",pid=1570,fd=13))

If you don’t have admin access to the DNS resolver, try querying it from some other system that should have access to it. For example, if you’re trying to use 10.10.0.2 as your DNS resolver, try querying it from another system on the same 10.10.0.0/16 network:

$ nslookup quad9.net 10.10.0.2
;; communications error to 10.10.0.2#53: host unreachable
;; communications error to 10.10.0.2#53: host unreachable
;; communications error to 10.10.0.2#53: host unreachable
;; no servers could be reached

The above example shows that the DNS resolver is not accessible from the system from which we’re trying to query it.

Tip

You can use several different command-line tools such as nslookup, host, or dig to query a specific DNS resolver (9.9.9.9) for a DNS name (quad9.net):

$ nslookup quad9.net 9.9.9.9
Server:		9.9.9.9
Address:	9.9.9.9#53

Non-authoritative answer:
Name:	quad9.net
Address: 216.21.3.77
Name:	quad9.net
Address: 2620:0:871:9000::77
$ host quad9.net 9.9.9.9
Using domain server:
Name: 9.9.9.9
Address: 9.9.9.9#53
Aliases:

quad9.net has address 216.21.3.77
quad9.net has IPv6 address 2620:0:871:9000::77
quad9.net mail is handled by 10 mx4.quad9.net.
quad9.net mail is handled by 20 mx2.quad9.net.
quad9.net mail is handled by 5 mx1.quad9.net.
$ dig quad9.net @9.9.9.9

; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> quad9.net @9.9.9.9
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65528
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;quad9.net.			IN	A

;; ANSWER SECTION:
quad9.net.		1200	IN	A	216.21.3.77

;; Query time: 20 msec
;; SERVER: 9.9.9.9#53(9.9.9.9) (UDP)
;; WHEN: Thu Sep 07 16:07:45 PDT 2023
;; MSG SIZE  rcvd: 54

Do I have a route to the DNS resolver?

The next question to ask is whether or not your system is able to route traffic to the WireGuard-specific DNS resolver through the correct network interface.

99% of the time, you want to route traffic to a WireGuard-specific DNS resolver trough the WireGuard interface itself.

In the following WireGuard configuration file, we’ve specified a DNS resolver of 172.17.0.2, but the AllowedIPs setting of the only WireGuard peer for this interface is restricted to 10.0.0.0/24:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
DNS = 172.17.0.2

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.0.0.0/24

This means that queries to the WireGuard-specific DNS resolver will not be routed through the WireGuard interface — which is almost always a problem.

The solution is either to correct the DNS setting of our WireGuard config to use a DNS resolver that is actually within one the peers’ AllowedIPs ranges; or to expand that range to include the DNS resolver. In this case, we’ll add the DNS resolver to the AllowedIPs ranges:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
DNS = 172.17.0.2

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.0.0.0/24, 172.17.0.2/32

We can verify that our system will now correctly route the DNS resolver 172.17.0.2 out its wg0 interface by running the following command iproute2 command:

$ ip route get 172.17.0.2
172.17.0.2 dev wg0 src 10.0.0.1 uid 1000
    cache

Is a firewall blocking connections to the DNS resolver?

The next question to ask is whether a firewall between your system and the DNS resolver is blocking queries to it.

If you know the DNS resolver is up and available, and that you have a route to it when your WireGuard interface is up, you should be able to query the DNS resolver from your local system using a tool like nslookup, host, or dig (as mentioned above).

If you instead get an error when you try to query the DNS resolver, there is likely a firewall between you and the DNS resolver blocking your UDP port 53 connections to it:

$ nslookup quad9.net 10.10.0.2
;; communications error to 10.10.0.2#53: host unreachable
;; communications error to 10.10.0.2#53: host unreachable
;; communications error to 10.10.0.2#53: host unreachable
;; no servers could be reached

You can try to further pinpoint the problem by running tcpdump as shown in the Troubleshooting WireGuard with Tcpdump article. To use the technique shown by the article, make the following two adjustments to the commands shown in the article:

  1. Substitute the DNS resolver address (eg 10.10.0.2) for the address used by the tcpdump commands (ie 192.168.200.22).

  2. Substitute the test DNS query (eg nslooklup quad9.net 10.10.0.2) for the whole cURL command (ie curl 192.168.200.22).

Tip

Make sure you also use the correct WireGuard listen ports in the tcpdump commands, if different than the example used by the article. For example, when using the following WireGuard config on your local workstation:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
ListenPort = 11111
DNS = 10.10.0.2

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 10.10.0.0/16

You should run this tcpdump command on your local workstation:

$ sudo tcpdump -niany udp port 11111 or host 10.10.0.2

And this tcpdump command on the WireGuard server:

$ sudo tcpdump -niany udp port 51820 or host 10.10.0.2

Am I using systemd?

The last question to ask is whether you’re using systemd on the client system.

If you are, you’re probably using the systemd-resolved stub resolver to resolve DNS queries on your system. In that case, try replacing the DNS setting in your WireGuard config with a PostUp command that runs systemd’s resolvectl utility to alter your DNS settings when the WireGuard interface starts up:

#DNS = 10.10.0.2
PostUp = resolvectl dns %i 10.10.0.2

See the WireGuard DNS Configuration for Systemd article for more information.

Default DNS Resolver

If you haven’t added an explicit DNS setting (or resolvectl command) to your WireGuard config file, you’re using the same default DNS resolver when your WireGuard tunnel is up as when it is down.

In that case, if DNS is not working when your WireGuard tunnel is up, it means that connections to your default DNS resolver are being routed through the WireGuard tunnel when the tunnel is up — but your default DNS resolver is not accessible through the WireGuard tunnel. There are two ways to fix this:

Add a WireGuard-specific DNS resolver

Most of the time when you can’t access your default DNS resolver when your WireGuard tunnel is up, you’ll want to configure a specific DNS resolver to use for the WireGuard tunnel. To do so, add a DNS setting to your WireGuard config:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
DNS = 9.9.9.9, 149.112.112.112, 2620:fe::fe, 2620:fe::9

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 0.0.0.0/0, ::/0

If you’re using systemd, instead add a PostUp command that runs the resolvectl utility to achieve a similar result:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
PostUp = resolvectl dns %i 9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net; resolvectl domain %i ~.

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 0.0.0.0/0, ::/0

Add a route for your default DNS resolver

Alternatively, if you want to continue to use your default DNS resolver when your WireGuard interface is up (and it is not already working out-of-the-box), you’ll need to add an explicit route for the resolver. To add a route for it, you need to know what the resolver’s IP address is, and what route is normally used to access it when your WireGuard interface is not up.

On Linux, traditionally you could find the IP address of your default DNS resolver simply by inspecting the nameserver field of your /etc/resolv.conf file:

$ grep nameserver /etc/resolv.conf
nameserver 127.0.0.53

However, if your system is running its own stub DNS resolver (as is the case with the example above), the nameserver will simply be the local host (ie part of the 127.0.0.0/8 network) — and you’ll have to check the stub resolver in order to figure out what resolver the stub itself is using.

If your system is using the systemd-resolved stub resolver, use the resolvectl utility figure out what default DNS resolver it’s actually using:

$ resolvectl status
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub

Link 2 (eth0)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.10.0.2
       DNS Servers: 10.10.0.2
        DNS Domain: corp

Then, when the WireGuard interface is down, use the ip route get command to determine the correct route to the resolver:

$ ip route get 10.10.0.2
10.10.0.2 via 192.168.1.1 dev eth0 src 192.168.1.23 uid 1000
    cache

Finally, add the route (using a PreUp command — and delete it with a corresponding PostDown command) in your WireGuard config:

# /etc/wireguard/wg0.conf

# local settings for Your Workstation
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.0.1/32
PreUp = ip route add 10.10.0.2 via 192.168.1.1 dev eth0
PostDown = ip route del 10.10.0.2 via 192.168.1.1 dev eth0

# remote settings for WireGuard Server
[Peer]
PublicKey = fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=
Endpoint = 203.0.113.2:51820
AllowedIPs = 0.0.0.0/0, ::/0