This episode deepens dnsdist: load balancing policies like leastOutstanding, roundrobin, and hash, backend health checks with failover, packet cache and dynamic rules configuration, plus Lua scripting for server selection, per-client rate limits, and query logging.

dnsdist used at a basic level already balances load and protects the front door. Episode 17 takes it to the next level: customizable policies, caches that relieve backend load, and Lua scripting that makes dnsdist act on your business logic — from choosing backends to limiting specific clients.
Here dnsdist changes from a tool into a platform. You'll write rules, not just read configuration.
Beyond leastOutstanding and roundrobin, dnsdist offers hash-based policies that always serve the same client from the same backend — important for cache affinity:
setServerPolicy(leastOutstanding)
setServerPolicy(roundrobin)
setServerPolicy(chash)setServerPolicy(chash) uses consistent hashing: client addresses are mapped to backends, and changing the number of backends only moves a small fraction of keys. This keeps cache effectiveness when backends are added or removed.
Health checks determine whether a backend is fit to receive queries. When a backend fails, dnsdist marks it down and shifts load automatically:
newServer({ address="192.0.2.11:53", checkName="example.com",
checkType="A", checkTimeout=500, maxCheckFailures=3 })The configuration above checks example.com A every interval. After three failures, the backend is marked down and queries are diverted to healthy ones — failover runs without human intervention.
The packet cache stores complete answers so identical queries are answered directly without touching the backend:
pc = newPacketCache(10000)
getPool("") : setCache(pc)
setCacheHitResponseRule(AllRule(), AllowResponseRule())newPacketCache(10000) creates a cache of 10,000 entries. Matching queries are answered from cache — the backend relaxes, latency drops dramatically.
Dynamic rules allow automatic blocking based on behavior:
addAction(MaxQPSIPRule(20, 32, 64), TCAction())The line above temporarily blocks clients exceeding 20 qps. MaxQPSIPRule triggers a TC action for aggressive sources — self-adapting DDoS protection.
Lua gives you full control over every passing query:
function onClientRequest(dq)
infolog("Query dari " .. dq.remoteaddr .. " untuk " .. dq.qname)
return DNSAction.None
endThe onClientRequest function is called for every incoming query. The example above logs the client address and queried name to syslog — the foundation of audit and analysis.
Per-client limits can be finer than built-in rules:
local limits = newNMG()
addAction(AndRule({QTypeRule(DNSQType.SOA), NotRule(makeRule(limits))}), DropAction())With Lua rules like the one above, clients outside the limits list sending excessive SOA queries are dropped. This shows the power of composition: small rules chained into complex policies.
dnsdist can route different queries to different backends:
addAction(QNameRule("internal.example.com"), PoolAction("internal"))
addAction(AllRule(), PoolAction("internet"))PoolAction routes queries to a specific pool. Queries for internal.example.com go to the internal Authoritative pool, everything else to the internet Recursor pool — split-horizon in a single frontend.
Every configuration change must be tested:
sudo systemctl restart dnsdist
dnsdist -c
showServers()
showStats()
quit()showStats() displays metrics like queries per second, cache hits, and the number of blocks. Make sure cache hits rises after the packet cache is active — that's proof your work paid off.
Episode 17 makes dnsdist serve your specific needs: suitable policies, health checks with failover, a packet cache that relieves backends, and Lua scripting for routing, rate limiting, and logging that static configuration could never achieve.
Key takeaways:
chash provides cache affinity; leastOutstanding balances dynamic load.onClientRequest, addAction, PoolAction) gives per-query full control.showStats is the proof that your configuration works as expected.In episode 18, we'll cover high availability and redundancy — redundant authoritative with a hidden primary and public secondaries, geographic distribution, reliable NOTIFY, plus multi-instance recursors behind dnsdist with ECMP and anycast for large scale.