Pi-hole v6 receives a DNS query on port 53 and then decides whether the packet that arrived is allowed to be answered. On a WireGuard host that second decision is usually why a client gets a timeout while the resolver looks healthy from the LAN. The port is open, FTL is running, yet nothing comes back from 10.90.0.1. Three things interact before an answer leaves: dns.interface, dns.listeningMode and the firewall in front of both.

The listening socket does not decide who gets an answer

FTL opens UDP and TCP listeners on port 53. In the default configuration the socket is often wildcard, so ss -lunp | grep :53 shows 0.0.0.0:53 and [::]:53 whether or not VPN clients are served. A wildcard socket accepts the packet, then FTL applies its interface and source rules before replying. That is why "the port is listening" proves very little about a tunnel client.

Read the running configuration instead of the file you believe is active. pihole-FTL --config dns.interface and pihole-FTL --config dns.listeningMode print the effective values, and they can differ from the host file when a container, environment variable or deployment manager owns the setting. The Pi-hole FTL configuration reference lists the modes and which one keys on the interface.

LOCAL asks which subnet, SINGLE asks which interface

LOCAL, the default, answers queries whose source address belongs to a subnet configured on one of the host interfaces. A simple wg0 with 10.90.0.1/24 passes that test, because the client at 10.90.0.2 sits inside the tunnel subnet. A routed client on the far side of a peer, or one hidden behind NAT in a different prefix, does not pass it even though the packet arrived on wg0, and no query appears in the log.

SINGLE changes the question. It accepts queries that arrive on the interface named by dns.interface, with loopback included, and ignores the source subnet. That is the mode that survives a chained or routed VPN topology. Set both keys together, because a mode without a named interface is not a complete instruction:

sudo pihole-FTL --config dns.interface "wg0"
sudo pihole-FTL --config dns.listeningMode "SINGLE"

BIND changes the socket, and that has a cost

BIND does not filter a wildcard socket; it binds port 53 only to addresses that exist on the named interface, so the kernel refuses packets that arrive elsewhere. It is stricter than SINGLE and not a synonym for it. The cost appears at boot: if wg0 is not up when FTL starts, there may be no address to bind, and the resolver can run without a listener until the interface appears and FTL restarts.

Choose BIND when you control the startup order, for example a systemd unit ordered after the WireGuard interface, and when you want the kernel to drop stray packets before FTL sees them. Choose SINGLE when the tunnel comes and goes, or when the interface is created by a tool that starts after FTL.

The firewall is the second gate

A correct FTL mode still fails if the host firewall drops port 53 on wg0. Admit the VPN source range for both protocols and keep the Internet side closed. In nftables, with the existing input chain in the inet filter table, the rules look like this:

sudo nft add rule inet filter input iifname "wg0" ip saddr 10.90.0.0/24 udp dport 53 accept
sudo nft add rule inet filter input iifname "wg0" ip saddr 10.90.0.0/24 tcp dport 53 accept

Add matching IPv6 rules if the tunnel carries IPv6, and put these before the chain drop policy. If the host uses ufw or firewalld, express the same match in that manager rather than mixing tools. Do not forward port 53 from the Internet router; clients should reach the resolver VPN address across the tunnel.

Two paths stay separate. Client queries terminate at FTL. FTL own upstream lookups leave according to the host routing table, which may or may not use the tunnel, so a resolver that answers but returns nothing useful is an upstream problem. The web admin and API listen on their own ports and need their own restriction.

Verify the answer and the refusal

From a VPN client, query the resolver address over both protocols: dig @10.90.0.1 example.com and dig +tcp @10.90.0.1 example.com. Then check the query log in the admin interface to confirm FTL recorded the query. A client timeout with no log entry points at the firewall or the interface filter; a log entry with no answer points at upstream resolution.

Test the negative path from a host outside the VPN. dig @10.90.0.1 example.com should time out, and ss -lunp on the resolver should not show port 53 bound to a public address. Restart the service once, then reboot the host and confirm the resolver still answers after wg0 reappears. A query that works only until the next reboot is not a finished change.

Artitgels cun il medem tema