Putting a Pi-hole address into an OpenVPN profile does not create a path to it, grant permission to query it, or guarantee that the client uses it. With Pi-hole on a separate LAN machine, four things have to line up: the route from the client, the forward from the gateway, the return path to the pool, and the reception policy on Pi-hole itself. Each failure looks different in a log. Working through them in that order is faster than fixing the last one first. The usual shortcut, opening recursion to every origin, trades a small problem for a permanent one.

What the pushed DNS option does and does not do

The server advertises a resolver with the directive below, assuming 192.168.50.53 is the Pi-hole host. The client receives the option and, depending on its platform and resolver integration, applies it to the tunnel interface or ignores it in favour of a local stub. The route line matters as much as the DNS line, because a resolver the client cannot reach is no resolver at all. None of this is visible from the server side.

server 10.90.0.0 255.255.255.0
push "dhcp-option DNS 192.168.50.53"
push "route 192.168.50.0 255.255.255.0"

Receiving an option is not the same as using it. A Linux client with a resolver manager may accept the pushed server and send only certain domains to it; a client that manages DNS itself may overwrite the setting entirely. Pushed routes are also restricted on some platforms, which leaves the resolver unreachable even though the option was accepted. Check on the client which server answers, not on the server what was sent.

The option also says nothing about the server side. DNS traffic still has to travel from the client to the gateway and from the gateway to Pi-hole before any option matters. A missing route and a refused query look alike from the client, so confirm forwarding on the gateway first.

The return path from Pi-hole to the pool

If the client's source address is preserved, Pi-hole replies to an address inside the VPN pool. Its own routing table probably knows nothing about that pool, so the reply goes to its default gateway and disappears. The query log shows a request with no matching answer, which is easy to misread as a firewall problem.

Give the LAN router a static route for the pool with the OpenVPN gateway as the next hop, or translate the source on the gateway before forwarding to Pi-hole. The first keeps per-client addresses visible in Pi-hole's query log. The second removes the return-route requirement but makes every query look like it came from the gateway. Neither is wrong; they answer different questions.

If per-client reporting or per-client policy matters, the static route is the better trade. If the LAN is managed by someone else and a route is not available, translation is the pragmatic option, with the loss of client identity written down. Record which one you chose, because every later log question depends on it.

Pi-hole's listening mode for routed clients

The interface listening behaviour dropdown on the Settings page under DNS decides which sources Pi-hole answers. Its strictest option accepts only clients on directly attached networks; the option that allows devices at most one hop away extends that to subnets behind an adjacent router, which is what a routed VPN client looks like. The packet does arrive on the LAN interface, but its source address belongs to a different subnet. That is a policy refusal rather than a routing failure, and the difference matters when you read logs.

The Pi-hole listening guide separates locally attached sources from routed origins, and the arrival interface is not the test. Choose a mode that permits the VPN pool without permitting the whole internet, then pair it with a firewall rule that accepts DNS from the pool only.

Opening recursion to all origins to make the first query work is the wrong repair. It turns a private resolver into an open one, and the fix is a scoped permission rather than a global one.

Match the answer to the query log

Ask the client, then check the server, and compare the two. On a systemd-resolved client the commands below report which resolver produced an answer and which servers are configured per link. Use a name that only Pi-hole knows, because a direct query to the Pi-hole address proves reachability rather than configuration.

resolvectl query intranet.example.lan
resolvectl dns

The expected answer names 192.168.50.53 as the server and returns an address that only that resolver would know. Then look at Pi-hole's own view: pihole -t follows the query log live, and every line carries the source address. If the source is the client's tunnel address, the pool route is intact. If it is the gateway's LAN address, source translation is in play and the client's own address will never appear.

If nothing appears in the log at all, the query never reached Pi-hole and gateway forwarding is the next thing to check. If the query appears and no answer comes back, the reply path is the problem, which is the previous section. Splitting the failure this way stops a routing fault from being mistaken for a firewall rule.

Check the gateway's forward rule

The gateway has to forward the client's DNS packets toward Pi-hole, and the rule that does it is worth reading rather than assuming. The commands below list the rules that touch port 53 with counters, on an nftables host and on an iptables host. A rule that exists but shows zero matches is the signal that traffic is being dropped before it is translated.

sudo nft list ruleset | grep -A3 'dport 53'
sudo iptables -t nat -L POSTROUTING -n -v | grep 53

Read the counters, not just the rules. A rule that never matches means the packet leaves by another interface, or the client never sent it in the first place. A rule that matches on every query means forwarding works and the problem is further along the path.

Whichever tool the host uses, keep the check narrow: the pool as source, port 53 as destination, and Pi-hole as the only permitted target. A broad forward rule that also allows port 80 is a different problem waiting to happen.

Ebitabo ebifaananyi