FreeBSD local-unbound Operations: Resolver Setup and DNSSEC Checks
Operate FreeBSD local-unbound as a host-local caching resolver with clear resolver ownership, upstream validation, cache checks, and rollback.
FreeBSD includes local-unbound(8), a local caching resolver intended for the host itself. It can reduce repeated upstream queries and perform DNSSEC validation, but it is not automatically a recursive resolver for every machine on the LAN or an authoritative server for a domain. Knowing that boundary prevents a common configuration mistake: enabling a local service and assuming that remote clients can now use it.
The resolver path on a FreeBSD host may include /etc/hosts, the name-service switch, /etc/resolv.conf, a DHCP client, VPN software, and resolvconf(8). A resolver that is healthy but bypassed by libc, or a correct file overwritten by a lease update, looks like intermittent DNS failure. Start by identifying who owns resolver configuration and follow one query from the application to the selected nameserver.
Understand the host lookup path
The default hosts entry in /etc/nsswitch.conf is files dns: local /etc/hosts mappings are considered before DNS. The resolver library reads /etc/resolv.conf; its nameserver lines identify servers to query, and search/domain settings affect unqualified names. A local Unbound instance only helps applications that actually use the system resolver path. Applications with their own DNS client, hard-coded resolver, proxy, or container namespace may bypass it.
Inspect the current resolver state before enabling anything:
grep '^hosts:' /etc/nsswitch.conf
cat /etc/resolv.conf
sysrc local_unbound_enable
service local_unbound status
If resolv.conf is generated, identify the owner before editing it. FreeBSD’s DHCP client may write DNS values received in a lease, and resolvconf can merge data from several interfaces, a VPN, and other services. A manual edit to generated output may work until the next renewal or network change. Configure the authoritative input or resolver integration instead, then confirm the generated result.
Know what local-unbound does and does not provide
The base-system resolver can cache answers and validate DNSSEC. FreeBSD’s installer and Handbook describe it as a local caching/forwarding resolver for the host. In the common configuration, the existing nameservers in /etc/resolv.conf are used as forwarders. The server listens for local queries; enabling this service does not publish an intended service to the network. If the requirement is a shared resolver for other devices, use the supported Unbound port configuration and design its listener, access policy, and service lifecycle separately.
The service is also distinct from authoritative DNS. An authoritative server answers for zones it hosts; a recursive/caching resolver obtains answers through the DNS hierarchy or configured forwarders. If a host needs both authoritative and recursive service, their listeners and configuration must be planned carefully because they use the same DNS port. Do not add arbitrary listening addresses or local zones to the base configuration without following local-unbound.conf(5) and the relevant Unbound manual.
Enable the base service with the normal rc configuration, then start it:
sysrc local_unbound_enable="YES"
service local_unbound start
service local_unbound status
The service’s setup may update resolver state and construct its configuration from the existing resolver file. Review the resulting /etc/resolv.conf and /var/unbound/unbound.conf; keep a copy of the known-good state before changing an operational resolver. If a DHCP or VPN agent owns nameservers, ensure the bootstrap forwarder list remains current after renewal rather than assuming a one-time start captures every later network change.
Validate forwarders and DNSSEC before switching applications
If the configuration forwards to upstream servers, verify that each selected upstream supports the DNSSEC behavior required by local validation. The FreeBSD Handbook specifically recommends testing upstream DNSSEC trust with drill -S before starting Unbound; an upstream that fails this check can cause validation failures that appear to applications as lookup errors. The provider’s address, routing, firewall, and system clock also affect whether validation succeeds.
# Replace the address with an approved upstream resolver.
drill -S FreeBSD.org @192.0.2.53
# After local-unbound is running and resolv.conf points to localhost:
drill -S FreeBSD.org
drill FreeBSD.org A
drill FreeBSD.org AAAA
192.0.2.53 is documentation-only. The first query tests a specific upstream; the later queries follow the host’s configured resolver path. A trust-tree success is evidence that the tested name validated through that path at that moment. It does not prove every domain, record type, interface, or application will work. Test a known secure name, a deliberately invalid DNSSEC test domain only if the service owner approves, and ordinary internal names that the host is expected to resolve.
DNSSEC validation can turn a broken upstream into a visible failure instead of silently returning an unauthenticated answer. Do not “fix” a validation error by disabling validation until you know whether the upstream strips DNSSEC data, the local clock is wrong, a captive portal intercepted DNS, or the test name is intentionally bogus. Capture the full drill output and exact resolver address; compare an explicit @server query with the local default path.
Observe the listener, cache, and system resolver
Check the daemon, socket, configuration, and application-level lookup separately. The local-unbound checker validates configuration syntax; its manual documents the default configuration path and exit status. Use the executable name and flags provided by the installed release’s local-unbound-checkconf(8) manual.
service local_unbound status
sockstat -l | grep ':53'
local-unbound-checkconf
drill @127.0.0.1 FreeBSD.org A
getent hosts FreeBSD.org
If the checker requires an explicit configuration path, use /var/unbound/unbound.conf only after confirming that is the active file on the host. sockstat shows bound sockets but does not establish DNS correctness. An explicit drill @127.0.0.1 tests the local daemon, while getent exercises the system name-service path. If the direct query succeeds and getent fails, investigate NSS and resolver-file ownership rather than replacing the Unbound cache.
Resolver caches obey TTL and may retain data until expiry; a changed authoritative record is not guaranteed to appear immediately everywhere. Compare the answer’s TTL, authoritative result, cached local response, and the application’s own cache. Avoid flushing or restarting every resolver layer before preserving the evidence. A restart can make a symptom disappear without revealing whether the root cause was stale cache, bad upstream, or generated configuration.
For operational telemetry, record daemon availability, query success through the local socket, validation failures, upstream reachability, resolver-file changes, and DNS response latency. Unbound logs may be controlled by its configuration and service setup; do not assume every failed query appears in /var/log/messages. Use bounded debug logging during an incident, then restore normal verbosity to avoid filling the filesystem with query details.
Diagnose common failure patterns
drill @127.0.0.1 refuses the connection. Confirm the service is enabled and running, the daemon bound the expected loopback address and port, and the active configuration parses. If another DNS process already owns port 53, inspect its listener before stopping or reconfiguring it.
Direct upstream queries work but local validation fails. Test each forwarder with drill -S, compare the returned DNSSEC records, confirm system time, inspect Unbound logs, and verify that the current config actually uses the intended forwarders. Do not edit a generated file while its creator remains active.
The resolver works until DHCP renewal or VPN reconnect. Compare /etc/resolv.conf and its timestamps before and after the network event; inspect DHCP/resolvconf state and the local-unbound forwarder configuration. Assign one clear owner to the generated file and ensure the resolver integration responds to changes.
Applications disagree about names. Compare drill, getent, /etc/hosts, nsswitch.conf, the application’s configured resolver, and any local cache. Search domains can make short-name behavior differ from a fully qualified query. Query A and AAAA records separately and note which address family the application selects.
External names resolve but private names do not. Determine whether the local resolver has the correct split-DNS forwarders or local data and whether a VPN-specific nameserver is being included. A public recursive resolver cannot know private records unless the resolver design directs those zones to the correct internal service. Do not replace the internal search domain with a public forwarder as a blanket workaround.
Change configuration with rollback and acceptance criteria
Before changing the resolver, save /etc/resolv.conf, /etc/nsswitch.conf, the active /var/unbound configuration, and the current DHCP/VPN/resolvconf settings. Record the server list, search domain, host time, service status, and baseline results for both public and internal names. The rollback must restore the actual configuration owner; copying a saved generated file back while DHCP is still writing it is not a stable rollback.
After a change, validate three layers: Unbound parses and runs, direct queries to the local socket return the expected records with appropriate validation, and the real application-facing name-service path resolves both internal and external names. Repeat after a DHCP renewal or VPN transition if those events can alter resolver input. Reboot validation is useful only after confirming how the boot service regenerates configuration and updates resolv.conf.
Do not measure success solely by a faster second lookup. A cache hit may improve latency while stale or wrong data persists. Acceptance means the intended resolver is selected, secure answers validate, internal split-DNS behavior is correct, external lookups work, failures are observable, and network configuration changes preserve the intended forwarder set. Keep local-unbound for the host-local role it is designed to serve; use a separate explicit service design when the requirement extends to a network-wide resolver.
Related:
- FreeBSD Wi-Fi Client Operations: Driver, WPA, and DHCP
- FreeBSD DHCP Client Lease Lifecycle: Boot, Renewal, and Recovery
Sources: