Safeguard
Supply Chain Security•

Run Your Own DNS Resolver at Home: Recursion, DNSSEC and Blocklists

A home recursive resolver asks the root, TLD and authoritative servers itself, validates DNSSEC and applies your blocklist. Here is how to build one.

Safeguard Security Team
50 min read

Running your own DNS resolver at home means a small always-on box answers every DNS question on your network by walking the DNS tree itself, from the root servers down, instead of handing the whole list of names your household looks up to your ISP or a public resolver. Add DNSSEC validation, QNAME minimisation and a blocklist, point DHCP at it, and keep it patched.

This guide goes one level deeper than our explainers on recursive versus authoritative DNS servers, what DNSSEC protects and what it does not and DoH, DoT and DNSSEC. We read RFC 1034 and RFC 1035, the DNSSEC core documents (RFC 4033 and RFC 4035), RFC 5011 on trust anchor updates, RFC 7816 and its replacement RFC 9156 on QNAME minimisation, RFC 8198 on aggressive NSEC caching, RFC 7858, RFC 8484 and RFC 9250 on encrypted transports, RFC 9462 on resolver discovery, RFC 8914 on extended DNS errors, the IANA root trust anchor page and file, the InterNIC root hints file, the Unbound 1.26 documentation from NLnet Labs and the BIND 9 reference manual from ISC. Every option name and default below comes from those sources. Commercial DNS filtering services are described here by category only.

Key takeaways

  • A recursive resolver does the whole lookup for its clients. RFC 1034 calls this recursive service: the client sets the RD (recursion desired) bit and the resolver chases referrals from the root to the top level domain to the authoritative servers, caches each answer for its TTL and returns the result.
  • Forwarding sends every query, in full, to one upstream resolver. Full recursion spreads the queries across many authoritative operators, each of whom sees only part of the picture, especially with QNAME minimisation turned on.
  • Running your own resolver changes who holds your complete query log. It does not hide your traffic from your ISP: queries from your resolver to authoritative servers usually travel unencrypted, and the IP addresses and TLS server names you connect to afterwards are still visible.
  • In Unbound 1.26, qname-minimisation and aggressive-nsec both default to yes, harden-dnssec-stripped defaults to yes, and prefetch and serve-expired default to no. DNSSEC validation needs the validator module and a root trust anchor, which most distribution packages set up for you.
  • The root zone rolled to a new key signing key on 11 October 2026. IANA's page says KSK-2024 (key tag 38696) is now signing the zone and KSK-2017 (key tag 20326) is no longer signing it. A validating resolver with a stale trust anchor stops validating the root. Check yours today.
  • Blocklists work by answering NXDOMAIN, a null address or a policy answer for listed names. They are cheap and effective against third party ad and tracker domains, but they cause false positives, miss first party and CNAME cloaked tracking, and do nothing once a device uses its own encrypted DNS.
  • Encrypted DNS from devices and browsers (DoH on port 443) can bypass your resolver completely. RFC 9462 discovery lets clients upgrade to an encrypted path to your resolver instead, if your software offers it.
  • Plan for failure: a second resolver, serve-expired, monitoring, and a documented way to switch DHCP back to a fallback if the box dies.

What does a home recursive resolver do?

RFC 1034, published in November 1987, describes two kinds of name service. In iterative service, a server answers only from what it knows and hands back a referral to servers closer to the answer. In recursive service, the server takes the client's question and pursues it to the end, issuing whatever queries it needs to other servers. The client asks for recursion with the RD bit in the header, and a server that offers it sets the RA (recursion available) bit, both defined in the RFC 1035 message format.

Your laptop, phone and television run stub resolvers. A stub cannot walk the tree; it sends one query with RD set and waits. Something has to do the walking. On most home networks that is the ISP's resolver, reached through the router, or a large public anycast resolver typed into the router settings. When you run your own recursive resolver, the walking happens on a box you control.

A full lookup, step by step

Suppose a client asks your resolver for the A record of www.example.org and nothing is cached.

StepResolver sends toQuestion (without QNAME minimisation)Answer it gets
1A root server, picked from the root hintswww.example.org AReferral: the NS records for org. plus glue addresses
2An org. serverwww.example.org AReferral: the NS records for example.org.
3An example.org. authoritative serverwww.example.org AThe A record, with its TTL
4Back to the clientThe A record, with RA set, and the AD bit if it validated

Each answer is cached for its TTL. The next query for anything under org. skips step 1 entirely, because the resolver already knows the org. servers. In practice the root and top level domain referrals stay in cache almost all the time, and a warm home resolver goes straight to the authoritative servers for most names. The root hints file published by InterNIC gives a TTL of 3,600,000 seconds (about 41 days) for the root server records, which tells you how rarely that first step really has to happen.

Forwarding versus full recursion

A forwarding resolver does not walk the tree. It keeps a cache and passes every miss, in full, to one upstream resolver. Unbound calls this a forward-zone; BIND calls it forwarders. Your router's built in DNS proxy is usually a forwarder pointed at the ISP.

The two designs differ in three ways that matter at home.

  1. Who sees the full list. A forwarder hands the upstream every name, with your public IP address attached. Full recursion spreads the questions across root operators, TLD registries and thousands of authoritative operators, each of whom sees only the queries for zones they serve.
  2. Who validates DNSSEC. A forwarder can validate locally, but only if the upstream passes the DNSSEC records through. Full recursion validates everything itself from the root trust anchor down.
  3. What breaks. A forwarder depends on one upstream service being up and honest. A recursive resolver depends on your box, your uplink and your trust anchor being current.

There is a middle path: forward over an encrypted transport (DoT) to an upstream you have chosen, while running blocklists locally. We compare all four options in a table further down.

What does your ISP or a public resolver learn from your DNS queries?

When your devices use the ISP's resolver, the ISP receives every name every device looks up, the time of each query, the record type and your public IP address, which it can tie to your account. A public resolver receives the same data, minus the account record, but with the same IP address. Each operator's privacy policy decides what is logged and for how long. RFC 4033 is plain that DNSSEC does nothing here: the extensions "do not provide confidentiality."

What running your own resolver changes

  • No single party holds the full log. The complete history of your lookups now exists only on your box, in whatever logs you choose to keep.
  • Authoritative servers see less. With QNAME minimisation, the root sees only that you wanted something under org., and the org. servers see only that you wanted something under example.org.. Only the example.org. servers see www.example.org.
  • You decide the filtering. No upstream can quietly rewrite NXDOMAIN answers into search pages or apply a filtering policy you did not choose.
  • Validation is yours. You know DNSSEC is being checked, because you turned it on and can test it.

What it does not change

  • Your ISP can still see DNS. Queries from your resolver to authoritative servers are normally plain UDP or TCP on port 53. Few authoritative servers offer an encrypted transport today, so an observer on your uplink can still read them. What you remove is the convenient central log, not the observation point.
  • Your source IP is now on every query. With a public resolver, authoritative servers see the resolver's address. With your own, they see your home IP. For most people this is a wash; for some it is a reason to forward instead.
  • The connections that follow. After DNS, your device connects to an IP address, and in most HTTPS connections the TLS server name is still sent in clear text. DNS privacy is one layer.
  • Caching patterns. A one household cache is small. Popular names will often be fetched fresh, which means authoritative operators see a query from your IP shortly after someone in your house decides to visit their site.

The honest summary: full recursion at home trades a single detailed observer for many partial observers plus your ISP's passive view. Pair it with encrypted DNS inside the house and sensible logging, and it is a real improvement. It is not anonymity. If DNS is one step in moving your household off large platform services, our guide on how to replace Google in your life covers the other services, and our DNS protocol explainer covers the message format and record types behind everything below.

How do QNAME minimisation, DNSSEC validation and aggressive NSEC caching work?

These three features are what make a modern recursive resolver more private and more correct than the 1987 design. All three are on in a default Unbound 1.26 build once the validator has a trust anchor.

QNAME minimisation

The original resolution algorithm sends the full query name to every server on the path, including the root, even though the root only needs to know you want something in org.. RFC 7816 (March 2016, Experimental) proposed sending each server only the labels it needs. RFC 9156 (November 2021) replaced it on the Standards Track and changed one detail that matters: it recommends hiding the real query type behind A or AAAA rather than NS, because A and AAAA queries blend into normal traffic and NS queries did not hide anything extra.

Here is the same lookup as above, minimised.

StepServerWhat it is askedWhat it learns
1Rootorg. A (or a referral query for org.)Someone wants something under org.
2org. serversexample.org. ASomeone wants something under example.org.
3example.org. serverswww.example.org AThe full name and real type

For long names, RFC 9156 adds limits so a resolver is not forced into a query per label. It recommends a maximum of 10 minimisation iterations (MAX_MINIMISE_COUNT), with the first 4 labels (MINIMISE_ONE_LAB) added one at a time and the rest added in larger steps. A name with twenty labels, such as some reverse lookup names for IPv6 addresses, still resolves in a bounded number of queries.

Some broken authoritative servers answer NXDOMAIN or an error for an intermediate name that does exist below them. Unbound's manual describes its default as best effort: it falls back to the full name and original type when the upstream replies with an RCODE other than NOERROR, except when it receives NXDOMAIN from a DNSSEC signed zone. A qname-minimisation-strict option turns the fallback off, and the manual warns that "a lot of domains will not be resolvable" if you enable it. Leave strict mode off at home.

BIND 9 has the same feature under the qname-minimization option, with strict, relaxed, disabled and off settings. The BIND manual says relaxed is the current default, that it falls back to normal queries on NXDOMAIN or other unexpected responses such as SERVFAIL or REFUSED, and that in relaxed mode BIND makes NS queries as it walks down the tree. The manual also warns the default "may be changed to strict in a future release", so pin the value in your configuration.

DNSSEC validation

DNSSEC, specified in RFC 4033, 4034 and 4035 (March 2005), adds signatures to DNS data. Each zone signs its records (RRSIG records) with its keys (DNSKEY records), and the parent zone publishes a hash of the child's key signing key (a DS record), signed with the parent's own keys. A validating resolver starts from a single configured trust anchor for the root zone and follows the chain of DS and DNSKEY records down to the answer.

Each answer ends up in one of four states:

StateMeaningWhat the client sees
SecureThe chain from the trust anchor to the answer checks outThe answer, with the AD (authenticated data) bit set
InsecureA parent proves the child zone is unsigned (no DS record)The answer, without AD
BogusSignatures are missing, expired or wrong where the chain says they must existSERVFAIL; with RFC 8914 support, an extended error such as code 6, "DNSSEC Bogus"
IndeterminateThe resolver could not decide, for example with no trust anchorDepends on configuration

Two Unbound settings decide how strict this is. harden-dnssec-stripped (default yes) means that if a zone is under a trust anchor and the DNSSEC data comes back missing, the zone is treated as bogus rather than silently downgraded to insecure. That defeats an on-path attacker who strips signatures. val-permissive-mode does the opposite: the resolver still checks, but passes bogus data to clients instead of returning SERVFAIL. It is a debugging aid, not a home setting.

What validation buys you at home is protection against forged answers for signed zones, including the cache poisoning attacks our DNS cache poisoning history walks through. What it does not buy you is protection for the many zones that are still unsigned, or any privacy.

The root trust anchor

Everything depends on the root key. IANA publishes the root trust anchors as an XML file at data.iana.org, with an S/MIME signature and the ICANN certificate bundle used to check it. As we fetched it, the file lists three key signing keys by key tag: 19036 (valid from 15 July 2010 until 11 January 2019), 20326 (KSK-2017, valid from 2 February 2017) and 38696 (KSK-2024, valid from 18 July 2024), all using algorithm 8, RSA/SHA-256.

IANA's trust anchor page gives the dated timeline for the current rollover:

DateEvent (per IANA)
26 April 2024KSK-2024 generated
11 January 2025KSK-2024 published in the root zone
10 February 2025KSK-2024 should begin to be trusted by resolvers following RFC 5011
11 October 2026Rollover: KSK-2024 signs the zone, KSK-2017 stops signing
11 January 2027KSK-2017 scheduled to be published with the REVOKE bit set
22 March 2027KSK-2017 scheduled to be removed from the root zone

IANA also says it plans an idealised three year rollover interval, with each new key published in standby for about two years first. In other words, trust anchor maintenance is not a one time job. It is part of running a validating resolver.

RFC 5011 (September 2007) is the mechanism most resolvers use to follow these changes automatically. A resolver that tracks the root's DNSKEY set sees a new key signing key appear, signed by the key it already trusts, and starts trusting the new key only after an add hold down time, which the RFC sets at 30 days or the expiry of the original TTL of the first DNSKEY set that contained the new key, whichever is greater. That rule guarantees the resolver sees at least two validated DNSKEY sets containing the new key before accepting it. Revocation works the same way in reverse: a key published with the REVOKE bit and signed by itself is removed. In Unbound, auto-trust-anchor-file turns on RFC 5011 tracking for a zone and the manual notes that the file "is written to when the anchor is updated", so the Unbound user must be able to write to it, and that the probes run several times a month, so the machine must be online frequently. A resolver that was switched off from before February 2025 until after 11 October 2026 never saw the hold down complete. Check it by hand.

Aggressive NSEC caching

When a signed zone says a name does not exist, it proves it with an NSEC or NSEC3 record that covers a whole range of names. RFC 8198 (July 2017, updating RFC 4035) lets a validating resolver reuse that proof: if a cached, validated NSEC record already covers nosuchname.example.org, the resolver can answer NXDOMAIN without asking anyone.

Unbound's manual describes aggressive-nsec (default yes) as using the NSEC chain to synthesise NXDOMAIN and other denials from previous answers, which "helps to reduce the query rate towards targets that get a very high nonexistent name lookup rate". At home the effect is a little speed and a little less traffic. At scale it blunts random subdomain floods against signed zones. It works only for signed zones, and only with validation on.

A related hardening, harden-below-nxdomain, follows RFC 8020: once a name is known not to exist, names beneath it do not exist either. The manual notes very old software sometimes returns NXDOMAIN for empty non-terminals and may be incompatible.

How do DNS blocklists work, and what are their limits?

A DNS blocklist turns your resolver into a policy point. When a client asks for a listed name, the resolver does not resolve it; it answers locally. That one change removes many advertising, tracking and known malicious domains from every device on the network at once, including devices you cannot install software on, such as televisions and appliances.

Three ways to express a blocklist

Hosts style lists. The oldest form is a text file of 0.0.0.0 tracker.example lines, built for the operating system hosts file. Community projects maintain large lists in this format, grouped by purpose: advertising, telemetry, malware, phishing and adult content. You convert these into resolver configuration.

Resolver specific local zones. Unbound's local-zone statement declares a zone the resolver answers itself. The type decides the answer. The Unbound manual documents always_nxdomain (like static, but ignores local data and answers NXDOMAIN) and always_null, which "always returns 0.0.0.0 or ::0 for every name in the zone". BIND has no direct equivalent and uses RPZ instead.

Response Policy Zones (RPZ). RPZ is a policy format written as an ordinary DNS zone, so it can be transferred and updated like any zone. The Unbound documentation explains the motivation: local-zone and local-data are Unbound specific, and RPZ is a policy format that works across resolver implementations and can be loaded directly from external sources. Unbound added RPZ in version 1.10.0 with QNAME and response IP triggers and supported the full set of triggers and actions from 1.14.0. Triggers include the query name, client IP, response IP, name server name and name server IP. Actions include NXDOMAIN, NODATA, passthru, drop and a local CNAME or data answer. RPZ needs the respip module in module-config. BIND supports RPZ through the response-policy option.

Which answer should a blocked name get?

AnswerHow clients reactNotes
NXDOMAINThe name does not exist; most apps give up quicklyHonest and fast; some apps retry or log errors
NODATAThe name exists, but has no record of this typeUseful for blocking only AAAA or HTTPS records
0.0.0.0 or ::0 (null)Connect attempt fails immediatelyAvoids some retry loops; behaviour differs per OS
A local block page addressBrowser shows your pageBreaks HTTPS with a certificate error; usually not worth it
DropNo reply; client times outSlow; avoid for ordinary blocklists

RFC 8914 (October 2020) defines extended DNS error codes that let a resolver explain itself, including 15 (Blocked, by the resolver operator's own policy), 16 (Censored, because of an external requirement) and 17 (Filtered, at the client's request). Support in client software is uneven, but logging these codes on the resolver side makes troubleshooting easier.

The limits you will hit

  • False positives. Lists block shared infrastructure: content delivery hosts, link shorteners, email click trackers, smart TV update servers. A blocked click tracker breaks the link in a password reset email. Keep a short allowlist and a way to turn filtering off for one device or for ten minutes.
  • First party and CNAME cloaked tracking. When a site serves tracking from its own domain, or points a subdomain of its own domain at a tracking provider with a CNAME, a name based list on the first party name either misses it or blocks the site. RPZ response IP or NSDNAME triggers can catch some CNAME cloaking; nothing catches tracking served from the same host as the content.
  • Hard coded and encrypted DNS. Some devices ignore DHCP and use their own resolver addresses. Others, and many browsers, use DoH on port 443. Your blocklist never sees those queries.
  • IP based connections. Software that connects to an address directly needs no DNS at all.
  • List supply chain. A blocklist is third party data loaded into your resolver. A bad update can block a domain your household depends on, and a list fetched over plain HTTP can be altered on the way. Fetch over HTTPS, keep the previous list, and diff before you reload.
  • It is not malware protection. A malware domain list helps with known command and control or phishing names. It does not stop a new domain registered this morning, and it does not inspect anything.

Commercial filtering exists in several categories: protective DNS services that block known malicious domains, family filtering resolvers that block content categories, and network appliances that combine DNS filtering with other controls. A home resolver with RPZ or local zones gives you the same mechanism with lists you choose.

How does encrypted DNS fit in?

Three IETF standards encrypt the path between a client and a resolver.

ProtocolRFC and dateTransportPortNotes
DNS over TLS (DoT)RFC 7858, May 2016TLS over TCP853Dedicated port, easy to identify and to allow or block
DNS over HTTPS (DoH)RFC 8484, October 2018HTTPS; the RFC recommends HTTP/2 as the minimum version443Uses the application/dns-message media type and a URI template such as https://dnsserver.example.net/dns-query{?dns}
DNS over QUIC (DoQ)RFC 9250, May 2022QUIC853 (UDP)Avoids head of line blocking; less widely deployed

RFC 7858 says servers must listen on port 853 unless they agree otherwise with clients, and that port 853 must never carry cleartext DNS.

Encrypted upstream from your resolver

If you decide to forward rather than recurse, forward over DoT. Unbound's forward-tls-upstream: yes makes queries to a forwarder use TLS, and the manual warns that you must also set tls-cert-bundle (or tls-win-cert on Windows), "otherwise the connections cannot be authenticated". Write each upstream in a forward-addr: line as address@853#hostname. The manual explains that @ sets the port, # names the host whose TLS certificate Unbound checks, and @ comes first when you use both. This protects the hop from your house to the upstream. The upstream itself still sees every query.

Full recursion cannot use this trick for most of the path, because authoritative servers rarely offer encrypted transport. That is the core trade off: forwarding over DoT hides queries from your ISP but gives one upstream the full log; recursion spreads the log across many operators but exposes the queries to your ISP.

Encrypted DNS inside the house

Unbound and BIND can both serve encrypted DNS to clients. Unbound's documentation covers DNS over TLS, DNS over HTTPS and DNS over QUIC as downstream options, and the BIND 9 reference manual documents a tls-port option (default 853) for DNS over TLS and an https-port option (default 443) for DNS over HTTPS listeners. Inside a home network the threat model is a compromised or nosy device on the same Wi-Fi, so encryption on the LAN is optional. Most people skip it at first.

When browsers and devices bypass your resolver

Many browsers and some operating systems can send DNS straight to a DoH server of their choosing, over port 443, where it looks like any other HTTPS traffic. When that happens your resolver, its DNSSEC validation and its blocklist are all out of the loop for that client. Port based firewall rules cannot catch it.

Your options, in order of how well they work:

  1. Configure the clients. Browsers and operating systems have settings, and enterprise policies, to turn DoH off or point it at a resolver you choose. This is the only reliable fix.
  2. Offer encrypted DNS yourself and let clients discover it. RFC 9462 (November 2023), Discovery of Designated Resolvers (DDR), lets a client that knows only your resolver's IP ask for SVCB records at _dns.resolver.arpa and learn that the same resolver speaks DoT, DoH or DoQ, then upgrade to it. The client gets encryption and still uses your resolver. Client support depends on the operating system.
  3. Block known DoH endpoints by name. Some blocklists include public DoH server names so that clients fall back to the system resolver. It is a moving target and may break applications that require DoH.

Comparison: ISP resolver, public resolver, forwarding with blocklists, full recursion

ISP resolverPublic resolver (plain or encrypted)Local forwarder with blocklistFull recursion at home
Who holds your full query logISP, tied to your accountPublic resolver operatorThe upstream you forward to, plus your boxOnly your box
What your ISP can see on the wireEverythingEverything with plain DNS; nothing with DoT or DoHNothing if forwarding over DoTQueries to authoritative servers, usually unencrypted
DNSSEC validationVaries by ISPUsually yesLocal, if upstream passes DNSSEC recordsLocal, from your own trust anchor
BlocklistsISP policy, if anyThe provider's policy, if anyYoursYours
Speed, cold cacheGoodGood, large shared cacheGoodSlower on first lookup of a name (several round trips)
Speed, warm cacheGoodGoodVery fast on the LANVery fast on the LAN
MaintenanceNoneNoneLow: updates and listsModerate: updates, lists, trust anchor, monitoring
Main failure modeISP outage or NXDOMAIN rewritingProvider outage or policy changeBox down, upstream downBox down, stale trust anchor, broken authoritative servers
Bypassed by device DoHYesYesYesYes

There is no single right answer. If you mostly want ad and tracker blocking, a forwarder over DoT with a blocklist is the low effort choice. If you want no third party to hold your full lookup history and you are happy to maintain a trust anchor, run full recursion.

Step by step: build a home recursive resolver with Unbound

This walkthrough uses Unbound on Debian or Ubuntu, on a small single board computer, an old mini PC or a virtual machine on your homelab host. A BIND version follows. The examples assume a home network of 192.168.1.0/24, an IPv6 unique local prefix in fd00::/8, and a resolver address of 192.168.1.53. Replace them with your own.

Step 1: give the box a fixed address

The resolver must not move. Either reserve its address in your router's DHCP server or configure it statically. A resolver whose address changes takes every device's DNS down with it. If you also use IPv6, give the box a stable address in your unique local prefix and note it. Our IPv6 autoconfiguration explainer covers how router advertisements and DHCPv6 hand out addresses and resolvers.

Step 2: install Unbound and the tools

sudo apt update
sudo apt install unbound dnsutils
unbound -V | head -n 1

unbound -V prints the version and the modules the build includes. Distribution packages lag upstream; check that your version still receives security fixes from your distribution.

Debian and Ubuntu packages usually ship a configuration snippet that turns on the root trust anchor with RFC 5011 tracking. Check before you add your own, because declaring the same trust anchor twice is an error.

grep -R "trust-anchor" /etc/unbound/
ls -l /var/lib/unbound/

On Ubuntu, systemd-resolved runs a local DNS stub listener on 127.0.0.53 and 127.0.0.54 on the loopback interface. That does not conflict with Unbound if you bind Unbound to the LAN address, as below.

Step 3: write the configuration

Create /etc/unbound/unbound.conf.d/home.conf:

server:
    # Listen only on the LAN address and loopback
    interface: 192.168.1.53
    interface: 127.0.0.1
    interface: ::1
    port: 53

    # Who may query: anything not listed is refused
    access-control: 127.0.0.0/8 allow
    access-control: ::1/128 allow
    access-control: 192.168.1.0/24 allow
    access-control: fd00::/8 allow

    # Privacy and hardening
    hide-identity: yes
    hide-version: yes
    qname-minimisation: yes
    aggressive-nsec: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-below-nxdomain: yes

    # Keep LAN addresses out of public answers (DNS rebinding)
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10
    private-domain: "home.arpa"

    # Performance and resilience
    edns-buffer-size: 1232
    prefetch: yes
    serve-expired: yes
    serve-expired-ttl: 86400

    # Logging and statistics: keep it light at home
    verbosity: 1
    log-queries: no
    extended-statistics: yes

What each group does, according to the Unbound manual:

  • access-control. The most specific netblock wins and anything that matches nothing is refused, so the order of lines does not matter. Never allow 0.0.0.0/0: an open resolver on the internet is abused for reflection attacks.
  • hide-identity and hide-version. Refuse the id.server, hostname.bind and version queries that reveal what you run.
  • qname-minimisation, aggressive-nsec, harden-dnssec-stripped. Already the defaults in Unbound 1.26; writing them out makes your intent clear and survives a future default change.
  • harden-glue. Trusts glue only if it is within the server's authority. Default yes.
  • private-address. Strips private addresses from answers for public names, which the manual says protects against DNS rebinding, where a browser is turned into a proxy into your network. private-domain lets your own local zone return private addresses.
  • edns-buffer-size: 1232. The default in Unbound 1.26, from the DNS Flag Day 2020 recommendation, which avoids fragmented UDP answers.
  • prefetch. Refreshes popular cache entries in the last 10 percent of their TTL. The manual estimates about 10 percent more traffic in exchange for popular names never expiring. Default no.
  • serve-expired and serve-expired-ttl. Serve a stale answer when fresh resolution fails or is slow. The manual cites RFC 8767's suggested limit of one to three days; 86400 seconds is the default limit. This keeps the house online through a short upstream outage.

You do not need a root-hints: line. The manual says the default is to use built in hints for the IN class, and a file is only needed if the built in list has gone out of date.

Step 4: check, start and test

sudo unbound-checkconf
sudo systemctl restart unbound
sudo systemctl status unbound --no-pager

unbound-checkconf catches syntax errors before they take your network down. Then test from another machine on the LAN:

# A normal lookup through your resolver
dig @192.168.1.53 www.example.org A

# DNSSEC: look for the "ad" flag in the header of a signed zone's answer
dig @192.168.1.53 example.org SOA +dnssec

# The same query with checking disabled shows the raw data
dig @192.168.1.53 example.org SOA +cd

# The root key set: look for key tag 38696 among the DNSKEY records
dig @192.168.1.53 . DNSKEY +multiline | grep -i "key id"

A deliberately mis-signed test zone is the best check that validation is actually enforced: a validating resolver must answer SERVFAIL for it, while +cd returns data. Several DNS research groups operate such zones for exactly this purpose.

Check the trust anchor file too. With RFC 5011 tracking, Unbound writes the anchor state into the file:

sudo grep -E "^\.|state" /var/lib/unbound/root.key

You should see the root DNSKEY with key tag 38696 in a valid state. If the file holds only the 2017 key, or the resolver returns SERVFAIL for every signed name, see the trust anchor section under maintenance below.

Step 5: add a blocklist

There are two practical routes.

Route A: an RPZ zone. If your list publisher offers RPZ format, Unbound can fetch and refresh it itself. Add to home.conf:

server:
    module-config: "respip validator iterator"

rpz:
    name: rpz.blocklist.home.arpa
    zonefile: /var/lib/unbound/rpz.blocklist.home.arpa
    url: https://lists.example.net/blocklist.rpz
    rpz-log: yes
    rpz-log-name: blocklist

The module-config line comes straight from the Unbound RPZ documentation, which notes RPZ depends on the respip module. The documentation also shows loading a policy zone by zone transfer with a master: line instead of url:. Keep your own small RPZ zone, loaded first, for local exceptions with the rpz-passthru. action.

Route B: convert a hosts style list to local zones. Many lists are only published in hosts format. Convert them, and validate each name so a malformed line cannot inject configuration:

#!/bin/sh
# /usr/local/sbin/update-blocklist.sh
set -eu
SRC="https://lists.example.net/hosts.txt"
TMP="$(mktemp)"
OUT="/etc/unbound/blocklist.conf"

curl -fsS --proto '=https' "$SRC" -o "$TMP.raw"

# Keep only "0.0.0.0 name" or "127.0.0.1 name" lines with plain hostnames
awk '($1=="0.0.0.0" || $1=="127.0.0.1") && $2 ~ /^[A-Za-z0-9._-]+$/ && $2 != "localhost" { print tolower($2) }' "$TMP.raw" \
  | sort -u \
  | grep -vxF -f /etc/unbound/allowlist.txt \
  | awk '{ printf "local-zone: \"%s.\" always_null\n", $1 }' > "$TMP"

# Never install an empty or tiny list by accident
[ "$(wc -l < "$TMP")" -gt 1000 ] || { echo "list too small, keeping old one"; exit 1; }

cp "$OUT" "$OUT.prev" 2>/dev/null || true
printf "server:\n" > "$OUT"
cat "$TMP" >> "$OUT"
unbound-checkconf && unbound-control reload
rm -f "$TMP" "$TMP.raw"

Create an empty /etc/unbound/allowlist.txt first, add include: /etc/unbound/blocklist.conf to the end of home.conf, and run the script from a weekly systemd timer or cron job. The script keeps the previous list, refuses to install a suspiciously small one, and checks the whole configuration before reloading. unbound-control needs its control interface enabled; on Debian the package normally sets this up, and unbound-control-setup creates the keys if not.

Test a listed name and an unlisted one:

dig @192.168.1.53 a-listed-name.example +short   # expect 0.0.0.0
dig @192.168.1.53 www.example.org +short          # expect a real address

Step 6: point DHCP and router advertisements at it

Clients learn their resolver from DHCP (option 6 for IPv4) and, on IPv6, from router advertisements (the RDNSS option) or DHCPv6. Change all of them, or IPv6 capable devices will keep using whatever your router or ISP advertises.

  • Router web interface. Set the LAN DHCP DNS server to 192.168.1.53. Some ISP routers hide this setting or do not offer it; in that case turn the router's DHCP off and run DHCP on the resolver box.
  • dnsmasq on a Linux router. DHCP option 6 carries the DNS server, so dhcp-option=6,192.168.1.53 hands out your resolver. For IPv6, the dnsmasq manual says its router advertisements include the machine's own link local address as the recursive DNS server by default, and use the DHCPv6 dns-server option instead if you set one, so set that option to the resolver's stable address.
  • Your router's IPv6 settings. Look for an option to advertise a custom DNS server, or to stop advertising the ISP's. Check with ipconfig /all, resolvectl status or scutil --dns on a client that the resolver it lists is yours.

Do not hand out a public resolver as the second DNS server "for backup". Clients do not treat the second entry as a fallback in any predictable order; many will use both, and the blocklist and privacy benefits disappear for some share of queries. Use a second home resolver instead, or accept the single point of failure and monitor it.

Step 7 (optional): stop devices from going around it

On a Linux based router you can force plain DNS through your resolver. This nftables example drops outbound DNS and DoT from the LAN except from the resolver itself:

table inet dnsguard {
  chain forward {
    type filter hook forward priority 0; policy accept;
    ip saddr 192.168.1.53 meta l4proto { tcp, udp } th dport { 53, 853 } accept
    meta l4proto { tcp, udp } th dport { 53, 853 } drop
  }
}

Test it before you rely on it, and keep a console session open in case you lock yourself out. It does not stop DoH on port 443; for that, configure clients as described above.

The same resolver with BIND 9

If you prefer BIND, the equivalent options block in /etc/bind/named.conf.options looks like this:

options {
    directory "/var/cache/bind";
    listen-on { 127.0.0.1; 192.168.1.53; };
    listen-on-v6 { ::1; };
    recursion yes;
    allow-recursion { 127.0.0.1; ::1; 192.168.1.0/24; fd00::/8; };
    allow-query-cache { 127.0.0.1; ::1; 192.168.1.0/24; fd00::/8; };
    dnssec-validation auto;
    qname-minimization relaxed;
    version none;
    hostname none;
    response-policy { zone "rpz.home.arpa"; };
};

zone "rpz.home.arpa" {
    type primary;
    file "/etc/bind/db.rpz.home.arpa";
    allow-query { none; };
};

dnssec-validation auto tells BIND to validate using its built in root trust anchor and to maintain it with RFC 5011. response-policy attaches an RPZ zone, which you write as a normal zone file with entries such as tracker.example CNAME . for NXDOMAIN. Check with named-checkconf and named-checkzone before systemctl reload named.

How do you keep it running and patched?

A home resolver is infrastructure. When it breaks, the house says "the internet is down". The work is small but regular.

Software updates

Resolvers parse untrusted data from the whole internet, and both Unbound and BIND publish security advisories several times a year. Turn on your distribution's automatic security updates (on Debian and Ubuntu, unattended-upgrades), and subscribe to the project's announcement list or your distribution's security tracker. After an update, unbound-checkconf and a quick dig test confirm the service came back.

Root hints

Root server addresses change rarely, but they do change. The InterNIC root hints file we fetched was last updated on 21 October 2026 and matches root zone version 2026102101; it lists 13 root server names, A through M, each with an IPv4 and an IPv6 address. Unbound and BIND use the hints only to find the first root server; they then fetch the current root NS set from the root itself (priming), so an old hints file matters only if every listed address is wrong. Keeping the package updated is enough. If you do use a file, refresh it from InterNIC a few times a year.

Trust anchor rollovers

This is the maintenance task with the sharpest edge in 2026. Per IANA, KSK-2024 began signing the root zone on 11 October 2026 and KSK-2017 stopped. A resolver that never learned KSK-2024 now fails to validate the root, which means every signed answer is bogus and the resolver returns SERVFAIL for most of the internet.

Check:

# Unbound: the managed anchor file should list key tag 38696
sudo grep -c "38696" /var/lib/unbound/root.key || echo "KSK-2024 not found"

# Ask the resolver for the root DNSKEY set with validation
dig @192.168.1.53 . DNSKEY +dnssec | grep -E "flags:|status:"

Fix, for Unbound: unbound-anchor fetches and verifies the root anchor. Its manual says it first tries to update over DNS, which "uses much less bandwidth, is much faster (200 msec not 2 sec)", and falls back to downloading the IANA XML file and checking its signature with the ICANN certificate. It exits with 1 if it updated the anchor from the certificate or used its built in anchor, and with 0 if no update was needed or RFC 5011 tracking could update it.

sudo systemctl stop unbound
sudo -u unbound unbound-anchor -a /var/lib/unbound/root.key -v
sudo systemctl start unbound

For BIND with dnssec-validation auto, the managed keys database follows RFC 5011, and an up to date package ships the current root key as its built in anchor. If an old installation is stuck, update the package and remove the managed keys database files in the working directory so BIND starts again from its built in anchor. Read your distribution's notes before you delete anything.

Mark the next dates in IANA's schedule, 11 January 2027 (revocation of KSK-2017) and 22 March 2027 (removal), and check your resolver's logs around them.

Monitoring

sudo unbound-control stats_noreset | grep -E "total.num.queries|total.num.cachehits|total.num.cachemiss|num.answer.rcode.SERVFAIL"

The num.answer.rcode counters are extended statistics, which is why the configuration above sets extended-statistics: yes; the same section also counts num.answer.secure and num.answer.bogus answers. Watch for three things: the service is running, the SERVFAIL count is not climbing (a jump usually means a trust anchor, clock or network problem; DNSSEC needs a correct clock), and a canary lookup succeeds. A two line script run every five minutes that queries a stable name and alerts you on failure is enough. If you already run a homelab dashboard, Unbound's statistics are easy to feed into it.

A fallback resolver

Plan for the box dying. The options, from cheapest:

  1. Write down how to switch back. A note on the router saying which setting to change, and what to change it to. Everyone in the house should be able to do it.
  2. Run a second resolver. A second small box, or a virtual machine on a different host, with the same configuration. Hand out both addresses. Because both apply the same blocklist and validation, it does not matter which one clients pick.
  3. Keep the box on reliable power. A resolver on a UPS keeps answering during short power cuts, and clean shutdown protects the trust anchor file. Our homelab UPS guide covers sizing and graceful shutdown with NUT.

Back up /etc/unbound/, your allowlist and your update script alongside the rest of your homelab configuration. If you keep homelab backups on a NAS, our home NAS guide explains snapshots and replication.

A dated timeline of the standards behind a home resolver

DateMilestoneSource
November 1987RFC 1034 and RFC 1035 define DNS concepts, recursive service and the message formatRFC 1034, RFC 1035
March 2005RFC 4033 to 4035 define DNSSEC as deployed todayRFC 4033, RFC 4035
September 2007RFC 5011 automates trust anchor updatesRFC 5011
15 July 2010First root trust anchor (key tag 19036) valid from this dateIANA root-anchors.xml
March 2016RFC 7816 QNAME minimisation (Experimental)RFC 7816
May 2016RFC 7858 DNS over TLSRFC 7858
July 2017RFC 8198 aggressive use of DNSSEC validated cacheRFC 8198
October 2018RFC 8484 DNS over HTTPSRFC 8484
11 October 2018KSK-2017 starts signing the root zoneIANA trust anchor page
October 2020RFC 8914 extended DNS errorsRFC 8914
November 2021RFC 9156 QNAME minimisation on the Standards Track, obsoleting RFC 7816RFC 9156
May 2022RFC 9250 DNS over QUICRFC 9250
November 2023RFC 9462 Discovery of Designated ResolversRFC 9462
11 October 2026KSK-2024 starts signing the root zoneIANA trust anchor page
11 January 2027KSK-2017 scheduled for revocationIANA trust anchor page
22 March 2027KSK-2017 scheduled for removalIANA trust anchor page

Common mistakes

  • Running an open resolver. Binding to every interface and allowing 0.0.0.0/0, then forwarding port 53 from the internet "to test". Open resolvers are found within hours and used to amplify attacks. Keep access-control to your own networks and never forward port 53 inward.
  • Leaving IPv6 pointed at the ISP. DHCP hands out your resolver, but router advertisements still advertise the ISP's, and half the house quietly bypasses your blocklist.
  • A public resolver as the "secondary". Clients do not use the second DNS server only as a fallback. Use a second resolver of your own.
  • A stale trust anchor. A resolver that was offline or built from an old image misses the rollover and returns SERVFAIL for signed names. After 11 October 2026, check for key tag 38696.
  • A wrong clock. DNSSEC signatures have validity periods. A single board computer without a real time clock that boots with the wrong date fails validation until NTP fixes it, and NTP may need DNS. Configure an NTP server by IP address or allow your time sync to start before validation matters.
  • Turning on strict QNAME minimisation. The Unbound manual warns that many domains will not resolve. Use the default best effort mode.
  • Turning on val-permissive-mode to "fix" SERVFAILs. It hides real problems and passes forged data to clients. Find the cause instead.
  • Huge, unreviewed blocklists. Stacking several million line lists slows reloads, burns memory on a small box and multiplies false positives. Start with one well maintained list per purpose and add an allowlist.
  • Logging every query forever. log-queries: yes with no rotation recreates the detailed log you set out to remove, on a box that is less protected than a provider's.
  • Forgetting the box exists. No updates, no monitoring, no fallback note. The resolver works for two years, then fails on a Sunday evening.

FAQ

Is running my own DNS resolver faster than using a public resolver?

