Wszystkie systemy sprawne · ponad 40 PoP-ówHosting od 2010
INTERKVM HOST SRL·AS 25198
Strona główna/Blog/How to run your own authoritative DNS server on small VPSes
Network 25 października 2026

How to run your own authoritative DNS server on small VPSes

A hidden primary, three secondaries in different cities and networks, TSIG-signed NOTIFY and zone transfers, a working Knot DNS config with automatic DNSSEC signing, and the dig commands that prove an authoritative DNS server answers correctly. Plus what it costs and when managed DNS wins.

Opublikowano
Czas czytania
8 min
Diagram of a hidden primary DNS server sending NOTIFY and TSIG-signed zone transfers to three public secondaries: Frankfurt, Bucharest and another network.

An authoritative DNS server is where the lookup for your domain ends: a resolver asks for www.example.com, the delegation leads to your server, and it answers from its own copy of the zone. Running your own is cheap and easy to get subtly wrong: two servers in one rack, an exposed primary, a DS record pointing at no key. Each takes down a whole domain. This setup avoids them: a hidden primary, three secondaries in different cities, Knot DNS and dig tests.

Authoritative is not recursive

A recursive resolver works for clients, walking from the root to whoever holds the answer and caching it. An authoritative server answers only for its own zones, from what RFC 2182 calls local knowledge.

An authoritative server that also recurses for anyone is an open resolver, which attackers use to aim replies at spoofed victims. Keep the jobs in separate programs: NSD describes itself as an authoritative name server, and Knot DNS's guide says to move any resolver on the same host off port 53.

Two servers is the rule, three is the advice

RFC 2182 starts from the registration rule of at least two servers per zone, then explains why two is thin: while one is down, the zone runs on the other alone. It recommends three for most organisation-level zones, "with at least one which must be well removed from the others."

It wants secondaries dispersed geographically and topologically. Separate cities survive a power cut at one site; separate networks survive a routing failure inside one provider. VPSes in Frankfurt, Amsterdam and Bucharest give you the first, but in one autonomous system, ours or anyone's, they miss the second. Put one secondary with another provider.

Hidden primary, public secondaries

The hidden primary holds the master copy and signs it, but is not in the NS records, so no resolver queries it; RFC 2182 calls this a stealth server. Its firewall admits only the secondaries, which are the listed servers.

On a change, the primary raises the SOA serial and sends a NOTIFY; each secondary pulls an IXFR (the differences) or AXFR (the whole zone), and the SOA refresh timer catches a lost NOTIFY. TSIG signs every NOTIFY and transfer with a shared secret, so only your primary can push changes and only your secondaries get the zone.

Serials only go up: RFC 2182 warns that secondaries ignore a decreased serial, and every later increment, until the old value is exceeded.

Knot DNS or NSD

Knot DNS NSD
TSIG transfers Yes Yes
DNSSEC signing Built in, automatic keys Not in its docs
Catalog zones Yes Yes
Docs highlight Signing, QUIC and TLS, query modules Raw performance, XDP

NSD, BSD-licensed, aims to be a reference implementation for emerging IETF standards; for DNSSEC, sign the zone elsewhere. Knot signs and rolls keys itself, so the examples follow the Knot DNS 3.6.1 configuration guide. BIND and PowerDNS are the other mature choices; BIND can also resolve, so confirm recursion is off.

A minimal Knot DNS configuration

Generate a shared secret with keymgr -t xfr_notify_key and use it on all four machines. The hidden primary's /etc/knot/knot.conf:

server:
    listen: 192.0.2.10@53
    listen: 2001:db8:10::10@53
    automatic-acl: on

log:
  - target: syslog
    any: info

key:
  - id: xfr_notify_key
    algorithm: hmac-sha256
    secret: <the secret printed by keymgr -t>

remote:
  - id: ns1
    address: [198.51.100.11, 2001:db8:11::11]
    key: xfr_notify_key
  - id: ns2
    address: [198.51.100.22, 2001:db8:22::22]
    key: xfr_notify_key
  - id: ns3
    address: [198.51.100.33, 2001:db8:33::33]
    key: xfr_notify_key

policy:
  - id: ecdsa
    algorithm: ECDSAP256SHA256
    zsk-lifetime: 60d

zone:
  - domain: example.com
    storage: /var/lib/knot/zones/
    file: example.com.zone
    semantic-checks: on
    notify: [ns1, ns2, ns3]
    dnssec-signing: on
    dnssec-policy: ecdsa

With automatic-acl on, remotes in notify may transfer the zone if they sign with the key; an explicit acl with address, key and action: transfer is the alternative. Each secondary, differing only in listen:

server:
    listen: 198.51.100.11@53
    listen: 2001:db8:11::11@53
    automatic-acl: on

key:
  - id: xfr_notify_key
    algorithm: hmac-sha256
    secret: <the same secret>

remote:
  - id: primary
    address: [192.0.2.10, 2001:db8:10::10]
    key: xfr_notify_key

zone:
  - domain: example.com
    storage: /var/lib/knot/zones/
    master: primary

Secondaries receive the zone signed. The policy creates keys on first load and rolls the ZSK; the default ksk-lifetime is infinite, so the KSK and your DS stay put. Keys live in the KASP database, so keep its permissions tight. Apply changes with knotc reload.

Glue and the registrar

At the registrar, list the three secondaries as NS, never the primary, matching your zone's NS set. If the servers are named inside the domain they serve, nobody can find ns1.example.com without asking ns1 first, so the parent publishes the address beside the delegation. That is glue, usually registered as a host or child name server.

$ORIGIN example.com.
$TTL 3600
@    SOA  ns1.example.com. hostmaster.example.com. (
          2026102501 3600 900 1209600 3600 )  ; serial refresh retry expire negative
     NS   ns1.example.com.
     NS   ns2.example.com.
     NS   ns3.example.com.
