NSD on FreeBSD: Authoritative-Only DNS Review
NSD (Name Server Daemon) is an authoritative-only DNS server developed by NLnet Labs, the same organization behind the Unbound recursive resolver. NSD does one thing: it serves DNS zones authoritatively. It does not recurse, it does not cache recursive queries, and it does not resolve names on behalf of clients. This single-purpose design makes NSD smaller, faster, and more secure than general-purpose DNS servers like BIND for the specific task of authoritative DNS. On FreeBSD, NSD integrates with the rc.d framework, runs well in jails, and pairs naturally with Unbound for a split-authority/resolver architecture. This review covers NSD's design philosophy, installation on FreeBSD, zone configuration, DNSSEC integration, zone transfer mechanisms, performance characteristics, and how it compares with BIND for authoritative DNS workloads.
Why Authoritative-Only Matters
A DNS server can play two roles: authoritative (answering queries for zones it owns) and recursive (resolving queries by chasing referrals through the DNS hierarchy). BIND does both. NSD does only the authoritative part.
This separation matters for three reasons:
- Security -- recursive resolvers are exposed to the entire DNS ecosystem and have been the target of cache poisoning, amplification attacks, and other exploits. An authoritative-only server has a smaller attack surface because it never chases external referrals or caches third-party data. Compromising NSD does not give an attacker a recursive resolver to abuse.
- Performance -- NSD is optimized for a single workload: reading zone data from disk into memory and answering queries from that data. There is no cache management, no recursion state machine, no DNSSEC validation of external responses. The code path from query reception to response is shorter.
- Operational clarity -- separating authoritative and recursive DNS into different daemons running on different IPs (or different jails) makes it obvious which component handles which traffic. Troubleshooting is simpler when each service has a single responsibility.
Installation on FreeBSD
Binary Package
shpkg install nsd
This installs the NSD daemon at /usr/local/sbin/nsd, the control tool at /usr/local/sbin/nsd-control, and the configuration directory at /usr/local/etc/nsd/.
Enable NSD:
shsysrc nsd_enable="YES"
Ports Installation
shcd /usr/ports/dns/nsd make install clean
Initial Setup
Generate the control keys for nsd-control:
shnsd-control-setup
This creates TLS certificates in /usr/local/etc/nsd/ that authenticate communication between the nsd-control command-line tool and the running daemon.
Create required directories:
shmkdir -p /usr/local/etc/nsd/zones mkdir -p /var/db/nsd chown nsd:nsd /var/db/nsd
Configuration
NSD uses a single configuration file at /usr/local/etc/nsd/nsd.conf. The syntax is clean and minimal.
Base Configuration
shserver: hide-version: yes identity: "" ip-address: 0.0.0.0 ip-address: ::0 port: 53 server-count: 2 username: nsd chroot: "" zonesdir: "/usr/local/etc/nsd/zones" database: "" logfile: "/var/log/nsd.log" pidfile: "/var/run/nsd/nsd.pid" remote-control: control-enable: yes control-interface: 127.0.0.1 control-port: 8952 server-key-file: "/usr/local/etc/nsd/nsd_server.key" server-cert-file: "/usr/local/etc/nsd/nsd_server.pem" control-key-file: "/usr/local/etc/nsd/nsd_control.key" control-cert-file: "/usr/local/etc/nsd/nsd_control.pem"
Key settings:
server-count-- number of NSD server processes. Set this to the number of CPU cores, or slightly less on shared systems. Each server process handles queries independently.hide-version-- prevents the NSD version from being disclosed inversion.bindqueries. A minor security measure.database: ""-- disables the legacy NSD database format. Modern NSD reads zone files directly and compiles them to an internal format on startup.
Zone Configuration
Add zones to nsd.conf:
shzone: name: "example.com" zonefile: "example.com.zone" zone: name: "2.0.192.in-addr.arpa" zonefile: "192.0.2.rev"
Zone File
Create /usr/local/etc/nsd/zones/example.com.zone:
sh$ORIGIN example.com. $TTL 3600 @ IN SOA ns1.example.com. admin.example.com. ( 2026040901 ; serial 3600 ; refresh 900 ; retry 604800 ; expire 300 ; minimum TTL (negative caching) ) @ IN NS ns1.example.com. @ IN NS ns2.example.com. @ IN A 203.0.113.10 @ IN AAAA 2001:db8::10 ns1 IN A 203.0.113.1 ns2 IN A 203.0.113.2 www IN A 203.0.113.10 www IN AAAA 2001:db8::10 mail IN A 203.0.113.20 @ IN MX 10 mail.example.com. @ IN TXT "v=spf1 mx -all"
Validate and Start
Check the configuration:
shnsd-checkconf /usr/local/etc/nsd/nsd.conf
Check a zone file:
shnsd-checkzone example.com /usr/local/etc/nsd/zones/example.com.zone
Start NSD:
shservice nsd start
Verify it is answering queries:
shdig @127.0.0.1 example.com A dig @127.0.0.1 example.com SOA
DNSSEC
NSD serves DNSSEC-signed zones but does not sign zones itself. The signing is done offline using tools like ldns-signzone (from the ldns package) or dnssec-keygen/dnssec-signzone (from BIND's tools). NSD loads the pre-signed zone file and serves the DNSSEC records (RRSIG, DNSKEY, DS, NSEC/NSEC3) to resolvers.
Signing a Zone with ldns
Install the ldns tools:
shpkg install ldns
Generate keys:
shcd /usr/local/etc/nsd/zones/ # Key Signing Key (KSK) ldns-keygen -a ECDSAP256SHA256 -k example.com # Zone Signing Key (ZSK) ldns-keygen -a ECDSAP256SHA256 example.com
This produces .key and .private files for each key. The KSK is used to sign the DNSKEY RRset, and the ZSK signs everything else.
Sign the zone:
shldns-signzone -n example.com.zone Kexample.com.+013+*.private
This produces example.com.zone.signed. Configure NSD to use the signed zone:
shzone: name: "example.com" zonefile: "example.com.zone.signed"
Reload NSD:
shnsd-control reload
Key Rollover
DNSSEC keys must be rotated periodically. ZSKs are typically rolled every 1-3 months, KSKs annually. The rollover process:
- Generate a new key.
- Add the new key to the zone file.
- Sign with both old and new keys (pre-publication method).
- Wait for the old signatures to expire from caches (2x TTL).
- Remove the old key and re-sign.
Automate this with a cron job or use a DNSSEC management tool. NSD itself does not manage key rollovers -- this is an operational task external to the name server.
Zone Transfers (AXFR/IXFR)
NSD supports both AXFR (full zone transfer) and IXFR (incremental zone transfer) for primary-secondary replication.
NSD as Primary
Configure NSD to allow zone transfers to secondary servers:
shzone: name: "example.com" zonefile: "example.com.zone" notify: 203.0.113.2 NOKEY provide-xfr: 203.0.113.2 NOKEY
For TSIG-authenticated transfers:
shkey: name: "transfer-key" algorithm: hmac-sha256 secret: "base64-encoded-secret-here" zone: name: "example.com" zonefile: "example.com.zone" notify: 203.0.113.2 transfer-key provide-xfr: 203.0.113.2 transfer-key
Generate a TSIG key:
shdd if=/dev/urandom bs=32 count=1 2>/dev/null | openssl base64
NSD as Secondary
Configure NSD to receive zone transfers from a primary:
shzone: name: "example.com" zonefile: "example.com.zone" allow-notify: 203.0.113.1 NOKEY request-xfr: AXFR 203.0.113.1 NOKEY
NSD will request a full zone transfer (AXFR) from the primary and store the zone data locally. When the primary sends a NOTIFY message (after a zone update), NSD checks the SOA serial and transfers the zone if the serial has increased.
For IXFR (incremental transfers that only send changes):
shzone: name: "example.com" zonefile: "example.com.zone" allow-notify: 203.0.113.1 NOKEY request-xfr: AXFR 203.0.113.1 NOKEY allow-axfr-fallback: yes
NSD will attempt IXFR first and fall back to AXFR if the primary does not support incremental transfers.
Performance
NSD is one of the fastest authoritative DNS servers available. Its performance characteristics on FreeBSD:
- Query throughput -- NSD handles 100,000+ queries per second per core on modern hardware. A 4-core FreeBSD server with NSD can handle 400,000+ qps for zones loaded in memory.
- Startup time -- zone loading is fast. A zone with 100,000 records loads in seconds.
- Memory usage -- memory consumption is proportional to zone size. A zone with 100,000 records uses roughly 50-100 MB. NSD loads all zone data into memory at startup for maximum query speed.
- Latency -- sub-millisecond response times for cached zone data, which is all data since everything is in memory.
Monitor NSD statistics:
shnsd-control stats nsd-control stats_noreset
NSD vs BIND for Authoritative DNS
Choose NSD When
- You only need authoritative DNS (no recursion).
- Security through simplicity matters -- NSD's smaller codebase means fewer potential vulnerabilities.
- You want maximum query throughput for zone serving.
- You are pairing authoritative DNS with Unbound for recursion (the NLnet Labs recommended architecture).
- You prefer operational simplicity -- NSD's configuration is straightforward with minimal options.
Choose BIND When
- You need both authoritative and recursive DNS in a single daemon.
- You need dynamic DNS updates (RFC 2136) -- BIND supports them natively, NSD does not.
- You need inline DNSSEC signing -- BIND can sign zones automatically, NSD requires external signing.
- You need catalog zones for automatic zone provisioning.
- Your team has existing BIND expertise and tooling.
Performance Comparison
For authoritative-only workloads, NSD typically outperforms BIND by 20-40% in queries per second. This margin comes from NSD's simpler code path -- no recursion logic, no cache management, no DNSSEC validation. For most deployments, both are fast enough that the difference is academic. The choice between them is better made on operational grounds than raw performance.
Running NSD in a FreeBSD Jail
NSD is an excellent candidate for jail isolation:
sh# Create a ZFS dataset for the jail zfs create tank/jails/dns-auth # Install NSD in the jail pkg -j dns-auth install nsd # Copy configuration cp /usr/local/etc/nsd/nsd.conf /tank/jails/dns-auth/usr/local/etc/nsd/
NSD's minimal dependencies (no external databases, no complex library requirements) make it straightforward to run in a minimal jail.
Verdict
NSD is the best authoritative DNS server for FreeBSD deployments that follow the principle of separating authoritative and recursive DNS. Its single-purpose design results in a smaller attack surface, simpler configuration, and higher query throughput compared to BIND for the same workload. Paired with Unbound for recursion, NSD and Unbound form a clean, well-separated DNS architecture where each component does exactly one thing.
The trade-off is that NSD does not sign zones (use external tools), does not support dynamic DNS updates (use a separate update mechanism), and does not do recursion. If you need any of those features in the authoritative server itself, BIND is the pragmatic choice. For pure authoritative DNS serving, NSD is the stronger option on FreeBSD.
Frequently Asked Questions
Can NSD and Unbound run on the same FreeBSD server?
Yes. Run NSD on port 53 bound to the public IP for authoritative queries, and Unbound on port 53 bound to localhost (or a private IP) for recursive queries. Alternatively, use separate FreeBSD jails with distinct IP addresses.
Does NSD support DNS over TLS or DNS over HTTPS?
No. NSD serves traditional DNS over UDP and TCP on port 53. For DNS over TLS (DoT) or DNS over HTTPS (DoH), place a TLS-terminating proxy in front of NSD, or use Unbound (which supports DoT and DoH) as a forwarding resolver.
How do I add a new zone to NSD without restarting?
Add the zone configuration to nsd.conf and the zone file to the zones directory, then run nsd-control reconfig. This loads new zone configurations without restarting. For zone data changes (updated zone file), run nsd-control reload example.com.
What happens if NSD runs out of memory?
NSD loads all zone data into memory at startup. If the server does not have enough RAM for all zones, NSD will fail to start or be killed by the OOM killer. Monitor memory usage and ensure your server has enough RAM for your total zone data plus operating system overhead.
Can NSD serve zones from a database instead of zone files?
No. NSD reads zone data from standard zone files. If you need database-backed DNS (for dynamic hosting platforms, for example), BIND with DLZ (Dynamically Loadable Zones) or PowerDNS (which uses database backends natively) are better choices.
How often should I rotate DNSSEC keys for NSD zones?
Rotate ZSKs every 1-3 months and KSKs annually. Use ECDSAP256SHA256 or ECDSAP384SHA384 algorithms for new deployments. RSA keys are still supported but ECDSA keys are smaller, faster, and equally secure. Automate key rotation with a script that generates new keys, signs the zone, and reloads NSD.