Cluster / HA Setup

Deploy DomU DNS across two Raspberry Pi nodes in a Master/Slave configuration for high availability. No database required β€” just file-based sync with HMAC-SHA256 authentication.

Architecture

text
dns-node-1 (Master)          dns-node-2 (Slave)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Accepts API changes β”‚      β”‚ Read-only API        β”‚
β”‚ Stores JSON locally │─────▢│ Receives push events β”‚
β”‚ Fetches blocklists  β”‚      β”‚ Polls master (30s)   β”‚
β”‚ Pushes to slaves    β”‚      β”‚ Autonomous DNS op.   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                            β”‚
         └────────── DNS β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              Router: both IPs as DNS

Key properties:

Prerequisites: Both Pis need static IPs, SSH access, and DomU DNS installed (see Quick Start). Open ports: 53/udp, 53/tcp, 80/tcp.

Step 1: Generate Sync Secret

The sync secret authenticates all master→slave push events with HMAC-SHA256. Generate it once and use the same value on both nodes:

bash
# Generate 64-character hex secret (32 bytes)
openssl rand -hex 32
# Example: a3f2c8b1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1

# On BOTH nodes, create /etc/domudns/env:
sudo tee /etc/domudns/env << 'EOF'
DOMUDNS_SYNC_SECRET=<YOUR_64_CHAR_HEX_SECRET>
EOF
sudo chmod 600 /etc/domudns/env   # Secret file β€” restrict access!

Step 2: Configure Master (dns-node-1)

/etc/domudns/config.yaml on the master node:

yaml
cluster:
  role: "master"
  data_dir: "/var/lib/domudns/data"
  slaves:
    - "http://<SLAVE_IP>:80"      # Replace with actual slave IP

dnsserver:
  listen: "[::]:53"
  upstream:
    - "9.9.9.9"
    - "149.112.112.112"

http:
  listen: ":80"

system:
  log_level: "info"

Step 3: Configure Slave (dns-node-2)

/etc/domudns/config.yaml on the slave node:

yaml
cluster:
  role: "slave"
  data_dir: "/var/lib/domudns/data"
  master_url: "http://<MASTER_IP>:80"   # Replace with actual master IP
  sync_interval: "30s"

dnsserver:
  listen: "[::]:53"
  upstream:
    - "9.9.9.9"

http:
  listen: ":80"

system:
  log_level: "info"

Start Both Services

bash
# On both nodes:
sudo systemctl restart domudns
sudo systemctl status domudns

Verify Cluster Sync

bash
# Check cluster status on master
curl -H "Authorization: Bearer <KEY>" \
  http://<MASTER_IP>/api/cluster/status

# Create a test zone on master
curl -X POST http://<MASTER_IP>/api/zones \
  -H "Authorization: Bearer <KEY>" \
  -H "Content-Type: application/json" \
  -d '{"domain": "test.lan", "ttl": 300}'

# Verify it appears on slave (within seconds)
curl http://<SLAVE_IP>/api/zones   # Read-only, no auth needed for listing

# Test DNS from both nodes
dig @<MASTER_IP> test.lan SOA
dig @<SLAVE_IP>  test.lan SOA

Configure Router for HA DNS

Set both node IPs as DNS servers in your router's DHCP settings:

DNS ServerRoleAddress
PrimaryMasterIP of dns-node-1
SecondarySlaveIP of dns-node-2

If the master goes down, clients automatically fall back to the slave for DNS resolution.

Note: API changes (zones, blocklist) must always be made on the master. The slave's API is read-only and rejects write operations.