Tunnel is up, so why does the private name still fail?
A working tunnel does not mean the client asked the right resolver. Check which one answered, then forward only the zone that actually needs it.
Everything about the connection looks right: the tunnel is up and the console shows the private endpoint, yet every request fails on name resolution. The natural move is to blame the tunnel, which usually turns out to be innocent. The query is being sent to a server that knows nothing about the private zone. No routing change will alter that answer, because the problem is who was asked.
Which resolver did the query reach?
Find the resolver attached to each link before you look at the tunnel at all. Linux exposes per-link servers through the resolver status command, and macOS shows scoped resolvers in its DNS configuration dump. If the answer still comes from the home router, the tunnel link was never consulted.
resolvectl status
scutil --dns | head -40
dig +short internal.example.com
Two stages get conflated here: a server pushing a resolver address, and a client installing it on the active link. A profile can contain the option, and a log can show it arriving, while the operating system keeps using the old server. Compare the log with what the link reports, and trust the link.
Use an address the cloud network really answers on
Cloud networks typically answer private names through a reserved address offset by two from the base of the range, so a network starting at 10.0.0.0 answers on 10.0.0.2. Some providers also publish a link-local resolver that works without knowing the range, which helps when the network was built by somebody else. Query the candidate address directly before you push it to anyone. An address that is not a resolver looks exactly like a firewall drop, because the query simply never gets an answer.
If the direct query answers from inside the network but not across the tunnel, the resolver is fine and the path between the two points is not. That result moves the investigation to routes, forwarding and access rules rather than to the client profile.
A tunnel does not carry the name space
Packets cross; names do not. Each side has to be told which namespace belongs to the other, and that is a separate object owned by whoever holds the zone. The usual shape is a forward for one namespace, pointed at a resolver reachable over the tunnel.
In practice each direction needs its own rule, because the side that owns the zone is the side that has to send the query on. Both directions need the query source routed and permitted, and the answering side must return replies through the tunnel rather than toward the internet. Keep the forward narrow, so that only the private namespace depends on the link. Sending every query across it turns a small dependency into a total outage the moment the tunnel drops.
Walk the boundaries in order
Test the resolver installed on the active link, then a direct query to that resolver, then the same query from the far side, and finally the application using the same name. Each step isolates one boundary, so a failure tells you where to look instead of restarting the investigation. An answer that works while the application fails points at certificate trust or an API policy rather than at DNS. Those faults are real, and no resolver change will repair them.
Write down which step failed. That single line is what the next person needs, and it takes ten seconds to record.
Keep the chain where the next person can find it
Record the pushed resolver, the forwarding rule, the permitted query source and the answering endpoint in one place. When the private name fails again, the record shows whether the tunnel moved, the resolver address changed, or a rule was narrowed. Without it, every incident starts from zero and ends with the same three guesses.
Keep the record short enough to stay accurate: four lines, a date, and the name of whoever owns each piece. A stale record is worse than none, because it looks authoritative while being wrong.