Skip to content

mw7101/domudns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DomU DNS

License: MIT Go Platform RAM Domains Blocked

A lightweight, privacy-first DNS server for Raspberry Pi 3B β€” ad blocking, authoritative zones, RFC 2136 DDNS, Split-Horizon, Let's Encrypt DNS-01, and Master/Slave clustering in a single Go binary using ~25 MB RAM.

Website & Docs: domudns.net


Why DomU DNS?

Every existing DNS solution was either too heavy for a Raspberry Pi 3B or lacked features needed for a proper homelab:

Feature DomU DNS Pi-hole AdGuard Home Technitium
RAM on Pi 3B ~25 MB 80–160 MB 50–130 MB 200 MB+
Ad blocking (220k+ domains) βœ… βœ… βœ… βœ…
Authoritative DNS zones βœ… ❌ ❌ βœ…
RFC 2136 DDNS (TSIG) βœ… ❌ ❌ βœ…
Split-Horizon DNS βœ… ❌ ❌ Partial
Zone Transfer AXFR/IXFR βœ… ❌ ❌ βœ…
Master/Slave Cluster βœ… ❌ ❌ βœ…
DoH + DoT βœ… Partial βœ… βœ…
Let's Encrypt DNS-01 βœ… ❌ ❌ Partial
DNSSEC signing βœ… ❌ ❌ βœ…
LRU Cache + Warming βœ… ❌ ❌ ❌
No database required βœ… ❌ βœ… βœ…
Runtime Go binary C/FTL+dnsmasq Go .NET runtime

Features

  • πŸ›‘οΈ Ad & Tracker Blocking β€” 220k+ domains, O(1) lookup, wildcard/regex support
  • 🌐 Authoritative DNS Zones β€” A, AAAA, MX, CNAME, PTR, TXT, SRV, CAA, NS; JSON file backend
  • πŸ”„ RFC 2136 DDNS β€” ISC dhcpd / Kea DHCP integration via TSIG; auto PTR records
  • πŸ”€ Split-Horizon DNS β€” different answers for internal vs. external clients
  • πŸ”— Master/Slave Cluster β€” HMAC-SHA256, atomic JSON writes, no database
  • πŸ”’ DoH + DoT β€” RFC 8484 / RFC 7858 with your own certificates
  • πŸ” Let's Encrypt DNS-01 β€” built-in ACME provider; Traefik httpreq, Certbot plugin, acme.sh/Proxmox
  • 🚫 DNS Rebinding Protection β€” blocks external domains resolving to private IPs
  • ⚑ LRU Cache + Warming β€” preloads top-N query-log domains at startup
  • πŸ“Š Next.js Dashboard β€” real-time stats, zone management, query log, cluster status
  • πŸ” Zone Transfer AXFR/IXFR β€” ACL-protected, TCP-only
  • πŸ“‘ DHCP Lease Sync β€” dnsmasq / FritzBox lease files β†’ DNS records
  • πŸ”‘ Named API Keys β€” per-service API keys with revocation
  • πŸ“ˆ Prometheus Metrics β€” queries, latency, cache hit rate
  • βš™οΈ Live Config Reload β€” upstream DNS, block mode, log level without restart

Performance

Metric Value
RAM on Raspberry Pi 3B ~25 MB
Blocklist size 220,000+ domains
Blocklist lookup O(1) β€” hash map
Response time (cache hit) 0 ms
Response time (authoritative) 0 ms
Response time (upstream) 1–8 ms
Cache capacity 10,000 entries (~5 MB)

Quick Start

# 1. Clone & build for Raspberry Pi 3B (ARMv7)
git clone https://github.com/mw7101/domudns.git
cd domudns && make build-arm

# 2. Copy to Pi
scp build/domudns-arm pi@<PI_IP>:/usr/local/bin/domudns

# 3. Install service & start
scp scripts/domudns.service pi@<PI_IP>:/tmp/
ssh pi@<PI_IP> "sudo mv /tmp/domudns.service /etc/systemd/system/ && sudo systemctl enable --now domudns"

# 4. Open setup wizard
open http://<PI_IP>/setup

No database required. All data is stored as JSON files under /var/lib/domudns/data/. Full docs: domudns.net/docs/quickstart.html


Installation

Prerequisites

  • Go 1.24+ (build machine only)
  • Raspberry Pi 3B with Debian Bookworm/Raspbian β€” or any Linux AMD64/ARM64/ARMv7
  • SSH access to the target host
  • Port 53 free (stop systemd-resolved if needed)

Step 1 β€” Build

git clone https://github.com/mw7101/domudns.git
cd domudns

# Raspberry Pi 3B (ARMv7)
make build-arm

# ARM64 (Pi 4/5, modern SBCs)
make build-arm64

# AMD64 (x86 server, NAS, VPS)
make build

Step 2 β€” Install on target host

# Create directories
ssh pi@<PI_IP> "sudo mkdir -p /etc/domudns /var/lib/domudns/data /usr/local/bin"

# Copy binary
scp build/domudns-arm pi@<PI_IP>:/tmp/domudns
ssh pi@<PI_IP> "sudo mv /tmp/domudns /usr/local/bin/domudns && sudo chmod +x /usr/local/bin/domudns"

