RouteDNS is a composable DNS stub resolver, proxy and router written in Go. It enables building flexible DNS processing pipelines with support for all modern DNS protocols, query routing, caching, blocklists, DNSSEC validation, Lua scripting, and 30+ other pipeline components — all configured via TOML.
graph LR
C[Clients] --> L
subgraph RouteDNS
L[Listeners<br/>DNS · DoT · DoH<br/>DoQ · DTLS · ODoH] --> P[Routers / Groups / Modifiers<br/>Router · Cache · Blocklist<br/>Rate Limiter · Load Balancer<br/>DNSSEC Validator · Lua Script<br/>...30+ types]
P --> R[Resolvers<br/>DNS · DoT · DoH<br/>DoQ · DTLS · ODoH]
end
R --> U[Upstream DNS]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C,U ext
class L listen
class P proc
class R resolve
Listeners receive queries over any supported protocol. Routers, groups and modifiers form the processing pipeline — routing, filtering, caching, and transforming queries and responses. Resolvers forward queries upstream. Every component implements the same Resolver interface, so they can be composed freely.
Protocols
Query Processing
Routing
Resilience & Performance
Deployment
setns or xsocket (no CAP_SYS_ADMIN required); a compatible fd-server is built in (routedns fd-server)Requires Go 1.24+:
go install github.com/folbricht/routedns/cmd/routedns@latest
Or build from source:
git clone https://github.com/folbricht/routedns.git
cd routedns/cmd/routedns && go install
Pre-built binaries for Linux (amd64, arm64, armv6/armv7 for Raspberry Pi, mips/mipsle softfloat for OpenWrt routers), macOS (amd64, arm64), FreeBSD, and Windows are available on the GitHub Releases page.
Releases include deb, rpm, apk, and Arch Linux packages for amd64, arm64, and armv7. They install the binary to /usr/bin/routedns, a default configuration to /etc/routedns/config.toml, and a systemd service:
sudo dpkg -i routedns_<version>_linux_amd64.deb # Debian/Ubuntu/Raspberry Pi OS
sudo rpm -i routedns_<version>_linux_amd64.rpm # Fedora/RHEL
sudo systemctl enable --now routedns
brew install folbricht/tap/routedns
Multi-arch images (amd64, arm64, armv7) are published on every release to Docker Hub and GitHub Container Registry, tagged latest and with the release version:
docker run -d --rm --network host folbricht/routedns
or from GHCR:
docker run -d --rm --network host ghcr.io/folbricht/routedns
With a custom config:
docker run -d --rm --network host -v /path/to/config.toml:/config.toml folbricht/routedns
This minimal config forwards all local DNS queries encrypted via DNS-over-TLS to Cloudflare, with caching. Set your system's nameserver to 127.0.0.1 (e.g. in /etc/resolv.conf).
[resolvers.cloudflare-dot]
address = "1.1.1.1:853"
protocol = "dot"
[groups.cloudflare-cached]
type = "cache"
resolvers = ["cloudflare-dot"]
backend = {type = "memory"}
[listeners.local-udp]
address = "127.0.0.1:53"
protocol = "udp"
resolver = "cloudflare-cached"
[listeners.local-tcp]
address = "127.0.0.1:53"
protocol = "tcp"
resolver = "cloudflare-cached"
Save as config.toml and run:
routedns config.toml
To check a configuration without serving queries, use --check. It builds everything the config defines and exits non-zero if anything failed, without binding any listener address:
routedns --check config.toml
An example systemd service file is provided here.
Route internal queries to company DNS servers while sending everything else securely to Cloudflare via DoH. Company servers are grouped with fail-rotate for resilience.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> RT[Router]
RT -->|*.mycompany.com| CO[Fail-Rotate<br/>Company DNS A/B]
RT -->|everything else| CF[Cloudflare DoH]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class RT proc
class CO,CF resolve
Configuration: use-case-2.toml
Single out devices by IP address and apply a custom blocklist plus a filtered upstream resolver, while giving all other devices unfiltered access.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> RT[Router]
RT -->|source 192.168.1.123| BL[Blocklist] --> CB[CleanBrowsing DoT]
RT -->|default| CF[Cloudflare DoT]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class RT,BL proc
class CB,CF resolve
Configuration: family-browsing.toml
Protect the whole network with multi-layer blocklists (query names, response names, response IPs), caching, and TTL clamping. Blocklists auto-refresh daily from remote HTTP sources.
graph LR
C[Clients] --> L[Listener<br/>UDP/TCP :53]
L --> CA[Cache]
CA --> TTL[TTL Modifier]
TTL --> BQ[Query Blocklist]
BQ --> BR[Response Name<br/>Blocklist]
BR --> BI[Response IP<br/>Blocklist]
BI --> CF[Cloudflare DoT<br/>Fail-Rotate]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class CA,TTL,BQ,BR,BI proc
class CF resolve
Configuration: use-case-6.toml
Validate DNSSEC signatures on all responses using built-in root trust anchors. Queries with invalid signatures are rejected.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> DV[DNSSEC Validator<br/>IANA Trust Anchor]
DV --> CF[Cloudflare DoT]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class DV proc
class CF resolve
[resolvers.cloudflare-dot]
address = "1.1.1.1:853"
protocol = "dot"
[groups.dnssec-validated]
type = "dnssec-validator"
resolvers = ["cloudflare-dot"]
[listeners.local-udp]
address = "127.0.0.1:53"
protocol = "udp"
resolver = "dnssec-validated"
[listeners.local-tcp]
address = "127.0.0.1:53"
protocol = "tcp"
resolver = "dnssec-validated"
Go
99.8%
RouteDNS is a composable DNS stub resolver, proxy and router written in Go. It enables building flexible DNS processing pipelines with support for all modern DNS protocols, query routing, caching, blocklists, DNSSEC validation, Lua scripting, and 30+ other pipeline components — all configured via TOML.
graph LR
C[Clients] --> L
subgraph RouteDNS
L[Listeners<br/>DNS · DoT · DoH<br/>DoQ · DTLS · ODoH] --> P[Routers / Groups / Modifiers<br/>Router · Cache · Blocklist<br/>Rate Limiter · Load Balancer<br/>DNSSEC Validator · Lua Script<br/>...30+ types]
P --> R[Resolvers<br/>DNS · DoT · DoH<br/>DoQ · DTLS · ODoH]
end
R --> U[Upstream DNS]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C,U ext
class L listen
class P proc
class R resolve
Listeners receive queries over any supported protocol. Routers, groups and modifiers form the processing pipeline — routing, filtering, caching, and transforming queries and responses. Resolvers forward queries upstream. Every component implements the same Resolver interface, so they can be composed freely.
Protocols
Query Processing
Routing
Resilience & Performance
Deployment
setns or xsocket (no CAP_SYS_ADMIN required); a compatible fd-server is built in (routedns fd-server)Requires Go 1.24+:
go install github.com/folbricht/routedns/cmd/routedns@latest
Or build from source:
git clone https://github.com/folbricht/routedns.git
cd routedns/cmd/routedns && go install
Pre-built binaries for Linux (amd64, arm64, armv6/armv7 for Raspberry Pi, mips/mipsle softfloat for OpenWrt routers), macOS (amd64, arm64), FreeBSD, and Windows are available on the GitHub Releases page.
Releases include deb, rpm, apk, and Arch Linux packages for amd64, arm64, and armv7. They install the binary to /usr/bin/routedns, a default configuration to /etc/routedns/config.toml, and a systemd service:
sudo dpkg -i routedns_<version>_linux_amd64.deb # Debian/Ubuntu/Raspberry Pi OS
sudo rpm -i routedns_<version>_linux_amd64.rpm # Fedora/RHEL
sudo systemctl enable --now routedns
brew install folbricht/tap/routedns
Multi-arch images (amd64, arm64, armv7) are published on every release to Docker Hub and GitHub Container Registry, tagged latest and with the release version:
docker run -d --rm --network host folbricht/routedns
or from GHCR:
docker run -d --rm --network host ghcr.io/folbricht/routedns
With a custom config:
docker run -d --rm --network host -v /path/to/config.toml:/config.toml folbricht/routedns
This minimal config forwards all local DNS queries encrypted via DNS-over-TLS to Cloudflare, with caching. Set your system's nameserver to 127.0.0.1 (e.g. in /etc/resolv.conf).
[resolvers.cloudflare-dot]
address = "1.1.1.1:853"
protocol = "dot"
[groups.cloudflare-cached]
type = "cache"
resolvers = ["cloudflare-dot"]
backend = {type = "memory"}
[listeners.local-udp]
address = "127.0.0.1:53"
protocol = "udp"
resolver = "cloudflare-cached"
[listeners.local-tcp]
address = "127.0.0.1:53"
protocol = "tcp"
resolver = "cloudflare-cached"
Save as config.toml and run:
routedns config.toml
To check a configuration without serving queries, use --check. It builds everything the config defines and exits non-zero if anything failed, without binding any listener address:
routedns --check config.toml
An example systemd service file is provided here.
Route internal queries to company DNS servers while sending everything else securely to Cloudflare via DoH. Company servers are grouped with fail-rotate for resilience.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> RT[Router]
RT -->|*.mycompany.com| CO[Fail-Rotate<br/>Company DNS A/B]
RT -->|everything else| CF[Cloudflare DoH]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class RT proc
class CO,CF resolve
Configuration: use-case-2.toml
Single out devices by IP address and apply a custom blocklist plus a filtered upstream resolver, while giving all other devices unfiltered access.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> RT[Router]
RT -->|source 192.168.1.123| BL[Blocklist] --> CB[CleanBrowsing DoT]
RT -->|default| CF[Cloudflare DoT]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class RT,BL proc
class CB,CF resolve
Configuration: family-browsing.toml
Protect the whole network with multi-layer blocklists (query names, response names, response IPs), caching, and TTL clamping. Blocklists auto-refresh daily from remote HTTP sources.
graph LR
C[Clients] --> L[Listener<br/>UDP/TCP :53]
L --> CA[Cache]
CA --> TTL[TTL Modifier]
TTL --> BQ[Query Blocklist]
BQ --> BR[Response Name<br/>Blocklist]
BR --> BI[Response IP<br/>Blocklist]
BI --> CF[Cloudflare DoT<br/>Fail-Rotate]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class CA,TTL,BQ,BR,BI proc
class CF resolve
Configuration: use-case-6.toml
Validate DNSSEC signatures on all responses using built-in root trust anchors. Queries with invalid signatures are rejected.
graph LR
C[Client] --> L[Listener<br/>UDP/TCP :53]
L --> DV[DNSSEC Validator<br/>IANA Trust Anchor]
DV --> CF[Cloudflare DoT]
classDef ext fill:#e2e8f0,stroke:#64748b,color:#1e293b
classDef listen fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
classDef proc fill:#fef3c7,stroke:#f59e0b,color:#78350f
classDef resolve fill:#d1fae5,stroke:#10b981,color:#064e3b
class C ext
class L listen
class DV proc
class CF resolve
[resolvers.cloudflare-dot]
address = "1.1.1.1:853"
protocol = "dot"
[groups.dnssec-validated]
type = "dnssec-validator"
resolvers = ["cloudflare-dot"]
[listeners.local-udp]
address = "127.0.0.1:53"
protocol = "udp"
resolver = "dnssec-validated"
[listeners.local-tcp]
address = "127.0.0.1:53"
protocol = "tcp"
resolver = "dnssec-validated"
Go
99.8%