DNS Operations & Record Management

Comprehensive DNS operations runbook for bind-01/bind-02 and VyOS DNS forwarder integration. Covers forward/reverse records, SOA serial updates, zone transfers, and netapi automation.

1. Quick Reference

Task Command

Add A record

sudo nsupdate -l (see below)

Add PTR record

sudo nsupdate -l (see below)

Check forward

dig +short <hostname>

Check reverse

dig +short -x <ip>

Restart BIND

sudo systemctl restart named

Check zone serial

dig SOA inside.domusdigitalis.dev +short

Zone transfer test

dig @bind-01 inside.domusdigitalis.dev AXFR

2. Infrastructure

Host Role IP

bind-01

Primary DNS (Authoritative)

10.50.1.90

bind-02

Secondary DNS

10.50.1.91

VyOS VIP

DNS Forwarder

10.50.1.1

Zone files location: /var/named/

Forward zone: inside.domusdigitalis.dev

Reverse zone: 1.50.10.in-addr.arpa

3. Zone File Structure Reference

The forward zone file is organized by IP range sections:

; Gateway (.1)
; Network Devices (.10-19)
; Identity Services (.20-29)
; iPSK Manager (.30-39)
; Wireless (.40-49)
; Windows Servers (.50-59)
; Active Directory SRV Records
; PKI Services (.60-69)
; Storage/Git (.70-79)
; IdP/SSO (.80-89)
; DNS Services (.90-99)
; LDAP/Directory (.100-109)
; Load Balancers (.110-119)
; Kubernetes (.120-129)
; IPMI/BMC (.200-209)
; Aliases (CNAMEs)

4. Useful awk Commands for Zone Files

4.1. View Zone File with Line Numbers

sudo awk 'NR<=80 {print NR": "$0}' /var/named/inside.domusdigitalis.dev.zone

4.2. View Specific Line Range

# Lines 70-85 (k3s section)
sudo awk 'NR>=70 && NR<=85 {print NR": "$0}' /var/named/inside.domusdigitalis.dev.zone

4.3. Find Section by IP Range

# Find entries in .120-129 range
sudo awk '/\.12[0-9]/ {print NR": "$0}' /var/named/inside.domusdigitalis.dev.zone

4.4. Find Host by Name

sudo awk '/grafana|prometheus|alertmanager/ {print NR": "$0}' /var/named/inside.domusdigitalis.dev.zone

4.5. Count Records by Type

sudo awk '/IN[[:space:]]+A/ {a++} /IN[[:space:]]+CNAME/ {c++} /IN[[:space:]]+SRV/ {s++} END {print "A:", a, "CNAME:", c, "SRV:", s}' /var/named/inside.domusdigitalis.dev.zone

4.6. View SOA Serial

sudo awk '/Serial/ {print $1}' /var/named/inside.domusdigitalis.dev.zone

5. Reverse Zone awk Commands

5.1. View PTR Records with Line Numbers

sudo awk '/IN[[:space:]]+PTR/ {print NR": "$0}' /var/named/10.50.1.rev

5.2. Find PTR by IP Octet

# Find .120 PTR
sudo awk '/^120/ {print NR": "$0}' /var/named/10.50.1.rev

5.3. Verify No Leading Whitespace (Critical!)

# Brackets reveal hidden whitespace
sudo awk '/IN[[:space:]]+PTR/ {print NR": ["$0"]"}' /var/named/10.50.1.rev

If output shows [ 120 …​] (leading spaces inside brackets), records are broken.

5.4. Count PTR Records

sudo awk '/IN[[:space:]]+PTR/ {count++} END {print "PTR records:", count}' /var/named/10.50.1.rev

5.5. List All PTR Records (IP → Hostname)

sudo awk '/IN[[:space:]]+PTR/ {gsub(/[[:space:]]+/, " "); print $1 " -> " $4}' /var/named/10.50.1.rev

