https://github.com/haukened/rr-dns
A small, lightning fast, local DNS caching resolver with Ad-Blocking. Written in Go.
https://github.com/haukened/rr-dns
clean-architecture dns domain-driven-design go golang lightweight solid-principles
Last synced: 3 days ago
JSON representation
A small, lightning fast, local DNS caching resolver with Ad-Blocking. Written in Go.
- Host: GitHub
- URL: https://github.com/haukened/rr-dns
- Owner: haukened
- License: mit
- Created: 2025-07-24T18:38:53.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2025-08-18T17:54:24.000Z (about 2 months ago)
- Last Synced: 2025-08-18T18:27:38.883Z (about 2 months ago)
- Topics: clean-architecture, dns, domain-driven-design, go, golang, lightweight, solid-principles
- Language: Go
- Homepage:
- Size: 635 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](docs/arc42.md)
[](https://go.dev/dl/)
[](LICENSE)
[](https://github.com/haukened/rr-dns/issues)
[](https://app.codecov.io/gh/haukened/rr-dns)
[](https://www.codefactor.io/repository/github/haukened/rr-dns)
[](https://pkg.go.dev/github.com/haukened/rr-dns)[](https://github.com/haukened/rr-dns/actions/workflows/ci_release.yaml)
[](https://github.com/haukened/rr-dns/actions/workflows/ci_security.yaml)# RR-DNS
A small, lightning fast, local DNS caching resolver with Ad-Blocking. Written in Go. RR is a [double entendre](https://en.wikipedia.org/wiki/Double_entendre) for "Rapid Resolver" (what it does) and "Resource Record" (the core object of DNS servers).**RR-DNS** (Rapid Resolver DNS) is a lightweight, fast, and modern DNS server written in Go. It is designed to be simple to operate, easy to extend, and highly testable β ideal for home networks, containers, embedded environments, and security-conscious setups.
> **Repo**: https://github.com/haukened/rr-dns
---
## π Purpose
RR-DNS exists to provide a minimal but robust DNS server that:
- Responds quickly and correctly to DNS queries
- Is small and efficient enough to run anywhere
- Follows strict architectural principles (CLEAN, SOLID)
- Can be easily extended to include features like blocklists, query logging, or a web admin interfaceWe are not trying to be BIND or Unbound. This is DNS done right β but simple.
---
## I don't wanna read, i just wanna run it!
> Great, docker is a fast way to do that!
### Environment Variables
| Variable Name | Purpose | Type | Default |
| :-- | :-- | :-- | :-- |
| DNS_CACHE_SIZE | cache entries capacity | Integer, >= 1 | 1000 |
| DNS_DISABLE_CACHE | disable DNS response caching | Boolean | false |
| DNS_ENV | runtime environment | `dev\|prod` | prod |
| DNS_LOG_LEVEL | log verbosity | `debug\|info\|warn\|error` | info |
| DNS_PORT | UDP listening port | Integer, 1-65534 | 8053 [^1] |
| DNS_ZONE_DIR | directory for zone files | String (path) | /zones/ [^2] |
| DNS_SERVERS | upstream DNS servers (ip:port) | List, space or comma-separated [^3] | 1.1.1.1:53, 1.0.0.1:53 |
| DNS_MAX_RECURSION | max in-zone alias chase depth | Integer, >= 1 | 8 |[^1]: In docker containers, default port is set to 8053 to prevent privileged port use.
[^2]: In docker containers, the default zone directory is changed from `/etc/rr-dns/zones/` to `/zones/` because we use distroless containers `/etc` isn't a guaranteed path, and `/zones/` is pragmatic for mount paths.
[^3]: `DNS_SERVERS` accepts multiple values separated by spaces or commas, for example: `1.1.1.1:53, 1.0.0.1:53`.### Authoritative and Recursive DNS Modes
rr-dns can operate in two modes:#### Authoritative DNS Server:
For any zones you define (using standard zone files), rr-dns acts as an authoritative DNS server. This means it will answer queries for those domains directly, using the records you provide.
#### Caching Recursive Resolver:
For domains not covered by your zone files, rr-dns automatically acts as a recursive resolver. It will query upstream DNS servers, cache the results, and return answers to clients.
>Note:
>You are not required to define any zones. If you do not provide zone files, rr-dns will function purely as a recursive caching resolver, forwarding and caching queries for all domains.This approach allows you to use rr-dns as a flexible DNS solutionβeither as an authoritative server, a recursive resolver, or both, depending on your configuration.
### Example Compose File
```yaml
version: "3.9"services:
dns:
container_name: rr-dns
image: ghcr.io/haukened/rr-dns:latest
environment:
- DNS_ENV=prod
- DNS_LOG_LEVEL=info
- DNS_PORT=8053
- DNS_ZONE_DIR=/zones
- DNS_CACHE_SIZE=1000
- DNS_DISABLE_CACHE=false
- DNS_MAX_RECURSION=8
- DNS_SERVERS=1.1.1.1:53,1.0.0.1:53
volumes:
- ./zones:/zones:ro
ports:
- "8053:8053/udp" # map host 8053 -> container 8053 (UDP)
# If you want host port 53, ensure it's free and Docker runs with sufficient privileges:
# - "53:8053/udp"
restart: unless-stopped
```---
## π Why Another DNS Server?
The DNS ecosystem is full of powerful resolvers β from BIND to Unbound, dnsmasq to CoreDNS β each built for different environments and complexity levels. But many of them:
- Try to solve *all* DNS use cases (authoritative, recursive, DHCP integration, plugin systems)
- Come with large configuration surfaces or legacy constraints
- Aren't optimized for simplicity, testability, or container-native workflowsWe built **RR-DNS** because we wanted something different:
- A resolver focused on **local caching, speed, and blocking**
- An implementation that is **cleanly architected**, **easy to reason about**, and **fun to work on**
- A system that fits naturally into **modern deployment environments** like Docker, k8s sidecars, or embedded devices
- A Go-based project where features like **ad-blocking**, **web admin**, and **logging** can evolve modularly over timeIn short: **RR-DNS fills the gap** between full-stack DNS suites and toy resolvers β with a maintainable, developer-friendly design.
---## π οΈ Architecture
RR-DNS is built with **CLEAN architecture** at its core:
```
cmd/rrdnsd β CLI entrypoint
docs/ β Project documentation (Arc42, design notes)
internal/
dns/
domain/ β Core types like Question, DNSResponse, ResourceRecord
infra/
config/ β Config loading via env or CLI
log/ β Structured logging with zap
wire/ β DNS wire format codec (RFC 1035)
upstream/ β Upstream DNS resolver with caching
dnscache/ β In-memory DNS response caching
zone/ β Static zone file loading (JSON/YAML/TOML)
blocklist/ β Ad/tracker blocking infrastructure
repo/ β Repository layer for data access
service/ β Query resolution, orchestration logic
pkg/ β Shared library code (if needed)
```### Guiding Principles
- [x] **CLEAN architecture**: clear boundaries between domain, service, and infra
- [x] **SOLID principles**: small interfaces, testable logic, dependency inversion
- [x] **Testability first**: domain and service layers are fully unit-testable with 100% coverage
- [x] **Go idioms**, not Go monoliths: no unnecessary abstractions, only meaningful ones
- [x] **RFC compliance**: Full DNS wire format implementation per RFC 1035For a detailed architectural breakdown, see the [Arc42 documentation](docs/arc42.md).
---
## π¦ Current Features
RR-DNS currently supports:
- [x] **DNS Wire Format Codec**: Complete RFC 1035 implementation with compression support
- [x] **Upstream DNS Resolution**: Configurable upstream resolvers (Google, Cloudflare, custom)
- [x] **Response Caching**: In-memory DNS response caching with TTL management
- [x] **Static Zone Support**: Load zones from JSON/YAML/TOML files
- [x] **Structured Logging**: Production-ready logging with zap (dev/prod modes)
- [x] **Configuration Management**: Environment variables and CLI argument support
- [x] **Comprehensive Testing**: 100% test coverage on core infrastructure
- [x] **Error Handling**: Robust error handling for malformed packets and edge cases
- [x] **UDP Server**: DNS query server implementation
- [x] **Query Resolution Service**: Orchestration of upstream, cache, and zone lookups
- [x] **CNAME Alias Resolution**: RFC 1034 Β§3.6.2 compliant chain expansion (loop & depth safeguards, partial-chain NOERROR policy, SERVFAIL on loop/depth)
- [X] **Docker Deployment**: Support deploying in docker containers.
- [ ] **Ad/Tracker Blocking**: Blocklist subscription and filtering
- [ ] **Snap Packaging**: Published on snapcraft.io
- [ ] **Apt Packaging**: Apt packages for Debian/Ubuntu/Derivates
- [ ] **DNS over HTTPS**: DoH support for extra privacy.
- [ ] **DNS over TLS**: Secure DNS queries with DoT support
- [ ] **REST API**: Admin endpoints for health checks and metrics
- [ ] **Web Admin UI**: Modern web interface for configuration and monitoring---
## πΊοΈ Roadmap
| Version | Features | Status |
|-----------|-------------------------------------------|--------------|
| **v0.1** | Core DNS Resolution features | β Complete |
| **v0.2** | Docker support | β Complete |
| **v0.3** | Blocklist subscriptions | π§ In Progress |
| **v0.4** | Snap package support | π Planned |
| **v0.5** | Apt package support | π Planned |
| **v1.0** | Stable release (no UI) | π Planned |
| **v1.1** | TLS/DoH support | π Planned |
| **v1.2** | REST API for config/query/logs | π Planned |
| **v1.3** | Web Admin UI | π Planned |
| **v2.0** | Stable release with Admin UI | π Planned |---
## οΏ½ Testing & Quality
RR-DNS prioritizes code quality and reliability:
- **100% Test Coverage**: All infrastructure components have comprehensive unit tests
- **Error Path Testing**: Extensive testing of edge cases and malformed data handling
- **RFC Compliance Testing**: DNS wire format validated against RFC 1035 specifications
- **Structured Testing**: Table-driven tests with clear scenarios and validations
- **Continuous Integration**: Automated testing on all commits```bash
# Run tests with coverage
go test -cover ./...# Generate detailed coverage report
go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out
```---
## π€ Contributing
We welcome contributions! Please:
- Follow Go formatting conventions
- Respect CLEAN boundaries β infra never calls domain, tests should focus on services and domain first
- Add unit tests for all logic (aim for 100% coverage)
- Log using structured logs: `log.Info(map[string]any{"queryID": id, "name": name}, "Received query")`
- Test error paths thoroughly, including edge cases and malformed data### Development Setup
```bash
# Clone the repository
git clone https://github.com/haukened/rr-dns.git
cd rr-dns# Run tests
go test ./...# Run tests with coverage
go test -cover ./...# Format code
go fmt ./...
```---
## π License
- [MIT](LICENSE)
---
## πββοΈ Maintainers
- [@haukened](https://github.com/haukened)
---
## π± Inspiration
Weβre inspired by the spirit of projects like
- [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html)
- [CoreDNS](https://coredns.io/)
- [Pi-hole](https://pi-hole.net/)
- [Technitium](https://technitium.com/dns/)but RR-DNS is built from the ground up with modern, maintainable Go in mind.