RTFM · Networking
Build authoritative DNS from nothing
Build a two-server, IPv4-and-IPv6 authoritative DNS service on Saphira: delegation, zones, transfers, DNSSEC, mail records, firewalling, monitoring, backups, and recovery.

Draw the finished service before installing BIND
An authoritative nameserver answers only for zones it owns. A resolver answers arbitrary questions by following the DNS hierarchy for a client. They are different roles. This design uses two Saphira servers in distinct failure domains: losing ns0, its network, or its provider must not make example.net disappear.
Registrar / parent zone
│
├── NS ns0.example.net. with glue where required
└── NS ns1.example.net. with glue where required
│
┌──────────┴──────────┐
▼ ▼
Saphira ns0 Saphira ns1
primary authority secondary authority
203.0.113.53 198.51.100.53
2001:db8:53::53 2001:db8:54::53
│ NOTIFY and IXFR/AXFR │
└──── signed example.net zone ────┘
│
DS published at parent| Role | What it owns | What it must not be mistaken for |
|---|---|---|
| Primary | Editable source zone and a controlled transfer policy. | The only Internet authority. |
| Secondary | Transferred copy and independent public answers. | A backup file on the same host. |
| Registrar / parent | Delegation NS, in-bailiwick glue, and DS record. | Something BIND can update by itself. |
| Resolver | Recursive answers for trusted LAN/VPN clients. | A public authority listener. |
Prove it works: Real availability
Use separate hosts, networks, power paths, and preferably sites/providers. Two containers on one host, or two VMs behind one home router, can be useful for testing but are one failure domain.
Install, bind, and firewall UDP plus TCP 53
DNS is not UDP-only. UDP handles many ordinary queries, but TCP is required for zone transfers and fallback after a truncated answer. EDNS and DNSSEC make larger responses normal. Block TCP/53 and some resolvers will fail even though a simple UDP test looked healthy.
# Inspect Saphira packages, then install the authority and diagnostics.
apk update
apk search -v bind
apk info -a bind
apk add bind bind-tools nftables
# Normal Saphira/OpenRC operation after configuration validation.
rc-service named status
rc-update add named default
# Systemd-selected Saphira only: prove the unit exists first.
systemctl status named.servicetable inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iifname "lo" accept
iifname "wan0" udp dport 53 accept
iifname "wan0" tcp dport 53 accept
}
}
# Replace wan0 with the actual public interface; inet applies to IPv4 and IPv6.ss -lntup '( sport = :53 )'
dig @ns0.example.net example.net SOA +tcp +noall +comments +answer
# Healthy: named owns UDP and TCP on intended IPv4 and IPv6 addresses.
# Bad: only loopback is unreachable from the Internet; an unintended wildcard
# listener can expose a management interface.Create a zone: SOA, records, and serial lifecycle
A zone is a database. The SOA is its version and timing contract with secondaries and caches. Every served edit must increase the serial. A date-based serial such as 2026082702 is readable, but the essential rule is that it always increases.
$TTL 3600
@ IN SOA ns0.example.net. hostmaster.example.net. (
2026082701 ; serial: increase on every published change
3600 ; refresh: secondary checks the primary
600 ; retry: wait after a failed refresh
1209600 ; expire: secondary stops serving an isolated stale copy
3600 ; negative-cache TTL for NXDOMAIN or NODATA
)
IN NS ns0.example.net.
IN NS ns1.example.net.
ns0 IN A 203.0.113.53
ns0 IN AAAA 2001:db8:53::53
ns1 IN A 198.51.100.53
ns1 IN AAAA 2001:db8:54::53
www IN A 203.0.113.10
www IN AAAA 2001:db8:100::10
mail IN A 203.0.113.25
mail IN AAAA 2001:db8:100::25
@ IN MX 10 mail.example.net.
_alias IN CNAME www.example.net.
_autodiscover._tcp IN SRV 0 5 443 www.example.net.
@ IN CAA 0 issue "letsencrypt.org"
@ IN TXT "v=spf1 mx -all"| Type | Purpose | Important constraint |
|---|---|---|
| A | Name to IPv4 address. | Does not supply IPv6. |
| AAAA | Name to IPv6 address. | Test the full IPv6 path before publishing. |
| CNAME | Alias one name to another. | Cannot coexist with other data at its owner. |
| MX | Mail destination with priority. | Target needs A/AAAA; do not target a CNAME. |
| TXT | SPF, DKIM, DMARC, BIMI, verification. | Preserve generated text exactly. |
| SRV | Service priority, weight, port, target. | Target is a fully qualified hostname. |
| CAA | Which CA may issue certificates. | Can intentionally block ACME when wrong. |
| PTR | Address to hostname in reverse DNS. | Address owner controls the parent reverse delegation. |
| NS | Names authoritative servers. | Must agree with parent delegation. |
| SOA | Primary, serial, transfer and negative-cache timing. | Serial change is mandatory for propagation. |
The final SOA value means negative answers are cached too. If a client asks for a not-yet-created name, then you add it, the client can still see NXDOMAIN until the negative TTL expires. That is normal cache behaviour, not evidence that the new zone file failed.
Delegate at the parent and understand glue
Loading a zone on ns0 does not make it Internet-visible. The registrar must publish a parent delegation to your NS names. If an NS name is inside the delegated child domain, such as ns0.example.net for example.net, the parent must also provide its address as glue. Otherwise a resolver needs example.net DNS to locate ns0.example.net, while needing ns0.example.net to resolve example.net.
# Follow the hierarchy from root to child.
dig +trace example.net NS
# After +trace identifies the parent, inspect delegation and glue there.
dig @a.gtld-servers.net example.net NS +noall +authority +additional
# Both child authorities must give the same SOA serial and NS set.
dig @ns0.example.net example.net SOA +noall +comments +answer
dig @ns1.example.net example.net SOA +noall +comments +answer| Symptom | Likely layer | Recovery |
|---|---|---|
| Parent points to old nameservers. | Registrar/parent delegation. | Correct parent NS; editing the child cannot override it. |
| In-bailiwick NS has missing or wrong glue. | Registrar/parent glue. | Set matching public IPv4 and IPv6 glue. |
| Parent and child NS differ. | Inconsistent delegation or stale authority. | Make the intended NS set agree at both layers. |
| One nameserver times out. | Firewall, binding, route, or provider. | Test UDP/TCP 53 and IPv4/IPv6 directly. |
Operate primary and secondary DNS safely
The primary owns editable source. The secondary serves a transferred copy. After a higher SOA serial, the primary sends NOTIFY; the secondary checks for a newer serial and transfers the whole zone with AXFR or changes with IXFR. NOTIFY makes changes prompt, while refresh polling means a secondary still learns changes if NOTIFY is lost.
// Primary ns0. Keep the actual TSIG secret in a protected local file.
key "ns1-transfer" {
algorithm hmac-sha256;
secret "REPLACE-WITH-A-PROTECTED-LOCAL-SECRET";
};
zone "example.net" {
type primary;
file "/var/bind/primary/example.net.zone";
allow-transfer { key ns1-transfer; };
also-notify { 198.51.100.53 key ns1-transfer; 2001:db8:54::53 key ns1-transfer; };
notify yes;
};
// Secondary ns1.
server 203.0.113.53 { keys { ns1-transfer; }; };
zone "example.net" {
type secondary;
primaries { 203.0.113.53 key ns1-transfer; 2001:db8:53::53 key ns1-transfer; };
file "/var/bind/secondary/example.net.zone";
};TSIG authenticates transfer and notification peers. It is not public DNSSEC. Restrict AXFR/IXFR even with TSIG. The shared secret must never be in Git, a support ticket, or a public zone; keep file ownership and mode restricted to the account that runs named and the administrator.
named-checkzone example.net /var/bind/primary/example.net.zone
named-checkconf /etc/bind/named.conf
rndc reload example.net
rndc zonestatus example.net
dig @ns0.example.net example.net SOA +short
dig @ns1.example.net example.net SOA +short
dig @ns1.example.net example.net SOA +tcp +short
# From the explicitly authorised secondary only, an AXFR may succeed.
# From everywhere else it should be refused.
dig @ns0.example.net example.net AXFRMake changes, inspect errors, and recover
1. Edit source and increase serial
Change the primary source file, increase the SOA serial once, and keep the previous known-good version.
2. Validate before loading
Run named-checkzone for the precise file and named-checkconf for the complete configuration. Correct the reported line rather than restarting repeatedly.
3. Reload the zone and inspect its state
Use rndc reload example.net followed by rndc zonestatus example.net. Use rc-service named reload for the daemon only after validation; systemd-selected Saphira uses systemctl reload named.service after unit verification.
4. Prove both authorities and both transports
Query ns0 and ns1 directly for SOA and changed data over UDP and TCP, then test externally over IPv4 and IPv6.
| Response | What it means | First check |
|---|---|---|
| NOERROR with answer | Requested record exists. | Answer, aa flag, serial, and authority agreement. |
| NODATA | Name exists but this type does not. | Expected record type and negative-cache TTL. |
| NXDOMAIN | Authoritative zone says name does not exist. | Spelling, origin, serial, and cache lifetime. |
| REFUSED | Server intentionally declined. | Correct authority and ACL/recursion/transfer policy. |
| SERVFAIL | Server could not complete processing. | Direct authority, logs, transfers, DNSSEC and parent DS. |
| Timeout | No response arrived. | Route, firewall, binding, UDP/TCP, IPv4/IPv6, glue. |
SERVFAIL is not one fault. If a direct query to ns0 fails, inspect zone load, permissions, transfer, and signing logs. If direct authorities are healthy but a validating resolver gives SERVFAIL, compare DNSKEY and parent DS, signature expiry, delegation, and IPv6 reachability. Query with dig plus trace to identify where the hierarchy stops, and use delv where installed to expose DNSSEC validation.
rndc status
rndc zonestatus example.net
dig @ns0.example.net example.net SOA +dnssec +noall +comments +answer
dig +trace example.net SOA
delv example.net SOA
# OpenRC logging depends on the configured syslog daemon.
rc-service named status
tail -f /var/log/messages
# Systemd-selected Saphira.
journalctl -u named.service -n 100 --no-pagerReverse DNS, mail, DNSSEC, and disaster recovery
Forward DNS is your zone. Reverse DNS belongs to the public-address owner. For IPv4, that owner may set a PTR or delegate an in-addr.arpa zone. For IPv6 it may delegate a nibble-aligned ip6.arpa zone, commonly /48, /56, or /64. For MailDragon, test the whole chain: public IP to PTR to mail hostname to A/AAAA back to the same public IP.
# Mail record chain. MailDragon generates DKIM material and guidance;
# the operator publishes it at the chosen authority.
dig example.net MX +short
dig mail.example.net A +short
dig mail.example.net AAAA +short
dig -x 203.0.113.25 +short
dig example.net TXT +short
dig selector._domainkey.example.net TXT +short
dig _dmarc.example.net TXT +short
dig default._bimi.example.net TXT +short
# DNSSEC chain and independent monitor.
dig +dnssec example.net DNSKEY +multi
dig +dnssec example.net DS +multi
delv example.net SOA
dig @ns0.example.net example.net SOA +time=2 +tries=1 +tcp +short
dig @ns1.example.net example.net SOA +time=2 +tries=1 +tcp +shortDNSSEC is a complete lifecycle: active signing keys produce DNSKEY and RRSIG records; the active key produces a DS; the parent publishes that DS; validating resolvers prove the chain. A wrong or stale DS often appears as recursive SERVFAIL. During rollover, keep old and new key material according to the selected signing model until the parent DS and cache lifetimes are safely aligned. To disable DNSSEC, remove DS at the parent first, wait for propagation, then stop signing. Never unsign first while a DS remains.
Back up configuration, editable primary zones, generated secondary zones where useful, DNSSEC private keys and managed signing state, TSIG secrets, service ownership/modes, registrar delegation, glue, DS values, and restoration instructions. Encrypt and restrict backups containing secrets. Rehearse restore in an isolated Saphira VM: validate config and zones, restore signing state, compare SOA/DNSKEY, then test queries before any public cutover.
To migrate from hosted DNS, lower the old TTLs in advance, inventory every record including CAA and verification TXT, reproduce the zone on both new authorities, validate direct IPv4/IPv6 UDP and TCP answers, then change parent delegation. Keep the old provider until independent checks prove delegation, glue, DNSSEC, mail records, and both new servers. Split DNS is a separate design: private names or private answers belong only on internal LAN/VPN resolvers, with matching routes and firewall policy. Never publish private addresses or management topology in the public zone.