DNS Resolver configuration and setup

DNS in a Distributed Homelab: Control, Constraints, and What Actually Works

As internal services evolve and automation becomes more central, one challenge keeps resurfacing: DNS management across locations and sites.

Keeping track of dynamically allocated IPs, ephemeral services, and changing infrastructure does not get easier over time — it becomes a source of increasing complexity.

To address parts of this, I've used and tested:

These help structure inventory, but they don't fully solve the problem of how DNS should behave in a dynamic, distributed environment.

The Context: A Distributed Setup That Grew Organically

I run a shared homelab/data centre setup across multiple physical locations together with friends. As the system has grown, so has the complexity around:

Image of DNS sites

  • IP range management (dynamic public IPs and private ranges)
  • Routing (VPN)
  • DNS resolution (internal and external services)

Services move, scale, and get redeployed.

Once you start down the rabbit hole, static assumptions break down quickly ;-)

DNS Setup and configuration

The Architecture: Simple, Distributed, and Actually Working

We've converged on a solution that is both simple and robust — and, importantly, it works in practice.

At the core is BIND, used to build a layered DNS structure.

Key principles

  • Each location runs its own local DNS resolver.
  • Resolvers point to secondary resolvers at other sites.
  • Data and resolution capability are distributed across locations.

Each resolver then forwards queries to internal DNS servers that:

  • Serve local IP ranges via internal zones.
  • Expose services through both internal and external URLs.
  • Enforce allow-lists for what can be resolved.

DNS as a Control Plane

A central idea in this setup is:

DNS is not just a lookup service — it is a control layer.

We log every request, and a "new request" will light up as a flare in the log rig.

Instead of allowing unrestricted resolution:

  • Unknown or non-approved queries are redirected to an internal "catch-all".
  • This catch-all acts as a controlled boundary.

From there, a deliberate "break glass" mechanism is available:

  • Authentication (username/password)
  • Temporary "resolve-all" access (e.g. during installation)
  • Or permanent allow-listing of specific domains

This introduces a balance between:

  • Security
  • Operational control
  • Flexibility when needed

Why This Matters

Letting everything resolve freely may feel simpler — but it quickly leads to:

  • Loss of visibility
  • Increased attack surface
  • Harder debugging
  • Unpredictable behaviour across environments

By constraining DNS and making access explicit, the system becomes:

  • More predictable
  • Easier to reason about
  • Easier to operate over time

The Trade-off: Friction vs Control

Yes — this introduces friction.

But it's intentional friction.

Most of our systems have no need to resolve external services. Exceptions are mainly updates and short bursts of activity — "nothing that can't be solved using a cron job."

We trade a bit of convenience for:

  • Traceability: We log behaviour to neo4j, and all new entries light up and are evaluated.
  • Reproducibility: When we redeploy a system, we know what it is supposed to talk to, and can preconfigure this during the instrumentation process using Ansible.
  • A clearer understanding of what our systems are actually doing.

Open Questions

Even though this setup works well, there are still open areas worth exploring:

  • We have still not reached a final plan for how to model inventory (IPs, services, DNS) consistently.
  • Where is the right boundary between automation and manual control?
  • Should DNS evolve into a policy engine — or stay a pure resolver?
  • How much dynamism can DNS realistically support before abstraction is required?