
RPDNS is a high-performance caching reverse DNS proxy designed to reduce load on authoritative DNS servers and protect against denial-of-service attacks.
[Clients] → [RPDNS] → [Authoritative Servers]
RPDNS sits between DNS clients and authoritative servers, providing:
While RPDNS can forward queries to recursive servers, it does not perform recursion itself. It's optimized for scenarios where you need to protect and accelerate access to authoritative DNS servers.
ANY query handling - Responds with synthesized HINFO records per RFC 8482# Clone the repository
git clone https://github.com/jedisct1/rpdns.git
cd rpdns
# Build the binary
go build -o rpdns .
# Or install directly
go install github.com/jedisct1/rpdns@latest
# Basic usage with custom upstream servers
sudo rpdns -upstream 1.1.1.1:53,8.8.8.8:53
# Production setup with tuning
sudo rpdns \
-upstream 10.0.0.1:53,10.0.0.2:53 \
-listen :53 \
-maxclients 5000 \
-cachesize 2097152 \
-memsize 4096
⚠️ Important: Ensure your system's file descriptor limit is set to at least maxclients * 2:
# Check current limit
ulimit -n
# Increase limit (temporary)
ulimit -n 10000
# For permanent changes, edit /etc/security/limits.conf
| Option | Type | Default | Description |
|---|---|---|---|
-upstream | string | 8.8.8.8:53,8.8.4.4:53 | Comma-delimited list of upstream DNS servers |
-listen | string | :53 | Address to listen on (TCP and UDP) |
-cachesize | int | 1048576 | Number of DNS responses to cache |
-memsize | uint | 2048 | Maximum memory usage in MB |
-maxclients | uint | 1000 | Maximum simultaneous client connections |
-maxfailures | uint | 100 | Failed queries before marking server offline |
-maxrtt | float | 0.25 | Maximum mean RTT (seconds) before marking server dead |
-minlabels | int | 2 | Minimum domain labels required |
-local-rrs | string | Path to local DNS records file | |
-debug | bool | false | Enable debug logging |
RPDNS can serve local DNS records without forwarding to upstream servers. This is useful for:
Create a local records file:
# /etc/rpdns/local.zone
# Format: <name> <ttl> IN <type> <value>
# A records
app.internal.example.com. 86400 IN A 10.0.1.100
db.internal.example.com. 86400 IN A 10.0.1.101
# CNAME records
www.internal.example.com. 86400 IN CNAME app.internal.example.com.
# MX records
internal.example.com. 86400 IN MX 10 mail.internal.example.com.
Then start RPDNS with:
rpdns -local-rrs /etc/rpdns/local.zone
The cache size should be based on your query patterns:
# For small deployments (< 1000 queries/sec)
-cachesize 524288 # 512K entries
-memsize 1024 # 1GB RAM
# For medium deployments (1000-10000 queries/sec)
-cachesize 2097152 # 2M entries
-memsize 4096 # 4GB RAM
# For large deployments (> 10000 queries/sec)
-cachesize 8388608 # 8M entries
-memsize 16384 # 16GB RAM
-maxrtt settingCreate /etc/systemd/system/rpdns.service:
[Unit]
Description=RPDNS Caching DNS Proxy
After=network.target
[Service]
Type=simple
User=rpdns
Group=rpdns
ExecStart=/usr/local/bin/rpdns \
-upstream 10.0.0.1:53,10.0.0.2:53 \
-listen :53 \
-maxclients 5000 \
-cachesize 2097152 \
-memsize 4096
Restart=always
RestartSec=5
LimitNOFILE=20000
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable rpdns
sudo systemctl start rpdns
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o rpdns .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/rpdns /usr/local/bin/
EXPOSE 53/tcp 53/udp
ENTRYPOINT ["rpdns"]
Run with:
docker run -d \
--name rpdns \
-p 53:53/tcp \
-p 53:53/udp \
rpdns:latest \
-upstream 1.1.1.1:53,8.8.8.8:53
RPDNS logs important events:
Example log output:
2024/01/15 10:23:45 Configured upstream servers: [10.0.0.1:53 10.0.0.2:53]
2024/01/15 10:23:45 Probing [10.0.0.1:53] ... working, rtt=2.145ms
2024/01/15 10:23:45 Probing [10.0.0.2:53] ... working, rtt=1.832ms
2024/01/15 10:23:45 Live upstream servers: [10.0.0.1:53 10.0.0.2:53]
Ready
Contributions are welcome! Please feel free to submit a Pull Request.
RPDNS is released under the ISC License. See LICENSE for details.
71 commits
Go
100.0%

