Skip to content

Instantly share code, notes, and snippets.

@giuseppe998e
Last active April 24, 2025 16:18
Show Gist options
  • Save giuseppe998e/14923d6e899089fcd1679ec0a2d9bdb8 to your computer and use it in GitHub Desktop.
Save giuseppe998e/14923d6e899089fcd1679ec0a2d9bdb8 to your computer and use it in GitHub Desktop.
RouteDNS Quad9 Secure DNS (DoH/DoT) Client Configuration
# Quad9 DoT/DoH Client
# DoT Resolvers
[resolvers.main-dot]
protocol = "dot"
address = "dns.quad9.net"
bootstrap-address = "9.9.9.9"
[resolvers.secondary-dot]
protocol = "dot"
address = "dns.quad9.net"
bootstrap-address = "149.112.112.112"
[groups.dot]
type = "fail-back"
resolvers = ["main-dot", "secondary-dot"]
reset-after = 60 # sec
servfail-error = true
# DoH Resolvers
[resolvers.main-doh]
protocol = "doh"
address = "https://dns.quad9.net/dns-query"
bootstrap-address = "9.9.9.9"
[resolvers.secondary-doh]
protocol = "doh"
address = "https://dns.quad9.net/dns-query"
bootstrap-address = "149.112.112.112"
[groups.doh]
type = "fail-back"
resolvers = ["main-doh", "secondary-doh"]
reset-after = 60 # sec
servfail-error = true
# TTL modified, cached and fail-rotate resolver
[groups.failrotate]
type = "fail-rotate"
resolvers = ["dot", "doh"]
[groups.ttlmod]
type = "ttl-modifier"
resolvers = ["failrotate"]
ttl-select = "average"
ttl-max = 86400
[groups.cache]
type = "cache"
resolvers = ["ttlmod"]
cache-answer-shuffle = "round-robin"
cache-flush-query = "cache.flush."
cache-prefetch-trigger = 30
cache-prefetch-eligible = 300 # 5min
backend = { type = "memory", size = 4096, filename = "/var/cache/routedns.quad9.json" }
[listeners.local-udp]
address = "127.0.0.1:53"
protocol = "udp"
resolver = "cache"
@giuseppe998e
Copy link
Author

giuseppe998e commented Apr 24, 2025

Configuration Breakdown

This RouteDNS configuration sets up Quad9 as both DNS-over-TLS (DoT) and DNS-over-HTTPS (DoH) upstream resolvers, applies fail-back and fail-rotate policies, modifies TTLs, adds caching, and listens locally on UDP.

  1. DoT Resolvers

    • [resolvers.main-dot]
      • protocol = "dot": use DNS-over-TLS
      • address = "dns.quad9.net": hostname of the Quad9 DoT service
      • bootstrap-address = "9.9.9.9": IP to resolve the hostname initially
    • [resolvers.secondary-dot]
      • Same as main-dot, but uses 149.112.112.112 as its bootstrap address
  2. DoT Fail-Back Group

    • [groups.dot]
      • type = "fail-back": normally uses main-dot; on failure, falls back to secondary-dot, then returns to primary after reset-after
      • resolvers = ["main-dot", "secondary-dot"]
      • reset-after = 60 sec: retry primary every 60 seconds
      • servfail-error = true: treat SERVFAIL as an error to trigger fail-over
  3. DoH Resolvers

    • [resolvers.main-doh]
      • protocol = "doh": use DNS-over-HTTPS
      • address = "https://dns.quad9.net/dns-query": Quad9 DoH endpoint
      • bootstrap-address = "9.9.9.9"
    • [resolvers.secondary-doh]
      • Same DoH endpoint with bootstrap 149.112.112.112
  4. DoH Fail-Back Group

    • [groups.doh]
      • Configured identically to groups.dot but for DoH resolvers
  5. Mixed Resolver Group (Fail-Rotate)

    • [groups.failrotate]
      • type = "fail-rotate": rotate through dot and doh resolvers on each query or on failure
      • resolvers = ["dot", "doh"]
  6. TTL Modifier

    • [groups.ttlmod]
      • type = "ttl-modifier": adjusts the TTL of answers before caching
      • resolvers = ["failrotate"]: applies to the output of the fail-rotate group
      • ttl-select = "average": choose an average between the record’s TTL and any configured maximum
      • ttl-max = 86400: cap TTL at 24 hours (86 400 seconds)
  7. Cache

    • [groups.cache]
      • type = "cache": enable caching of DNS answers
      • resolvers = ["ttlmod"]: cache the TTL-modified answers
      • cache-answer-shuffle = "round-robin": distribute cached answers in round-robin order
      • cache-flush-query = "cache.flush.": special query prefix to flush the cache
      • cache-prefetch-trigger = 30: prefetch entries when they have ≤ 30 sec left before expiry
      • cache-prefetch-eligible = 300: only prefetch entries if they originally had ≥ 300 sec TTL
      • backend = { type = "memory", size = 4096, filename = "/var/cache/routedns.quad9.json" }: use a 4 KiB in-memory cache and persist to the given file
  8. Listener

    • [listeners.local-udp]
      • address = "127.0.0.1:53": bind to localhost UDP port 53
      • protocol = "udp": accept plain DNS over UDP
      • resolver = "cache": forward queries into the cache group (and thus through TTL modifier, fail-rotate, DoT/DoH, etc.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment