Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


Unbound on Ubuntu can provide a validating recursive DNS resolver for a small, trusted LAN without exposing a public service. This guide uses Ubuntu 24.04 LTS amd64 and its maintained Unbound package, checked as 1.19.2-1ubuntu3.10 on 11 October 2026. Package updates can change that revision; check your configured Ubuntu repositories rather than pinning an obsolete build. No complete end-to-end author test was performed.[1]
Key Takeaways
- Begin on loopback, then bind one actual LAN address with a minimal authorized subnet.
- Keep Ubuntu's trust anchor, service identity, and chroot arrangements intact.
- DNSSEC validates signed data; it does not encrypt DNS or filter advertisements.
- Test explicit queries before switching DNS on one client, and retain its original settings.
Use this procedure when you own the host and LAN and can maintain a recursive resolver. The overview of ongoing self-hosting duties helps assign updates, logs, access policy, and recovery. Compare DNS and VPN roles if you need an exit address or a different traffic path: a resolver answers names, not all application traffic.
Unbound is not automatically an ad-blocking service; DNS filtering has a separate policy and workflow. If the host already fails name resolution, first diagnose Ubuntu's resolver chain. The general device troubleshooting sequence separates application failures from DNS failures.
This is a small recursive resolver, not a forwarding profile, router deployment, or public DNS service. Recursive operation needs authorized outbound UDP and TCP port 53 to authoritative servers. Networks that prohibit that traffic need an approved alternative; do not disable their controls. Reserve a stable LAN address through the network owner's existing address-management process.
Record current packages, Unbound configuration and includes, unit and drop-ins, enablement, firewall rules, addresses, routes, and resolver ownership. Keep private backups outside active include directories. Retain an independent management connection. If an existing Unbound deployment or automated manager owns these paths, stop and agree a change with that owner rather than pasting a second conflicting server block.
Check bindings and system DNS before installing. Ubuntu's systemd-resolved may listen on a loopback stub such as 127.0.0.53; that is different from 127.0.0.1. A wildcard listener can conflict with either. Do not disable the system resolver or rewrite /etc/resolv.conf to free a port.[6]
sudo ss -lntup 'sport = :53'
resolvectl status
ip -4 address
ip -6 address
sudo apt-get update
sudo apt-get install unbound dnsutils
apt-cache policy unbound
unbound -V
sudo unbound-checkconf /etc/unbound/unbound.conf
sudo systemctl status unbound.service --no-pager
Installing the package may start its service: inspect the resulting listener and service state immediately. Stop and investigate an unexpected broad listener. Preserve the baseline if the package was already installed. apt-cache policy shows installed and candidate versions; unbound -V shows the program build, not necessarily the full Ubuntu package revision.
Read /etc/unbound/unbound.conf, its include directives, and /etc/unbound/unbound.conf.d/ before adding a file. Verify the package-provided root trust-anchor arrangement and its accessible state directory; leave it in place. Do not duplicate auto-trust-anchor-file, initialize a second anchor blindly, set username: "", or remove chroot settings to silence a permissions error. Correct permissions and paths within the package's design.[2][6]
The current upstream handbook can explain concepts, but its examples and version may differ from Noble's package. Configuration fields and defaults here follow the Ubuntu manual. The package owns hardening and trust-anchor maintenance unless the existing administrator has deliberately configured otherwise.[2][3]
After reconciling existing interface and ACL directives, use a privileged editor to create the previously absent /etc/unbound/unbound.conf.d/reader-lan.conf, root-owned mode 644. Confirm the main file actually includes that directory. Add only this scoped block; keep the package's other files unchanged:
server:
interface: 127.0.0.1
port: 53
access-control: 127.0.0.0/8 allow
access-control: 0.0.0.0/0 refuse
access-control: ::/0 refuse
hide-identity: yes
hide-version: yes
verbosity: 1
log-queries: no
The broad IPv4 and IPv6 ACL entries refuse clients; they are not listener addresses. The more-specific loopback allowance takes precedence. With only an explicit IPv4 loopback interface, this addition requests no incoming IPv6 listener. Other included configuration can still add interfaces or ACLs, which is why checking the merged configuration and actual sockets matters.[2]
Validate the effective file before restarting. unbound-checkconf should exit zero; it does not prove answers, network access, DNSSEC, or firewall safety.[4]
sudo unbound-checkconf /etc/unbound/unbound.conf
sudo systemctl restart unbound.service
sudo systemctl status unbound.service --no-pager
sudo ss -lntup 'sport = :53'
dig @127.0.0.1 example.com A
Read bounded diagnostics with sudo journalctl -u unbound.service -n 30 --no-pager. The explicit loopback query should receive a DNS response from the intended process. Inspect status and answer separately; NOERROR with no requested record can be valid. On a bind failure, resolve the actual address conflict; on a permissions failure, inspect package-owned paths. Do not add 0.0.0.0 as a shortcut.
Replace the exercise file with this block only after loopback works. Substitute the host's real stable LAN IPv4 address for every use of 192.168.50.2 below, including the firewall and query commands. Replace 192.168.50.0/24 with the smallest actual authorized subnet; do not assume every private address belongs to your LAN.
server:
interface: 127.0.0.1
interface: 192.168.50.2
port: 53
access-control: 127.0.0.0/8 allow
access-control: 192.168.50.0/24 allow
access-control: 0.0.0.0/0 refuse
access-control: ::/0 refuse
hide-identity: yes
hide-version: yes
verbosity: 1
log-queries: no
Only assigned addresses can bind. Avoid 0.0.0.0, ::, broad private-range allowances, and router port forwarding. The most-specific matching ACL governs access. These examples do not add an IPv6 interface or client allowance; if IPv6 access is required, design and validate its actual interface, minimal prefix, and policy separately. Do not confuse incoming listeners with outbound IPv6 queries or AAAA answers.[2]
Re-run the effective configuration check, restart the service, and inspect both UDP and TCP listeners. Interface changes require a restart; do not assume a reload creates a new socket. Keep the current firewall manager and permit only UDP and TCP port 53 from the authorized subnet to this host's LAN address, if policy requires an allowance. Record exact additions, preserve management access, and never flush or disable a shared firewall. Unbound ACL and firewall restrictions serve different layers.
For an already-active UFW-owned policy, the narrow additions are:
sudo ufw allow proto udp from 192.168.50.0/24 to 192.168.50.2 port 53 comment 'unbound-lan'
sudo ufw allow proto tcp from 192.168.50.0/24 to 192.168.50.2 port 53 comment 'unbound-lan'
Replace the sample subnet and address before use. Do not enable UFW solely to run these commands. If another owner manages the firewall, use that owner's equivalent procedure.
On one authorized LAN test client with dig, issue explicit queries before changing system DNS:
dig @192.168.50.2 example.com A
dig @192.168.50.2 example.com A +tcp
dig @192.168.50.2 example.com AAAA
dig @192.168.50.2 com. SOA +dnssec
dig @192.168.50.2 dnssec-failed.org A +dnssec
dig @192.168.50.2 dnssec-failed.org A +dnssec +cdflag
Read the server address, status, answer type, and flags. A signed positive query such as com. SOA should return validated data with ad when validation succeeds. The deliberately broken dnssec-failed.org example should return SERVFAIL while it remains bogus. With +cdflag, a different result can help distinguish validation failure from reachability; use that flag only for this diagnostic, never as the normal client policy. These are expected observations, not live results asserted by this article.[2][5]
| Reader check | Expected observation | If it differs |
|---|---|---|
| Explicit UDP and TCP queries from the allowed client | Responses come from the selected LAN address | Inspect each transport's listener and firewall rule |
| Signed positive answer | Successful validation and ad for authenticated data | Inspect time, trust anchor, logs, and whether the selected name remains signed |
| Known-bogus answer without CD | SERVFAIL when the test remains bogus | Confirm the effective validator and test-domain condition; do not disable validation |
| Repeat a cacheable positive query before TTL expiry | A cached answer may show a decreasing TTL | Prefetch, expiry, and different cache paths can alter observations |
| Controlled source outside the allowed subnet, reaching the listener | REFUSED under this ACL | A firewall drop may instead time out; verify the actual source and policy |
| IPv4 and IPv6 socket inventory | Only intended listeners exist | Check all includes and firewall families before declaring the boundary correct |
| Service restart followed by a new query | Authorized resolution works again | Restore the safe configuration if the baseline is not recovered |
Use a genuinely separate, authorized source for the refusal check. A timeout from one network is not evidence that every unauthorized network is excluded. Do not create a public exposure or broad allow rule just to obtain a refusal response. Mark the check unperformed if no controlled source is available.
For cache observation, repeat the same name, type, and class shortly after a successful query and compare TTL and reported server. Do not treat lower query time as a benchmark: scheduling, network caches, prefetch, and upstream conditions can change it. DNSSEC protects signed-data authenticity, not the confidentiality of DNS queries. Query logs remain disabled in this sample; diagnostics can still contain addresses, so retain them privately.
Only after explicit checks, save one client's existing DNS values and their owner: DHCP, NetworkManager, networkd, VPN, or another supported manager. Change that client's DNS through its supported interface to the resolver's actual LAN address. Leave the router and other clients unchanged. Check the effective resolver state and ordinary application resolution. Applications using their own DoH can bypass system DNS; a working browser alone does not prove the system uses Unbound.
To recover, first restore that client's exact previous DNS settings through the same owner and verify ordinary resolution. Remove only the recorded exercise firewall additions, then restore the saved Unbound configuration. If this exercise created reader-lan.conf and it was previously absent, remove only that file; keep package trust-anchor and other include files. Validate the effective baseline, restart or stop the service according to its former state, and restore prior enablement. Verify listeners, system resolver, DNS answers, and management access.
If a unit or drop-in was changed by the existing deployment procedure, restore it and run sudo systemctl daemon-reload before restarting. Remove the package only if the baseline proves this exercise introduced it and nothing else needs it. Do not remove trust-anchor state from an existing installation or blindly rewrite /etc/resolv.conf. If recovery is uncertain, stop further changes and use the independent management path.
To add filtering in front of this resolver, AdGuard Home with split upstreams can forward selected domains to Unbound.
The intended configuration binds specific private interfaces and permits one authorized subnet. Confirm merged includes, sockets, firewall families, and upstream NAT; the snippet alone is not proof of the entire deployment boundary.
No. Inspect the actual bindings and resolver chain. Different explicit loopback addresses can coexist; a real conflict needs an ownership-aware correction.[6]
No. It validates signed DNS data. This recursive UDP/TCP configuration does not add encrypted client transport or hide queries from the network.
DNS uses both. A UDP answer does not establish that TCP works when required for a larger response or another supported exchange.
No. Record type, inbound transport family, and outbound resolver connectivity are different properties; inspect sockets explicitly.
No. Observe cache behavior under controlled conditions, but one timing difference is not a reliable benchmark or comparison.
No. Check one client and its recovery first. Router-wide distribution is outside this scoped procedure and needs its own maintenance and rollback plan.
Disclaimer: Use only authorized hosts and networks and follow applicable law and policy. These are documentation-based steps and expected reader checks, not an author deployment record, compatibility certification, or performance guarantee.
Sources:
Sources checked 11 October 2026.
Related Articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