For names already in cache, yes: the answer comes from a box on your LAN in well under a millisecond of network time. For a name nobody in the house has looked up recently, full recursion takes several round trips to root, TLD and authoritative servers and is usually slower than a large public resolver's shared cache. prefetch and serve-expired narrow the gap for popular names.

Does a home resolver hide my browsing from my ISP?

No. With full recursion, your resolver's queries to authoritative servers usually travel unencrypted, so your ISP can still observe them, and the IP addresses and TLS server names of the sites you visit stay visible. What changes is that no single resolver operator keeps your complete lookup history.

Should I use Unbound or BIND for a home resolver?

Both are mature open source resolvers with full DNSSEC validation, QNAME minimisation and RPZ. Unbound is a resolver only, with a smaller configuration and modest memory use, which suits a single board computer. BIND is both an authoritative server and a resolver, which suits you if you also want to host your own zones. Pick the one whose documentation you find easier to follow.

What happened with the DNSSEC root key in October 2026?

According to IANA, the root zone rolled from KSK-2017 (key tag 20326) to KSK-2024 (key tag 38696) on 11 October 2026. KSK-2024 had been published since 11 January 2025, so resolvers following RFC 5011 should have learned it automatically. KSK-2017 is scheduled for revocation on 11 January 2027 and removal on 22 March 2027.

How do I know DNSSEC validation is working on my resolver?

Query a signed zone with dig +dnssec and look for the ad flag in the header. Then query a deliberately mis-signed test zone: a validating resolver returns SERVFAIL, and the same query with +cd (checking disabled) returns data. Also confirm your trust anchor file contains key tag 38696.

Will a DNS blocklist stop malware?

Partly. Lists of known malicious domains block connections to names already identified as phishing or command and control. They do not stop new domains, direct IP connections or malware that uses its own encrypted DNS. Treat DNS filtering as one layer, not a defence on its own.

Why do some websites break when I use a blocklist?

Lists sometimes include domains a site needs, such as content delivery hosts, consent managers, payment widgets or email click trackers. Check your resolver's log for blocked names around the time of the failure, add the needed name to your allowlist and reload.

Can my browser bypass my home DNS resolver?

Yes. If DNS over HTTPS is enabled in the browser with its own provider, the browser sends queries over port 443 to that provider and never asks your resolver. Change the browser's secure DNS setting to use your system resolver or point it at your own resolver's DoH endpoint.

Do I need root hints in my Unbound configuration?

No. Unbound's manual says the default uses built in hints for the IN class. After start up, Unbound primes itself from the root servers anyway. Keep the package updated and only add a root-hints: file if you have a specific reason.

What is the smallest hardware that can run a home DNS resolver?

Any always-on Linux machine with a stable address. A single board computer or a small virtual machine on a homelab host, such as the one in our guide to building a tiny data center at home, is plenty for a household; the cache and a blocklist of a few hundred thousand names fit in modest memory. Reliable storage and power matter more than speed.

Where Safeguard fits

Safeguard does not run DNS, filter domains, validate DNSSEC, manage routers or protect endpoints. The fit here is indirect. A home resolver is a stack of open source software: the resolver itself, its cryptographic libraries, the blocklist update scripts you write, and often a container or a small web dashboard around it. Like any open source software, those components get security advisories. Safeguard can scan a repository or directory with safeguard scan --dir ., a container image with --image, or an existing SBOM with --sbom, write results as JSON under ./output, and show which of your projects carry a known vulnerable component and in which version. That helps when the resolver or its dashboard runs as a container you build, or when your homelab configuration lives in a repository you maintain.

A home resolver sits in front of every device in the house, and the containers, scripts and libraries around it are rebuilt and pulled again on every update, so the open source components inside them deserve the same tracking as any other dependency. The fit is indirect: what follows applies to the images and repositories you build and run, not to the DNS protocol, your router's firmware or a distribution package you install as is.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

The demos below show Safeguard working on ordinary software projects, not on DNS traffic or resolver configuration. They apply to the repositories, scripts and container definitions that make up a self hosted resolver and its dashboard.

The first demo is a real Safeguard CLI session. After install, safeguard opens the interactive terminal, you ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with your approval, prints a summary and pushes nothing. It ends with /permissions, /model and /scan. The same ask, approve and test loop fits a repository holding a small web dashboard or helper service you run next to your resolver.

Click through it at your own pace, or open it full screen.

A home resolver lives on a box that never leaves the house network. The local runner demo shows how to set up the runner on your own machine, approve it with its device code, and then scan from the terminal. A runner on your homelab host can scan the images and repositories that live there without sending them anywhere else to be built.

Click through it at your own pace, or open it full screen.

If you are away from your desk, the phone app demo shows a real Android session: you send work to a computer running the CLI and approve each command on the phone with Allow once, Allow for turn or Deny. Nothing runs on your homelab machine until you approve it.

Click through it at your own pace, or open it full screen.

To try it on your own repositories, download Safeguard and follow the CLI documentation.

Quick check

With QNAME minimisation on, what does the root server learn when your resolver looks up www.example.org for the first time?

Only that the resolver wants something under org.. RFC 9156 has each server see just the labels it needs, and recommends hiding the real query type behind A or AAAA. Only the example.org. servers see the full name.

Your validating resolver suddenly returns SERVFAIL for most signed domains after being offline for two years. What is the most likely cause?

A stale root trust anchor. IANA's schedule moved root signing from KSK-2017 to KSK-2024 on 11 October 2026. A resolver that missed the RFC 5011 hold down period never learned key tag 38696; refresh the anchor with unbound-anchor or an updated package.

Sources

Last reviewed: 23 October 2026. Resolver defaults change between releases and the root key rollover is still in progress; check the IANA trust anchor page and the documentation for the version your distribution ships before you change a production home network.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.