DNS server for blocklists. Answers DNS queries for IP ranges and domain names. Good for spam filtering and network access control.
- All 7 dataset types (ip4trie, ip4set, ip4tset, ip6trie, ip6tset, dnset, generic)
- IPv6 with AAAA records
- Per-zone access control (ACLs)
- Prometheus/OpenTelemetry metrics
- Config file (YAML) or CLI mode
- Dynamic config reloading (add/remove/update zones without restart)
- Single binary, no external dependencies
go build./rbldnsd -c rbldnsd.yamlExample rbldnsd.yaml:
server:
bind: "0.0.0.0:53"
auto_reload: true # Watch config for changes
zones:
- name: bl.example.com
type: ip4trie
files:
- /etc/rbldnsd/blocklist.txt
ns:
- ns1.example.com
soa:
mname: ns1.example.com
rname: hostmaster.example.com
serial: 2024010101
- name: wl.example.com
type: generic
files:
- /etc/rbldnsd/whitelist.txt
acl_rules:
allow:
- 192.168.0.0/16
deny:
- 203.0.113.0/24
metrics:
prometheus_endpoint: "0.0.0.0:9090"# Single zone
./rbldnsd -z "bl.local:ip4trie:blocklist.txt"
# Multiple zones
./rbldnsd -z "bl.local:ip4trie:blocklist.txt wl.local:generic:whitelist.txt"
# Custom port
./rbldnsd -b 127.0.0.1:5300 -z "bl.local:ip4trie:blocklist.txt"
# Foreground (for debugging)
./rbldnsd -n -b 127.0.0.1:5300 -z "bl.local:ip4trie:blocklist.txt"rbldnsd supports 7 different dataset types optimized for different use cases:
| Type | Use |
|---|---|
| ip4trie | IPv4 blocklists (efficient trie-based) |
| ip4set | IPv4 simple ranges |
| ip4tset | IPv4 with per-entry values |
| ip6trie | IPv6 blocklists |
| ip6tset | IPv6 with per-entry values |
| dnset | Domain blocklists with wildcards |
| generic | Standard DNS records (A, MX, TXT, AAAA) |
IP4 Blocklist (ip4trie) - Just the IP/CIDR, responds with 127.0.0.2 by default:
192.0.2.0/24
203.0.113.0/24
!192.0.2.50
Generic DNS Records (generic):
example.com 3600 IN A 192.0.2.1
example.com 3600 IN TXT "v=spf1 mx -all"
mail.example.com 3600 IN A 192.0.2.2
example.com 3600 IN MX 10 mail.example.com
Domain Blocklist (dnset) - Just the domain, responds with 127.0.0.2 by default:
spam.example.com
badactor.org
*.evil.net
!trusted.evil.net
For detailed format documentation for all dataset types, see ZONE_FORMAT.md.
# Check if IP is listed (reverse DNS)
dig @localhost 4.3.2.192.bl.local A
# Forward DNS query
dig @localhost wl.local A
dig @localhost wl.local MX| Flag | Default | Use |
|---|---|---|
-c file.yaml |
- | Load config from file |
-z "zone:type:file" |
- | Define zone via CLI (spaces separate multiple zones) |
-b addr:port |
0.0.0.0:53 | Bind address |
-n |
- | Run in foreground |
-v |
- | Show version |
Config file mode (-c) is recommended for production. Use when you need multiple zones, ACLs, or metrics.
CLI mode (-z) is for testing or simple one-zone setups. Limitations: no ACLs, no NS/SOA records, no metrics.
Examples:
# Config file (recommended)
./rbldnsd -c rbldnsd.yaml
# CLI - single zone
./rbldnsd -z "bl.local:ip4trie:blocklist.txt"
# CLI - multiple zones (spaces separate)
./rbldnsd -z "bl.local:ip4trie:blocklist.txt wl.local:generic:whitelist.txt"
# CLI - custom port
./rbldnsd -b 127.0.0.1:5300 -z "bl.local:ip4trie:blocklist.txt"
# Foreground for debugging
./rbldnsd -n -b 127.0.0.1:5300 -z "bl.local:ip4trie:blocklist.txt"SIGHUP- Reload zonesSIGTERM/SIGINT- Graceful shutdown
docker build -t rbldnsd .
docker-compose up -dDefault (uses /config/rbldnsd.yaml):
docker run -d \
--name rbldnsd \
-p 53:53/udp \
-v $(pwd)/config:/config:ro \
-v $(pwd)/data:/data:ro \
rbldnsd:latestCustom config path:
docker run -d \
--name rbldnsd \
-p 53:53/udp \
-v $(pwd)/config:/my-config:ro \
-v $(pwd)/data:/data:ro \
-e CONFIG_PATH=/my-config/rbldnsd.yaml \
rbldnsd:latestCLI flags (no config file):
docker run -d \
--name rbldnsd \
-p 53:53/udp \
-v $(pwd)/data:/data:ro \
rbldnsd:latest -z "bl.local:ip4trie:/data/blocklist.txt"rbldnsd watches the config file for changes:
- Add zone → Loaded automatically
- Remove zone → Unloaded automatically
- Update zone files → Reloaded automatically
Requires auto_reload: true in config (default).
To manually reload (SIGHUP signal):
docker kill -s HUP rbldnsdserver:
bind: "0.0.0.0:53" # Listen address
timeout: 5 # Query timeout in seconds
auto_reload: true # Watch config for changes
reload_debounce: 2 # Debounce delay in seconds (batches file changes)
read_timeout: 1 # UDP read timeout in seconds
shutdown_timeout: 5 # Graceful shutdown timeout in seconds
udp_buffer_size: 512 # UDP receive buffer size in bytes
default_ttl: 3600 # Default TTL for DNS records in seconds
soa_refresh: 3600 # Default SOA refresh interval in seconds
soa_retry: 600 # Default SOA retry interval in seconds
soa_expire: 86400 # Default SOA expire time in seconds
soa_minimum: 3600 # Default SOA minimum TTL in secondslogging:
level: "info" # Log level (debug, info, warn, error)zones:
- name: bl.example.com
type: ip4trie # Dataset type
files:
- /data/blocklist.txt
# ACL options (choose one):
# Option 1: Inline rules
acl_rules:
allow:
- 192.168.0.0/16
deny:
- 203.0.113.0/24
# Option 2: External file
# acl: /etc/rbldnsd/acl.txt
# NS records (optional)
ns:
- ns1.example.com
- ns2.example.com
# SOA record (optional)
soa:
mname: ns1.example.com
rname: hostmaster.example.com
serial: 2024010101
refresh: 3600
retry: 600
expire: 86400
minimum: 3600allow:
192.168.0.0/16
10.0.0.0/8
127.0.0.1
deny:
203.0.113.0/24
metrics:
prometheus_endpoint: "0.0.0.0:9090"
otel_endpoint: "http://localhost:4318"| Type | Use |
|---|---|
| ip4trie | IPv4 blocklists (efficient trie-based) |
| ip4set | IPv4 simple ranges |
| ip4tset | IPv4 with per-entry values |
| ip6trie | IPv6 blocklists |
| ip6tset | IPv6 with per-entry values |
| dnset | Domain blocklists with wildcards |
| generic | Standard DNS records (A, MX, TXT, AAAA) |
When using -c, rbldnsd watches the config file for changes (requires auto_reload: true, which is default).
No restart needed:
- Add a zone → Loaded automatically
- Remove a zone → Unloaded automatically
- Update zone files → Reloaded automatically
- Change settings → Applied to new queries
[Unit]
Description=rbldnsd
After=network.target
[Service]
Type=simple
User=rbldnsd
ExecStart=/usr/local/bin/rbldnsd -c /etc/rbldnsd/rbldnsd.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.targetsudo systemctl enable rbldnsd
sudo systemctl start rbldnsd
sudo systemctl reload rbldnsd # Reload zones- Memory: All zones loaded at startup
- CPU: Concurrent queries handled with goroutines
- Network: UDP only (no TCP)
- Speed: O(1) ACL matching, efficient trie lookups
Same:
- Zone file format (100% compatible)
- All 7 dataset types
- DNS query responses
Different:
- Config is YAML (not
$SOA/$NSdirectives in zone files) - Dynamic config reloading (watches file)
- IPv6 AAAA record support
- Metrics (Prometheus/OpenTelemetry)
- UDP only (no TCP DNS)
- No DNSSEC
- No rate limiting (use firewall/load balancer)
- Bind address change requires restart
- With
-c: Logs error and exits. No zones loaded. - Without
-c(CLI mode): Not applicable.
- Logs error, keeps running with previous config. Next change to file will be retried.
- Partial updates are applied: removed zones are removed, valid new zones are loaded, invalid zones are skipped.
- With
-c: Zone is skipped, logs warning, other zones continue loading. Server starts even if all zones fail. - Without
-c(CLI mode): Logs error and exits. Server will not start without valid zones.
- Zone is skipped, old copy stays in memory. Next file change will be retried automatically.
- Other zones reload normally. No impact on running queries.
- Same behavior as zone files: skip on error, keep running.
MIT - See LICENSE file
For security issues, see SECURITY.md