6. Add Host Records (Full Procedure)

6.1. Step 1: Plan the Record

Field Value

Hostname

k3s-master-01

FQDN

k3s-master-01.inside.domusdigitalis.dev

IP Address

10.50.1.120

Reverse

120.1.50.10.in-addr.arpa

6.2. Step 2: Connect to bind-01

ssh bind-01

6.3. Step 3: Add Forward (A) Record

6.3.1. Option A: nsupdate (Dynamic Update)

sudo nsupdate -l << 'EOF'
zone inside.domusdigitalis.dev
update add k3s-master-01.inside.domusdigitalis.dev. 3600 A 10.50.1.120
send
EOF

If nsupdate -l fails with FORMERR or TSIG error:

; Communication with ::1#53 failed: timed out
update failed: FORMERR
; TSIG error with server: expected a TSIG or SIG(0)

BIND is not configured for local dynamic updates. Use Option B (manual zone file edit) instead.

6.3.2. Option B: Manual Zone File Edit (Fallback)

sudo vi /var/named/inside.domusdigitalis.dev.zone

1. Increment SOA serial (near top of file, format YYYYMMDDNN):

@ IN SOA bind-01.inside.domusdigitalis.dev. admin.inside.domusdigitalis.dev. (
    2026022301 ; Serial - INCREMENT THIS (today's date + sequence)
    ...

2. Add A record (in the A records section with other hosts):

k3s-master-01   IN      A       10.50.1.120

3. Save and exit (:wq)

4. Validate syntax:

sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zone
Expected output
zone inside.domusdigitalis.dev/IN: loaded serial 2026022301
OK

5. Reload zone:

sudo rndc reload inside.domusdigitalis.dev

6.3.3. Verify Forward Record

dig +short k3s-master-01.inside.domusdigitalis.dev @localhost
Expected output
10.50.1.120

6.4. Step 4: Add Reverse (PTR) Record

6.4.1. Option A: nsupdate (Dynamic Update)

sudo nsupdate -l << 'EOF'
zone 1.50.10.in-addr.arpa
update add 120.1.50.10.in-addr.arpa. 3600 PTR k3s-master-01.inside.domusdigitalis.dev.
send
EOF

6.4.2. Option B: Manual Zone File Edit (Fallback)

sudo vi /var/named/10.50.1.rev
Reverse zone filename is 10.50.1.rev, not 1.50.10.in-addr.arpa.zone.

1. Increment SOA serial

2. Add PTR record:

CRITICAL: NO leading whitespace! Zone files are whitespace-sensitive:

  • Record at column 1 → uses the specified owner name

  • Record starting with whitespace → continues previous owner name (WRONG!)

; WRONG - leading spaces causes NXDOMAIN:
  120     IN      PTR     k3s-master-01.inside.domusdigitalis.dev.

; CORRECT - starts at column 1:
120             IN      PTR     k3s-master-01.inside.domusdigitalis.dev.
120             IN      PTR     k3s-master-01.inside.domusdigitalis.dev.

3. Save, validate, reload:

sudo named-checkzone 1.50.10.in-addr.arpa /var/named/10.50.1.rev
sudo rndc reload 1.50.10.in-addr.arpa

6.4.3. Verify Reverse Record

dig +short -x 10.50.1.120 @localhost
Expected output
k3s-master-01.inside.domusdigitalis.dev.

6.5. Step 5: Verify SOA Serial Updated

dig SOA inside.domusdigitalis.dev +short | awk '{print "Serial: "$3}'
nsupdate automatically increments the SOA serial. Manual zone file edits require manual serial increment (Step 3B/4B).

6.6. Step 6: Force Zone Transfer to Secondary

sudo rndc notify inside.domusdigitalis.dev
sudo rndc notify 1.50.10.in-addr.arpa

Verify on bind-02:

dig +short k3s-master-01.inside.domusdigitalis.dev @10.50.1.91
dig +short -x 10.50.1.120 @10.50.1.91

6.7. Step 7: Clear VyOS DNS Cache

ssh vyos-01 "restart dns forwarding"

6.8. Step 8: Verify from Client

dig +short k3s-master-01.inside.domusdigitalis.dev
dig +short -x 10.50.1.120

Both should resolve correctly from any client using VyOS as DNS forwarder.

7. Bulk Record Addition

7.1. Kubernetes Cluster Example

Add all k3s nodes at once:

sudo nsupdate -l << 'EOF'
zone inside.domusdigitalis.dev
update add k3s-master-01.inside.domusdigitalis.dev. 3600 A 10.50.1.120
update add k3s-master-02.inside.domusdigitalis.dev. 3600 A 10.50.1.121
update add k3s-master-03.inside.domusdigitalis.dev. 3600 A 10.50.1.122
update add k3s-worker-01.inside.domusdigitalis.dev. 3600 A 10.50.1.123
update add k3s-worker-02.inside.domusdigitalis.dev. 3600 A 10.50.1.124
update add k3s-worker-03.inside.domusdigitalis.dev. 3600 A 10.50.1.125
send
EOF

Reverse records:

sudo nsupdate -l << 'EOF'
zone 1.50.10.in-addr.arpa
update add 120.1.50.10.in-addr.arpa. 3600 PTR k3s-master-01.inside.domusdigitalis.dev.
update add 121.1.50.10.in-addr.arpa. 3600 PTR k3s-master-02.inside.domusdigitalis.dev.
update add 122.1.50.10.in-addr.arpa. 3600 PTR k3s-master-03.inside.domusdigitalis.dev.
update add 123.1.50.10.in-addr.arpa. 3600 PTR k3s-worker-01.inside.domusdigitalis.dev.
update add 124.1.50.10.in-addr.arpa. 3600 PTR k3s-worker-02.inside.domusdigitalis.dev.
update add 125.1.50.10.in-addr.arpa. 3600 PTR k3s-worker-03.inside.domusdigitalis.dev.
send
EOF

8. Delete Records

8.1. Delete A Record

sudo nsupdate -l << 'EOF'
zone inside.domusdigitalis.dev
update delete old-host.inside.domusdigitalis.dev. A
send
EOF

8.2. Delete PTR Record

sudo nsupdate -l << 'EOF'
zone 1.50.10.in-addr.arpa
update delete 199.1.50.10.in-addr.arpa. PTR
send
EOF

9. Modify Records

Delete then add (atomic update):

sudo nsupdate -l << 'EOF'
zone inside.domusdigitalis.dev
update delete host.inside.domusdigitalis.dev. A
update add host.inside.domusdigitalis.dev. 3600 A 10.50.1.200
send
EOF

10. CNAME Records

sudo nsupdate -l << 'EOF'
zone inside.domusdigitalis.dev
update add k8s.inside.domusdigitalis.dev. 3600 CNAME k3s-master-01.inside.domusdigitalis.dev.
send
EOF

11. SOA Serial Management

11.1. Check Current Serial

dig SOA inside.domusdigitalis.dev +short | awk '{print "Primary:", $1, "| Serial:", $3}'

11.2. Manual Zone File Edit (If Required)

Prefer nsupdate over manual edits. Manual edits require: 1. Incrementing SOA serial 2. Reloading zone 3. Manual zone transfer trigger

If you must edit zone files directly:

sudo vim /var/named/inside.domusdigitalis.dev.zone

Increment SOA serial (format: YYYYMMDDNN):

@ IN SOA bind-01.inside.domusdigitalis.dev. admin.inside.domusdigitalis.dev. (
    2026022201 ; Serial - INCREMENT THIS
    3600       ; Refresh
    600        ; Retry
    604800     ; Expire
    86400      ; Minimum TTL
)

Reload zone:

sudo rndc reload inside.domusdigitalis.dev

Verify:

sudo named-checkzone inside.domusdigitalis.dev /var/named/inside.domusdigitalis.dev.zone

12. Zone Transfer Operations

12.1. Check Secondary Sync Status

# On bind-01 (primary)
dig SOA inside.domusdigitalis.dev @10.50.1.90 +short | awk '{print "Primary serial:", $3}'

# On bind-02 (secondary)
dig SOA inside.domusdigitalis.dev @10.50.1.91 +short | awk '{print "Secondary serial:", $3}'

Serials should match.

12.2. Force Zone Transfer

# On primary
sudo rndc notify inside.domusdigitalis.dev

# On secondary
sudo rndc refresh inside.domusdigitalis.dev

12.3. Full Zone Transfer Test

dig @bind-01 inside.domusdigitalis.dev AXFR | head -20

13. VyOS DNS Forwarding

VyOS forwards DNS queries to BIND (bind-01/bind-02). BIND is the authoritative DNS server.

13.1. View DNS Forwarding Configuration

ssh vyos-01 "show configuration commands | grep dns"

13.2. Current DNS Forwarding Setup

ssh vyos-01 "show dns forwarding statistics"

13.3. Configure DNS Forwarding (Reference)

This configuration is already deployed. Reference only:

configure
set service dns forwarding listen-address 10.50.1.1
set service dns forwarding name-server 10.50.1.90
set service dns forwarding name-server 10.50.1.91
set service dns forwarding allow-from 10.50.0.0/16
commit
save

13.4. Clear DNS Forwarding Cache

ssh vyos-01 "restart dns forwarding"

13.5. DNS Resolution Path

  1. Client queries VyOS VIP (10.50.1.1)

  2. VyOS forwards to bind-01 (10.50.1.90) or bind-02 (10.50.1.91)

  3. BIND returns authoritative answer for inside.domusdigitalis.dev

  4. BIND forwards external queries to upstream (8.8.8.8, 1.1.1.1)

14. Troubleshooting

14.1. Record Not Resolving

Check on BIND directly:

dig +short hostname.inside.domusdigitalis.dev @10.50.1.90

Check zone file:

sudo grep hostname /var/named/inside.domusdigitalis.dev.zone

Check BIND logs:

sudo journalctl -u named --since "5 minutes ago" | grep -i error

14.2. PTR Not Resolving

Verify reverse zone:

dig +short -x 10.50.1.120 @10.50.1.90

If result is empty or NXDOMAIN, check zone file.

Check reverse zone file:

sudo awk '/120/ {print NR": ["$0"]"}' /var/named/10.50.1.rev

Most common cause: Leading whitespace

Zone validated OK but lookup returns NXDOMAIN? Check for leading spaces:

# Show exact characters (brackets reveal whitespace)
sudo awk '/120/ {print NR": ["$0"]"}' /var/named/10.50.1.rev

If output shows [ 120 …​] (leading spaces inside brackets), fix with:

# Remove leading whitespace from lines with PTR records
sudo sed -i '/IN[[:space:]]*PTR/s/^[[:space:]]*//' /var/named/10.50.1.rev

# Verify fix
sudo awk '/120/ {print NR": ["$0"]"}' /var/named/10.50.1.rev

# Reload zone
sudo rndc reload 1.50.10.in-addr.arpa

# Test
dig +short -x 10.50.1.120 @localhost

14.3. Secondary Not Syncing

Check AXFR permission:

dig @bind-01 inside.domusdigitalis.dev AXFR

If "Transfer failed", check /etc/named.conf for allow-transfer directive.

Check secondary logs:

ssh bind-02 "sudo journalctl -u named --since '10 minutes ago' | grep -i transfer"

14.4. VyOS Not Resolving New Records

Restart DNS forwarding:

ssh vyos-01 "restart dns forwarding"

Verify VyOS is using BIND:

ssh vyos-01 "show configuration commands | grep 'name-server'"

15. Validation Script

Run after adding records:

#!/bin/bash
# dns-validate.sh <hostname> <ip>

HOST=$1
IP=$2
DOMAIN="inside.domusdigitalis.dev"

echo "=== Validating $HOST.$DOMAIN ($IP) ==="

# Forward lookup
FORWARD=$(dig +short $HOST.$DOMAIN)
[ "$FORWARD" = "$IP" ] && echo "✓ Forward: $FORWARD" || echo "✗ Forward: expected $IP, got $FORWARD"

# Reverse lookup
REVERSE=$(dig +short -x $IP | sed 's/\.$//')
[ "$REVERSE" = "$HOST.$DOMAIN" ] && echo "✓ Reverse: $REVERSE" || echo "✗ Reverse: expected $HOST.$DOMAIN, got $REVERSE"

# Primary DNS
P_FWD=$(dig +short $HOST.$DOMAIN @10.50.1.90)
[ "$P_FWD" = "$IP" ] && echo "✓ bind-01: $P_FWD" || echo "✗ bind-01: $P_FWD"

# Secondary DNS
S_FWD=$(dig +short $HOST.$DOMAIN @10.50.1.91)
[ "$S_FWD" = "$IP" ] && echo "✓ bind-02: $S_FWD" || echo "✗ bind-02: $S_FWD"

# VyOS (DNS forwarder)
VYOS_FWD=$(dig +short $HOST.$DOMAIN @10.50.1.1)
[ "$VYOS_FWD" = "$IP" ] && echo "✓ VyOS: $VYOS_FWD" || echo "✗ VyOS: $VYOS_FWD"

Usage:

./dns-validate.sh k3s-master-01 10.50.1.120

16. Chronicle

16.1. 2026-02-23: k3s Monitoring Stack DNS Records

Added DNS records for Prometheus/Grafana/AlertManager (all point to k3s-master-01):

Hostname IP

grafana.inside.domusdigitalis.dev

10.50.1.120

prometheus.inside.domusdigitalis.dev

10.50.1.120

alertmanager.inside.domusdigitalis.dev

10.50.1.120

PTR record: 120.1.50.10.in-addr.arpak3s-master-01.inside.domusdigitalis.dev

Single PTR for shared IP - k3s-master-01 is canonical hostname.

16.1.1. Troubleshooting: PTR Whitespace Issue (Test Case)

Symptom: Zone validated OK, but reverse lookup returned empty.

sudo named-checkzone 1.50.10.in-addr.arpa /var/named/10.50.1.rev
zone 1.50.10.in-addr.arpa/IN: loaded serial 2026022301
OK

dig +short -x 10.50.1.120 @localhost
# (empty result)

Diagnosis: Use awk with brackets to reveal hidden whitespace:

sudo awk '/120|121|122/ {print NR": ["$0"]"}' /var/named/10.50.1.rev
58: [; Kubernetes (.120-129)]
59: [120             IN  PTR     k3s-master-01.inside.domusdigitalis.dev.]
60: [121             IN  PTR     k3s-master-02.inside.domusdigitalis.dev.]
61: [122             IN  PTR     k3s-master-03.inside.domusdigitalis.dev.]

Records started at column 1 (no leading spaces) - whitespace was NOT the issue here.

Fix: Reload zone (sometimes required after validation):

sudo rndc reload 1.50.10.in-addr.arpa
zone reload queued

dig +short -x 10.50.1.120 @localhost
k3s-master-01.inside.domusdigitalis.dev.

Lesson: Always run rndc reload after zone validation, even if zone was already reloaded.

16.2. 2026-02-21: k3s DNS Records

Added DNS records for k3s cluster:

Hostname IP

k3s-master-01.inside.domusdigitalis.dev

10.50.1.120

k3s-master-02.inside.domusdigitalis.dev

10.50.1.121

k3s-master-03.inside.domusdigitalis.dev

10.50.1.122

17. References