Skip to content

DNS

This page describes my homelab DNS setup, which is designed for speed, privacy, and internal resolution.

Overview

I use AdGuard Home as my primary DNS server for the network. AdGuard Home handles ad-blocking, filtering, and local DNS resolution. To enhance privacy and security, AdGuard Home forwards queries to a local Unbound instance configured to use DNS-over-TLS (DoT) with external resolvers.

This setup allows:

  • Centralized DNS filtering and logging via AdGuard Home
  • Secure, encrypted DNS queries to external resolvers
  • Redundant and distributed DNS across multiple nodes

Resolvers

I maintain 4 Unbound resolvers distributed across my nodes:

Node Resolver Notes
Node 1 Unbound 1
Node 2 Unbound 2
Node 3 Unbound 3
Node 1 Unbound 4 DNS64 turned off

DNS64 is turned off for Unbound 4 for split-tunnel VPN clients

Why Unbound?

AdGuard Home can do DNS64 on it's own, but there is actually a CNAME resolution bug where it will not follow a CNAME record properly and synthesise it to a NAT64 address.

See this issue on GitHub.

To fix this, I put Unbound behind AdGuard Home and then configured Unbound to synthesise NAT64 addresses.

Syncing AdGuard Instances

Node 1 contains the master/primary AdGuard Home instance which then has it's config taken and synced to the other 3.

There is no configuration needed for the DNS64 turned off node, since it is Unbound that handles that.

These are all synced with adguardhome-sync.

Here is my current config for adguardhome-sync:

cron: "0 */2 * * *"

runOnStart: true

continueOnError: true

origin:
  url: https://adguard-lxc.localdomain:443
  insecureSkipVerify: true
  username: admin
  password: 'secret'

replicas:
  - url: https://adguard2-lxc.localdomain:443
    username: admin
    password: 'secret'
    insecureSkipVerify: true

  - url: https://adguard3-lxc.localdomain:443
    username: admin
    password: 'secret'
    insecureSkipVerify: true

  - url: https://adguard4-lxc.localdomain:443
    username: admin
    password: 'secret'
    insecureSkipVerify: true

api:
  port: 0

features:
  generalSettings: true
  queryLogConfig: true
  statsConfig: true
  clientSettings: true
  services: true
  filters: true
  dhcp:
    serverConfig: true
    staticLeases: true
  dns:
    serverConfig: true
    accessLists: true
    rewrites: true

Local DNS

I mainly have 2 domains for DNS. One is my bought domain from a domain registrar, and the other is for local DNS within the homelab.

I have 2 configured so that internally, services can communitcate with their localdomain addresses and end users can communicate with the front-facing Let's Encrypt domain that points to my Caddy LXC container.