Step 3 β€” Minimal Configuration

Create /etc/domudns/config.yaml on the host:

cluster:
  role: "master"
  data_dir: "/var/lib/domudns/data"

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

http:
  listen: ":80"

system:
  log_level: "info"

Step 4 β€” Start

scp scripts/domudns.service pi@<PI_IP>:/tmp/
ssh pi@<PI_IP> "
  sudo mv /tmp/domudns.service /etc/systemd/system/
  sudo systemctl daemon-reload
  sudo systemctl enable --now domudns
"

Step 5 β€” First-Time Setup

Open http://<PI_IP>/setup in your browser. The wizard guides you through:

  1. Set admin username and password
  2. Generate a secure API key
  3. Configure blocklist sources
  4. Add your local DNS zones

Configuration

Full reference: domudns.net/docs/config.html

cluster:
  role: "master"              # "master" | "slave"
  data_dir: "/var/lib/domudns/data"
  # slaves:
  #   - "http://<SLAVE_IP>:80"

dnsserver:
  listen: "[::]:53"
  upstream:
    - "9.9.9.9"
    - "149.112.112.112"
  cache:
    enabled: true
    max_entries: 10000
    warmup_count: 200         # Preload top domains from query log at startup
  # DNS over HTTPS (RFC 8484)
  doh:
    enabled: false
    path: "/dns-query"
  # DNS over TLS (RFC 7858)
  dot:
    enabled: false
    listen: "[::]:853"

blocklist:
  enabled: true
  file_path: "/var/lib/domudns/blocklist.hosts"
  fetch_interval: 24h
  block_mode: "zero_ip"       # "zero_ip" | "nxdomain"
  default_urls:
    - "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"

http:
  listen: "0.0.0.0:80"

system:
  log_level: info
  metrics:
    enabled: true
    listen: "0.0.0.0:9090"

Environment Variables

# Cluster sync secret (HMAC-SHA256, same on all nodes β€” required for clustering)
export DOMUDNS_SYNC_SECRET="<64-hex-characters>"

High Availability Cluster

Pi 1 (Master) ──── HTTP Push ──→ Pi 2 (Slave)
  • Master pushes zone/blocklist/config changes to all slaves via HMAC-SHA256-signed HTTP
  • Slaves poll master as fallback every 30s
  • Set both IPs as DNS servers in your router's DHCP settings β†’ automatic failover
# Master (/etc/domudns/config.yaml)
cluster:
  role: "master"
  slaves:
    - "http://<SLAVE_IP>:80"

# Slave (/etc/domudns/config.yaml)
cluster:
  role: "slave"
  master_url: "http://<MASTER_IP>:80"

Full guide: domudns.net/docs/cluster.html


Let's Encrypt DNS-01

DomU DNS acts as a built-in ACME DNS-01 provider β€” no open port 80 required. Works with:

  • Traefik β€” native httpreq provider support
  • Certbot β€” DNS plugin (certbot-dns-domudns)
  • acme.sh / Proxmox β€” shell hook script included

Full guide: domudns.net/docs/letsencrypt.html


DNS Architecture

Incoming query (port 53)
    ↓
1. Blocklist check (O(1) hash lookup)
    ↓
2. DDNS UPDATE handler (RFC 2136, TSIG)
    ↓
3. AXFR/IXFR handler (zone transfers)
    ↓
4. ACME challenge TXT (_acme-challenge.*)
    ↓
5. Authoritative zone (Split-Horizon view-aware)
    ↓
6. LRU Cache
    ↓
7. Conditional forwarding (domain-specific upstreams)
    ↓
8. Default upstream (round-robin UDP, TCP fallback)
    ↓
9. DNS Rebinding Protection
    ↓
Return response

REST API

Full reference: domudns.net/docs/api.html

# Create a zone
curl -X POST http://<PI_IP>/api/zones \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"domain": "home.lan", "ttl": 3600}'

# Add a record
curl -X POST http://<PI_IP>/api/zones/home.lan/records \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name": "nas", "type": "A", "ttl": 3600, "value": "192.168.1.100"}'

# Live config update (no restart)
curl -X PATCH http://<PI_IP>/api/config \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"dnsserver": {"upstream": ["9.9.9.9", "149.112.112.112"]}}'

Operating Modes

Mode Description Config
Standalone Single node, full functionality role: "master", no slaves:
Master Pushes changes to slaves role: "master" + slaves: [...]
Slave Receives from master, read-only API role: "slave" + master_url: ...

Development

make test           # All tests (unit + integration + security)
make test-unit
make test-integration
make lint && make fmt
sudo make run       # Port 53 requires root

Troubleshooting

# Service status
sudo systemctl status domudns
sudo journalctl -u domudns -f

# Test DNS
dig @<PI_IP> google.com
dig @<PI_IP> ads.doubleclick.net   # Should return 0.0.0.0 (blocked)

# Health check
curl http://<PI_IP>/api/health

# Reset password (forgot credentials)
echo '{}' > /var/lib/domudns/data/auth.json
sudo systemctl restart domudns
# β†’ login with admin/admin β†’ setup wizard

License

MIT License β€” see LICENSE

Contributors

Languages