https://github.com/redlemonbe/runbound
High-performance DNS server in Rust · drop-in Unbound replacement · AF/XDP fast path · REST API · DoT/DoH · static binaries for x86_64 & ARM
https://github.com/redlemonbe/runbound
af-xdp dns dns-server dnssec doh dot ebpf rust unbound xdp
Last synced: 9 days ago
JSON representation
High-performance DNS server in Rust · drop-in Unbound replacement · AF/XDP fast path · REST API · DoT/DoH · static binaries for x86_64 & ARM
- Host: GitHub
- URL: https://github.com/redlemonbe/runbound
- Owner: redlemonbe
- License: agpl-3.0
- Created: 2026-05-16T15:31:39.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-06-13T08:12:52.000Z (10 days ago)
- Last Synced: 2026-06-13T08:13:45.193Z (10 days ago)
- Topics: af-xdp, dns, dns-server, dnssec, doh, dot, ebpf, rust, unbound, xdp
- Language: Rust
- Homepage: https://www.runasm.com
- Size: 563 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Threat model: THREAT_MODEL.md
- Audit: audit.toml
- Security: SECURITY.md
- Cla: CLA.md
Awesome Lists containing this project
README
# Runbound
## ASM-accelerated, Unbound-compatible DNS server — live REST API + XDP fast path
**Unbound-compatible DNS server — REST API, XDP kernel-bypass, no restart ever.**
[](https://github.com/redlemonbe/Runbound/actions/workflows/ci.yml) [](LICENSE) [](COMMERCIAL_LICENSE.md)
[](https://github.com/redlemonbe/Runbound/releases/latest)
[](docs/audit.md) [](https://github.com/sponsors/redlemonbe)
> ⚠️ **Status: Experimental** — Runbound is under active development and has not yet undergone external human security audit. Not yet recommended for production deployments handling sensitive traffic. See [METHODOLOGY.md](METHODOLOGY.md) for the development approach.
Most existing `unbound.conf` files work as-is. Non-standard or exotic directives are ignored gracefully — see [Unbound compatibility](docs/unbound-migration.md). Runbound adds a live REST API, AF_XDP kernel-bypass, and a browser dashboard on top.
---
## What you get
| | BIND9 | Unbound | Runbound |
|---|:---:|:---:|:---:|
| Drop-in Unbound config | ❌ | ✅ | ✅ |
| UDP / TCP / DoT / DoH | ✅ | ✅ | ✅ |
| Add / block domains live | ⚠️ | ❌ restart | ✅ API |
| Block-list feed subscriptions | ⚠️ | ❌ manual | ✅ API |
| Real-time stats + Prometheus | ✅ statistics channel (XML/JSON) | ❌ | ✅ |
| Master/slave replication | ✅ | ❌ | ✅ built-in*¹ |
| Automatic TLS (Let's Encrypt) | ❌ | ❌ | ✅ ACME |
| Anycast deployment (built-in BGP announcer) | ❌ | ❌ | ✅ v0.19.0 |
| AF/XDP kernel-bypass fast path | ❌ | ❌ | ✅ |
| XDP ICMP echo responder (rate-limited) | ❌ | ❌ | ✅ |
| Embedded browser dashboard | ❌ | ❌ | ✅ no nginx needed |
| Linear scaling (no lock contention) | ❌ | ❌ | ✅ |
| Static binary, no dependencies | ❌ | ❌ | ✅ musl |
| Split-horizon DNS (per-subnet answers) | ❌ | ⚠️ views | ✅ v0.9.63 |
| RBAC (read/dns/operator/admin roles) | ❌ | ❌ | ✅ v0.9.62 |
| Webhook notifications (Slack/Discord/ntfy) | ❌ | ❌ | ✅ v0.9.58 |
| Multi-user API with zone isolation | ❌ | ❌ | ✅ v0.9.51 |
| White-label UI branding | ❌ | ❌ | ✅ v0.9.61 |
| Hot backup / restore | ❌ | ❌ | ✅ API |
*¹ Runbound ships both REST API-driven replication and standard AXFR/IXFR zone transfers (RFC 5936, v0.9.13+). AXFR requires explicit ACL configuration — see [docs/configuration.md](docs/configuration.md).
---
## Install
### Requirements
- Linux x86\_64 or arm64
- systemd
- Port 53 available (stop `unbound`, `bind9`, `dnsmasq` or `systemd-resolved` first if running)
- `curl` or `wget`
- Root access (`sudo`)
### One-line install
```bash
curl -fsSL https://raw.githubusercontent.com/redlemonbe/Runbound/main/install.sh | sudo bash
```
That's it. The script:
1. Downloads the latest binary for your architecture
2. Creates a `runbound` system user
3. Writes a default config to `/etc/runbound/runbound.conf`
4. Generates a random API key in `/etc/runbound/env`
5. Installs and starts the systemd service
See **[docs/INSTALL.md](docs/INSTALL.md)** for every option (`--uninstall`, `--purge`, `--help`), integrity verification (SHA256 + minisign), file locations and troubleshooting.
At the end you'll see:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Version: runbound
API key: a1b2c3d4... ← save this
Config: /etc/runbound/runbound.conf
Logs: journalctl -u runbound -f
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
**Save the API key** — you'll need it to use the dashboard and the REST API.
### Verify it works
```bash
# DNS is responding
dig @127.0.0.1 google.com
# Service status
sudo systemctl status runbound
# API is up
curl -s http://127.0.0.1:8080/api/stats \
-H "Authorization: Bearer YOUR_API_KEY" | python3 -m json.tool
```
### Stop conflicting services first (if needed)
If port 53 is already taken:
```bash
# Check what's using port 53
sudo ss -tlnp | grep :53
# Common culprits
sudo systemctl stop unbound # Unbound
sudo systemctl stop bind9 # BIND9
sudo systemctl stop dnsmasq # dnsmasq
sudo systemctl disable systemd-resolved && sudo systemctl stop systemd-resolved # systemd-resolved
```
Then re-run the install command.
### Uninstall
```bash
# Remove Runbound, keep your config and data
curl -fsSL https://raw.githubusercontent.com/redlemonbe/Runbound/main/install.sh | sudo bash -s -- --uninstall
# Remove everything: config, data, and the runbound user/group
curl -fsSL https://raw.githubusercontent.com/redlemonbe/Runbound/main/install.sh | sudo bash -s -- --purge
```
`--uninstall` keeps your config and data in `/etc/runbound` and `/var/lib/runbound`; `--purge` deletes them (and the `runbound` system user/group) as well.
---
## Dashboard (Web UI)
Runbound embeds the dashboard — no nginx needed. Enable it in your config:
```
server:
ui-enabled: yes
ui-port: 8091
```
Restart the service, then open `https://YOUR_SERVER_IP:8091`.
On first access your browser will warn about the self-signed certificate. Download the Runbound CA at `https://YOUR_SERVER_IP:8091/webui/ca.crt` and install it once — no more warnings on any device on your network.
Enter your API key and click **Connect**. Full setup guide: [docs/web-ui.md](docs/web-ui.md).
---
## Manage DNS without touching a file
```bash
TOKEN="your-api-key"
# Add a local DNS entry — live, no restart
curl -s -X POST http://localhost:8080/api/dns \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"nas.home.","type":"A","value":"192.168.1.10","ttl":300}'
# Block a domain
curl -s -X POST http://localhost:8080/api/blacklist \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain":"ads.example.com"}'
# Subscribe to a block-list feed (auto-refreshed)
curl -s -X POST http://localhost:8080/api/feeds \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"urlhaus","url":"https://urlhaus.abuse.ch/downloads/hostfile/"}'
```
---
## Performance
Benchmarked under a documented, reproducible methodology — **the truth is the receiver
NIC hardware counters** (`tx_packets`), warm cache, governor pinned, flow-control off,
never the generator's self-report. Full per-run reports: [docs/benchmark/](docs/benchmark/).
Rig (2026-06-13): AMD Threadripper PRO 5995WX receiver, dual Xeon E5-2690 v2 generator
(dnsmark), direct 10 GbE DACs (Intel X710/i40e + X510/ixgbe).
| Runbound v0.18.1 | Served (receiver NIC) | Receiver CPU | Limited by |
|---|---|---|---|
| `xdp: yes` — **dual-link** (X510 + X710) | **~20.28 M qps** | ~24 % | the two 10 G links — **server not saturated** |
| `xdp: yes` — single link (X710) | ~10.1 M qps | ~11 % | 10 G link (response direction) |
| `xdp: no` — kernel slow path (X710) | ~3.71 M qps | ~19 % | kernel-UDP RX + generator |
In no run did Runbound reach its own CPU ceiling (≤24 %); the limit is always the link, the
NIC RX path, or the generator. Same-rig kernel-UDP reference resolvers: **unbound 1.22.0
~2.09 M**, **BIND 9.20.23 ~1.84 M** — Runbound's slow path ~2×, its fast path ~5–6×, at
lower CPU and lower latency. ~20.28 M qps ≈ **1.75 trillion queries/day** on a single node,
the order of a large public resolver's average load. Latency, the generator-bound
dual-X710 run, and full context: [docs/benchmark/INDEX.md](docs/benchmark/INDEX.md).
The fast path is **self-configuring**: AF_XDP ring sizes are derived from the NIC
hardware, huge pages are self-provisioned, and NIC queues scale to the CPU
automatically (kept at the driver default on bus-bound Xeon v2 + X520). The
architecture targets linear scaling — `SO_REUSEPORT`, `ArcSwap` lock-free config,
per-core affinity, and a single-lookup ASM hot path (CRC32c + SIMD).
## AF/XDP Fast Path
An eBPF XDP program attaches to the NIC at startup. UDP/53 packets for local zones and cache hits are answered in user space at driver level — zero syscalls on the hot path. All other queries pass through to the normal resolver via `XDP_PASS`.
Negative answers (`NODATA` / `NXDOMAIN`) are cached on the fast path too (RFC 2308). AF_XDP ring sizes, huge pages, and NIC queue counts are **configured automatically** at startup — see [docs/xdp.md](docs/xdp.md).
```bash
# Verify XDP is active
journalctl -u runbound | grep XDP
```
Disable without editing config: `RUNBOUND_DISABLE_XDP=1` — useful if the host becomes unreachable after an XDP attach. Details: [docs/xdp.md](docs/xdp.md).
---
## Documentation
Full index: **[docs/index.md](docs/index.md)**
Quick links: [Quick Start](docs/quick-start.md) · [Configuration](docs/configuration.md) · [REST API](docs/api.md) · [XDP](docs/xdp.md) · [Internals](docs/internals.md) · [Systemd](docs/systemd.md) · [Security Audit](docs/security-audit/SECURITY-AUDIT.md) · [Building & Verifying](docs/BUILD.md) · [Security Policy](SECURITY.md) · [Threat Model](THREAT_MODEL.md)
---
## Contributing
CI (`.github/workflows/ci.yml`) runs on every push to `main` and every pull request — build, clippy and tests must all be green:
```bash
cargo build --release # xdp is in the default feature set
cargo clippy --all-targets -- -D warnings # must be warning-free
cargo test # all tests must pass
```
Pull requests welcome. By submitting a PR you agree to the [CLA](CLA.md).
---
*Not affiliated with the NLnet Labs Unbound project.*
---
## Support the project
[](https://github.com/sponsors/redlemonbe)
**Bitcoin** — `3FP8hkkiu4kwCD1PDFgAv2oq1ZTyXwy3yy`
**Ethereum** — `0xB5eEAf89edA4204Aa9305B068b37A93439cBb680`
---
## License
AGPL v3 — see [LICENSE](LICENSE). Commercial license available for organizations that need to deploy without AGPL obligations: [COMMERCIAL_LICENSE.md](COMMERCIAL_LICENSE.md).