RPDNS is a high-performance caching reverse DNS proxy designed to reduce load on authoritative DNS servers and protect against denial-of-service attacks.
[Clients] → [RPDNS] → [Authoritative Servers]
RPDNS sits between DNS clients and authoritative servers, providing:
While RPDNS can forward queries to recursive servers, it does not perform recursion itself. It's optimized for scenarios where you need to protect and accelerate access to authoritative DNS servers.
ANY query handling - Responds with synthesized HINFO records per RFC 8482# Clone the repository
git clone https://github.com/jedisct1/rpdns.git
cd rpdns
# Build the binary
go build -o rpdns .
# Or install directly
go install github.com/jedisct1/rpdns@latest
# Basic usage with custom upstream servers
sudo rpdns -upstream 1.1.1.1:53,8.8.8.8:53
# Production setup with tuning
sudo rpdns \
-upstream 10.0.0.1:53,10.0.0.2:53 \
-listen :53 \
-maxclients 5000 \
-cachesize 2097152 \
-memsize 4096
⚠️ Important: Ensure your system's file descriptor limit is set to at least maxclients * 2:
# Check current limit
ulimit -n
# Increase limit (temporary)
ulimit -n 10000
# For permanent changes, edit /etc/security/limits.conf
| Option | Type | Default | Description |
|---|---|---|---|
-upstream | string | 8.8.8.8:53,8.8.4.4:53 | Comma-delimited list of upstream DNS servers |
-listen | string | :53 | Address to listen on (TCP and UDP) |
-cachesize | int | 1048576 | Number of DNS responses to cache |
-memsize | uint | 2048 | Maximum memory usage in MB |
-maxclients | uint | 1000 | Maximum simultaneous client connections |
-maxfailures | uint | 100 | Failed queries before marking server offline |
-maxrtt | float | 0.25 | Maximum mean RTT (seconds) before marking server dead |
-minlabels | int | 2 | Minimum domain labels required |
-local-rrs | string | Path to local DNS records file | |
-debug | bool | false | Enable debug logging |
RPDNS can serve local DNS records without forwarding to upstream servers. This is useful for:
Create a local records file:
# /etc/rpdns/local.zone
# Format: <name> <ttl> IN <type> <value>
# A records
app.internal.example.com. 86400 IN A 10.0.1.100
db.internal.example.com. 86400 IN A 10.0.1.101
# CNAME records
www.internal.example.com. 86400 IN CNAME app.internal.example.com.
# MX records
internal.example.com. 86400 IN MX 10 mail.internal.example.com.
Then start RPDNS with:
rpdns -local-rrs /etc/rpdns/local.zone
The cache size should be based on your query patterns:
# For small deployments (< 1000 queries/sec)
-cachesize 524288 # 512K entries
-memsize 1024 # 1GB RAM
# For medium deployments (1000-10000 queries/sec)
-cachesize 2097152 # 2M entries
-memsize 4096 # 4GB RAM
# For large deployments (> 10000 queries/sec)
-cachesize 8388608 # 8M entries
-memsize 16384 # 16GB RAM
-maxrtt settingCreate /etc/systemd/system/rpdns.service:
[Unit]
Description=RPDNS Caching DNS Proxy
After=network.target
[Service]
Type=simple
User=rpdns
Group=rpdns
ExecStart=/usr/local/bin/rpdns \
-upstream 10.0.0.1:53,10.0.0.2:53 \
-listen :53 \
-maxclients 5000 \
-cachesize 2097152 \
-memsize 4096
Restart=always
RestartSec=5
LimitNOFILE=20000
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable rpdns
sudo systemctl start rpdns
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o rpdns .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/rpdns /usr/local/bin/
EXPOSE 53/tcp 53/udp
ENTRYPOINT ["rpdns"]
Run with:
docker run -d \
--name rpdns \
-p 53:53/tcp \
-p 53:53/udp \
rpdns:latest \
-upstream 1.1.1.1:53,8.8.8.8:53
RPDNS logs important events:
Example log output:
2024/01/15 10:23:45 Configured upstream servers: [10.0.0.1:53 10.0.0.2:53]
2024/01/15 10:23:45 Probing [10.0.0.1:53] ... working, rtt=2.145ms
2024/01/15 10:23:45 Probing [10.0.0.2:53] ... working, rtt=1.832ms
2024/01/15 10:23:45 Live upstream servers: [10.0.0.1:53 10.0.0.2:53]
Ready
Contributions are welcome! Please feel free to submit a Pull Request.
RPDNS is released under the ISC License. See LICENSE for details.
71 commits
Go
100.0%