ns1  A    198.51.100.11
ns1  AAAA 2001:db8:11::11

Confirm each secondary answers authoritatively, then register the glue, then switch the NS list.

DNSSEC without breaking your own domain

The parent publishes a DS record fingerprinting your key-signing key. An unsigned zone simply resolves; a zone whose DS matches no published key gets SERVFAIL from validating resolvers for every name.

  1. Enable signing and let the signed zone reach every secondary.
  2. Check each secondary serves RRSIG records.
  3. Knot's docs say a new KSK starts in the ready state, when the server publishes CDS and CDNSKEY records. Query them and enter the values in the registrar's DS form.
  4. Confirm validation from outside.

To turn DNSSEC off, or move to servers with different keys, change the DS first and wait out its TTL. Signatures also expire: if the primary stays down past their validity, the zone fails while every secondary looks healthy.

Rate limiting against reflection

Refusing recursion is not enough: small queries with a forged source draw larger answers, signed ones especially, onto the victim. Response rate limiting caps how fast answers repeat to one destination. In Knot it is the mod-rrl query module, enabled per zone with module or everywhere with global-module; the docs load mod-cookies before it.

TTLs for planned changes

  • Records: lower a TTL one old TTL ahead. At 86400 seconds, publish the short value 86,400 ÷ 3,600 = 24 hours before the change.
  • SOA expire: how long a secondary serves without reaching the primary. Make it weeks (1209600 is 14 × 86,400 seconds), so a dead primary is an inconvenience, not an outage.
  • Changing NS: keep the old servers answering until the parent's TTL on your delegation has passed.

Testing with dig

dig +norec @198.51.100.11 example.com SOA     # authoritative: "aa" in the flags

for ns in 192.0.2.10 198.51.100.11 198.51.100.22 198.51.100.33; do   # same serial everywhere
  printf '%s ' "$ns"; dig +norec +short @"$ns" example.com SOA | awk '{print $3}'
done                                          # run from a host the primary's firewall admits
dig +nssearch example.com                     # SOA from every listed server

dig +trace example.com NS                     # the delegation from the root down

dig +norec +dnssec @198.51.100.11 example.com SOA   # expect RRSIG records
dig +norec @198.51.100.11 example.com CDS           # what the registrar's DS form needs
dig +short example.com DS                           # what the parent publishes
delv example.com SOA                                # validates from built-in root trust anchors

dig @198.51.100.11 example.org A              # not an open resolver: REFUSED, no "ra" flag

Monitoring serial drift and expiry

A secondary that stops transferring keeps serving its old copy until the zone expires. Signing re-signs the SOA on every run, so compare secondaries with the primary, not a fixed serial:

#!/bin/sh
zone=example.com
want=$(dig +norec +short @192.0.2.10 "$zone" SOA | awk '{print $3}')
for ns in 198.51.100.11 198.51.100.22 198.51.100.33; do
  got=$(dig +norec +short +time=3 +tries=2 @"$ns" "$zone" SOA | awk '{print $3}')
  [ "$got" = "$want" ] || echo "WARN $ns serves ${got:-nothing}, primary has $want"
done
dig +norec +dnssec @198.51.100.11 "$zone" SOA | awk '$4=="RRSIG" {print $9}'   # RRSIG expiry, YYYYMMDDHHMMSS

Alert on drift longer than the refresh interval and on signatures near expiry; dedicated server monitoring covers the alerting. If a secondary is unreachable from one region, an mtr report shows where the path breaks.

Sizing: a small VPS is plenty

The work is answering small UDP queries. At an assumed 500 bytes per response, a 1 Gbps guarantee covers 1,000,000,000 ÷ (500 × 8) = 250,000 responses per second. Signing, the CPU work, happens on the primary.

Our entry unmetered VPS, one dedicated 3.2 GHz vCPU, 4 GB DDR4, 50 GB NVMe and 1 Gbps guaranteed, is €29 a month (checked October 2026) in Frankfurt, Amsterdam, Paris, Madrid, Milan, Vienna, Dublin and Bucharest. Three secondaries cost 3 × €29 = €87; a hidden primary makes €116. All share AS 25198, so the well-removed server belongs elsewhere; how to choose a VPS covers what to check there.

When managed DNS or anycast wins

  • You do not want to be on call for the service everything depends on.
  • You need GeoDNS or health-checked answers, covered in build your own CDN.
  • Your domain attracts floods that three unicast servers cannot absorb.
  • You want anycast, which needs your own prefix and BGP; see announcing your own IP space.

A middle path keeps the primary and keys yours, with secondaries at a provider that accepts NOTIFY and TSIG transfers.

Frequently asked questions

What is an authoritative DNS server?

An authoritative DNS server holds the records for a zone and answers queries about it from its own copy, without asking any other server. Every lookup for your domain ends there. A recursive resolver does the opposite job, querying other servers for a client and caching what it finds.

How many authoritative DNS servers does a domain need?

Registries require at least two. RFC 2182 recommends three for most organisations, with at least one well removed from the others, because a zone with two servers runs on one whenever either is down. Spread them across cities and onto more than one provider's network.

Can an authoritative server also be a recursive resolver?

It should not be. Combining both on one public address makes an open resolver, which strangers use for lookups and attackers use to reflect traffic at victims. Run any resolver as a separate program for your own machines, and check that the authoritative server refuses domains it does not host.

What happens if the DS record is wrong?

Validating resolvers treat the zone as broken, not unsigned, and return SERVFAIL for every name in it. Add the DS only after every secondary serves signed answers. When switching DNSSEC off or changing keys, update the DS first and wait out its TTL before retiring old keys.

Tweaksv1
Theme