nx9-dns-server is a high-performance, fully RFC-compliant authoritative DNS server with built-in DNSSEC, async I/O via Tokio, SQLite storage, Docker support, and a full suite of diagnostic tools โ designed for production reliability from day one.
Production-grade, RFC-compliant, memory-safe DNS server built with Rust and Tokio.
Tokio async runtime for thousands of concurrent UDP/TCP queries with minimal latency and CPU overhead.
Automatic RRSIG signing, DNSKEY distribution, DS record support. Full RFC 4033โ4035 compliance, key loaded from env.
Simple, reliable file-based storage with efficient indexing, ACID transactions, and schema migration support via rusqlite.
Rust's ownership model eliminates buffer overflows, use-after-free, and null pointer bugs at compile time. Zero undefined behavior.
Multi-stage musl build targeting Alpine Linux. Pre-built Docker Compose config. Ports 53, 8080, 8081 exposed.
Service unit included. Runs as dnsuser non-root, auto-restarts on failure, integrates with journald logging.
Bundled shell scripts: dnscheck.sh, dns_dump.sh, performance_test.sh, soa-update.sh, preprocess-key.sh and more.
build-deb.sh and build-rpm.sh scripts for native Debian/Ubuntu .deb and CentOS/RHEL .rpm packages.
A modular Rust architecture emphasizing performance, security, and maintainability. Core DNS engine + storage layer + optional DNSSEC + future Web UI / API.
Binds UDP + TCP :53. Parses packets, validates queries, manages DnsCache (thread-safe HashMap with TTL, cleaned every 5 minutes), orchestrates the full pipeline. Graceful SIGINT shutdown.
SQLite database with dns_records table. Efficient compound primary key (domain, record_type, value). WITHOUT ROWID for performance. ACID transactions.
Loads RSASHA256 keypair from DNSSEC_KEY_FILE. Generates RRSIG per record, emits DNSKEY automatically. Preprocessing via preprocess-key.sh. DS records for parent delegation.
All config via environment variables. Structured logging with env_logger. DnsError enum via thiserror for clean, typed error handling throughout.
Planned Rocket/Axum backend. React/Svelte frontend. JWT auth. RBAC roles. Full REST API at /api/v1/zones. Webhooks for record changes.
| Crate / Tool | Role | Detail | Status |
|---|---|---|---|
| tokio | Async Runtime | UDP/TCP async handlers + periodic tasks | โ Core |
| rusqlite | Database | SQLite binding for dns_records storage | โ Core |
| log / env_logger | Logging | Structured runtime diagnostics | โ Core |
| thiserror | Error Types | Custom DnsError enum | โ Core |
| BIND dnssec-keygen | Key Generation | RSASHA256 keypair for DNSSEC | โ External |
| Rocket / Axum | Web Framework | UI and API endpoints | ๐ง Planned |
| JWT | Auth | Token-based auth + RBAC | ๐ง Planned |
| Prometheus | Metrics | METRICS_ENABLE=true exposes :9100/metrics | ๐ง Planned |
RFC 1035 and RFC 4034 compliant query handling โ from socket receive to signed response. The actual algorithm nx9-dns-server implements.
| Component | Purpose | Implementation | Detail |
|---|---|---|---|
| DnsCache | Response Cache | Thread-safe HashMap with TTL | Cleaned every 5 minutes |
| ServerConfig | Configuration | Loaded via env vars | DNS_BIND, DNS_DB_PATH, etc. |
| DnsError | Error Handling | thiserror enum | Graceful SIGINT shutdown |
| rusqlite | Record Storage | SQLite database backend | WITHOUT ROWID, compound PK |
| tokio | Async I/O | UDP/TCP handlers + tasks | Periodic cache cleanup |
| DNSSEC | Secure Signing | RSA-SHA256 + Base64 | RRSIG, DNSKEY, DS records |
Flow-Chart.png and the algorithm is documented in Algorithm & Flowchart.md at the repository root.
Prerequisites: Rust stable toolchain, SQLite dev libraries. For DNSSEC: bind9-dnsutils. For containers: Docker.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shgit clone https://github.com/thakares/nx9-dns-server.git cd nx9-dns-server cargo build --release # Binary โ target/release/dns_server
# From the SQL file at repo root sqlite3 dns.db < dns_records.sql # Or use the sample DB in conf/ cp conf/dns.db.sample dns.db
DNS_BIND="0.0.0.0:53" \ DNS_DB_PATH="./dns.db" \ DNS_FORWARDERS="8.8.8.8:53,1.1.1.1:53" \ DNS_NS_RECORDS="ns1.yourdomain.tld.,ns2.yourdomain.tld." \ ./target/release/dns_server
bash dnscheck.sh # or manually: dig @localhost yourdomain.tld A
# Build the image (multi-stage, musl target, Alpine runtime) git clone https://github.com/thakares/nx9-dns-server.git cd nx9-dns-server docker build -t nx9-dns-server:latest . # Run with all volumes and environment variables docker run -d --name nx9-dns \ -p 53:53/udp -p 53:53/tcp \ -p 8080:8080 -p 8081:8081 \ -v /path/to/dns.db:/var/nx9-dns-server/dns.db \ -v /path/to/keys:/etc/nx9-dns-server/keys \ -e DNS_BIND=0.0.0.0:53 \ -e DNS_DB_PATH=/var/nx9-dns-server/dns.db \ -e DNSSEC_KEY_FILE=/etc/nx9-dns-server/keys/Kyourdomain.tld.key \ -e DNS_FORWARDERS=8.8.8.8:53,1.1.1.1:53 \ -e WEB_UI_BIND=0.0.0.0:8080 \ -e API_BIND=0.0.0.0:8081 \ -e AUTH_SECRET=your-secure-random-string \ nx9-dns-server:latest
# docker-compose.yml (provided in repo) version: '3.8' services: dns: image: nx9-dns-server:latest container_name: nx9-dns ports: - "53:53/udp" - "53:53/tcp" - "8080:8080" - "8081:8081" volumes: - ./data/dns.db:/var/nx9-dns-server/dns.db - ./keys:/etc/nx9-dns-server/keys - ./logs:/var/log/nx9-dns-server environment: - DNS_BIND=0.0.0.0:53 - DNS_DB_PATH=/var/nx9-dns-server/dns.db - DNSSEC_KEY_FILE=/etc/nx9-dns-server/keys/Kyourdomain.tld.key - DNS_FORWARDERS=8.8.8.8:53,1.1.1.1:53 - DNS_NS_RECORDS=ns1.yourdomain.tld.,ns2.yourdomain.tld. - WEB_UI_BIND=0.0.0.0:8080 - API_BIND=0.0.0.0:8081 restart: unless-stopped # Launch: docker compose up -d --build
sudo cp target/release/dns_server /usr/local/bin/ sudo cp dns_server.service /etc/systemd/system/
[Unit] Description=NX9 DNS Server After=network.target [Service] Type=simple User=dnsuser Group=dns WorkingDirectory=/var/nx9-dns-server ExecStart=/usr/local/bin/dns_server Restart=on-failure RestartSec=5s Environment="DNS_BIND=0.0.0.0:53" Environment="DNS_DB_PATH=/var/nx9-dns-server/dns.db" Environment="DNSSEC_KEY_FILE=/var/nx9-dns-server/Kyourdomain.tld.+008+24550.key" Environment="DNS_FORWARDERS=8.8.8.8:53,1.1.1.1:53" Environment="DNS_NS_RECORDS=ns1.yourdomain.tld.,ns2.yourdomain.tld." Environment="LOG_FILE=/var/log/nx9-dns-server/server.log" Environment="AUTH_SECRET=your-secure-random-string-here" [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable --now dns-server.service sudo systemctl status dns-server.service
Build native packages using the provided scripts in the repository root.
# build-deb.sh at repo root bash build-deb.sh # Or install directly bash install.sh
# build-rpm.sh at repo root bash build-rpm.sh # Verify package rpm -qi nx9-dns-server-*.rpm
All configuration is environment-variable driven โ no config files required. Works for bare-metal, systemd, Docker, and Kubernetes alike.
| Variable | Description | Default | Example |
|---|---|---|---|
| DNS_BIND | IP:port to bind DNS server | 0.0.0.0:53 | 192.168.1.10:53 |
| DNS_DB_PATH | Path to SQLite database file | dns.db | /var/nx9-dns-server/dns.db |
| DNSSEC_KEY_FILE | Path to DNSSEC public key file | โ | /var/nx9/Kexample.+008+24550.key |
| DNS_FORWARDERS | Comma-separated upstream resolvers | โ | 8.8.8.8:53,1.1.1.1:53 |
| DNS_NS_RECORDS | Comma-separated NS records | required | ns1.example.com.,ns2.example.com. |
| DNS_CACHE_TTL | Cache TTL in seconds | 3600 | 7200 |
| WEB_UI_BIND | Web UI bind address | 127.0.0.1:8080 | 0.0.0.0:8080 |
| API_BIND | REST API bind address | 127.0.0.1:8081 | 0.0.0.0:8081 |
| AUTH_SECRET | JWT signing secret | โ | random-secure-string |
| ADMIN_PASSWORD | Initial admin password (first-run only) | โ | strong-password |
| LOG_FILE | Log file path | โ | /var/log/nx9/server.log |
| LOG_LEVEL | Logging verbosity | info | debug |
| WORKER_THREADS | Tokio worker threads | cpu count | 16 |
| DB_CACHE_SIZE | SQLite cache pages | โ | 50 |
| MAX_UDP_SIZE | Max UDP packet (useful for DNSSEC) | 4096 | 8192 |
| MAX_TCP_CLIENTS | Concurrent TCP connections | โ | 250 |
| METRICS_ENABLE | Expose Prometheus :9100/metrics | false | true |
127.0.0.1 in production unless external access is explicitly needed. Use a strong, randomly generated AUTH_SECRET.Records are stored in SQLite. Until the Web UI and API ship, manage them directly with SQL โ simple, scriptable, version-controllable.
-- dns_records.sql (at repository root) CREATE TABLE IF NOT EXISTS dns_records ( domain TEXT NOT NULL, record_type TEXT NOT NULL, value TEXT NOT NULL, ttl INTEGER DEFAULT 3600, PRIMARY KEY (domain, record_type, value) ) WITHOUT ROWID;
INSERT OR REPLACE INTO dns_records VALUES ('example.com', 'A', '203.0.113.10', 3600), ('example.com', 'AAAA','2001:db8::1', 3600), ('example.com', 'MX', '10 mail.example.com', 3600), ('example.com', 'MX', '20 backup.example.com', 3600), ('example.com', 'NS', 'ns1.example.com', 86400), ('example.com', 'NS', 'ns2.example.com', 86400), ('example.com', 'SOA', 'ns1.example.com hostmaster.example.com 2024010101 10800 3600 604800 86400', 86400), ('example.com', 'TXT', '"v=spf1 a mx ~all"', 3600), ('www.example.com','A','203.0.113.10', 3600);
| Type | Description | Example Value | DNSSEC | TTL Advice |
|---|---|---|---|---|
| A | IPv4 address | 203.0.113.10 | โ RRSIG | 3600s |
| AAAA | IPv6 address | 2001:db8::1 | โ RRSIG | 3600s |
| MX | Mail exchange (priority prefix) | 10 mail.example.com | โ RRSIG | 3600s |
| NS | Name server delegation | ns1.example.com | โ RRSIG | 86400s |
| SOA | Start of Authority (required) | ns1 hostmaster serial refresh retry expire minttl | โ RRSIG | 86400s |
| TXT | Arbitrary text (SPF, DKIM, verify) | "v=spf1 a mx ~all" | โ RRSIG | 3600s |
| CNAME | Canonical name alias | example.com | โ RRSIG | 3600s |
| PTR | Reverse DNS (rDNS) | example.com | โ RRSIG | 3600s |
| SRV | Service locator record | 0 5 443 target.example.com | โ RRSIG | 3600s |
| CAA | CA Authorization (TLS cert control) | 0 issue "letsencrypt.org" | โ RRSIG | 3600s |
| DNSKEY | Public key distribution | Auto-generated from key file | โ Chain root | 3600s |
| DS | Delegation Signer (parent zone) | Submit to registrar | โ Parent | 3600s |
-- Open the database sqlite3 /var/nx9-dns-server/dns.db -- View all records SELECT * FROM dns_records; -- Filter by domain SELECT * FROM dns_records WHERE domain = 'example.com'; -- Update a record INSERT OR REPLACE INTO dns_records VALUES ('www.example.com', 'A', '203.0.113.11', 3600); -- Delete a record DELETE FROM dns_records WHERE domain='old.example.com' AND record_type='A';
-- SOA format: -- primary hostmaster serial refresh retry expire minttl INSERT INTO dns_records VALUES ('example.com', 'SOA', 'ns1.example.com hostmaster.example.com 2024010101 10800 3600 604800 86400', 86400); -- Update SOA serial after changes sudo -u dnsuser /var/nx9-dns-server/soa-update.sh -- TTL guidelines: -- 300โ900s โ frequently changing records -- 3600s โ standard records (A, MX, TXT) -- 86400s โ stable records (NS, SOA)
DNSSEC adds a verifiable chain of trust from root to your zone. nx9-dns-server handles signing automatically once a key is configured.
Verifies DNS data genuinely originates from the stated authoritative source โ not an attacker spoofing replies.
RRSIG signatures guarantee responses haven't been tampered with or manipulated in transit.
Cryptographically proves a requested record doesn't exist, preventing NXDOMAIN spoofing attacks.
# 1. Install key generation tool sudo apt-get install bind9-dnsutils # Debian/Ubuntu sudo yum install bind-utils # CentOS/RHEL # 2. Generate 2048-bit RSASHA256 keypair dnssec-keygen -a RSASHA256 -b 2048 \ -n ZONE yourdomain.tld # Produces: Kyourdomain.tld.+008+12345.key # Kyourdomain.tld.+008+12345.private # 3. Install key and set env var sudo cp Kyourdomain.tld.+008+12345.key \ /var/nx9-dns-server/ export DNSSEC_KEY_FILE="/var/nx9-dns-server/Kyourdomain.tld.+008+12345.key" # 4. (Optional) preprocess key format sudo -u dnsuser /var/nx9-dns-server/preprocess-key.sh # 5. Restart and verify sudo systemctl restart dns-server dig @localhost yourdomain.tld A +dnssec
# Check DNSKEY is being served dig @localhost yourdomain.tld DNSKEY # Verify RRSIG on A record dig @localhost yourdomain.tld A +dnssec # Trace full DNSSEC chain dig @localhost yourdomain.tld A \ +dnssec +trace # Generate DS record โ submit to registrar dnssec-dsfromkey Kyourdomain.tld.+008+12345.key # Check signature expiry dig @localhost yourdomain.tld SOA \ +dnssec | grep RRSIG # Key rotation: generate new key, update # DNSSEC_KEY_FILE, restart, resubmit DS sudo systemctl restart dns-server
*.private key files secure and never expose them. Only the *.key (public) file is needed by the server. For high-security deployments, consider hardware security modules (HSMs).The repo ships a comprehensive set of shell scripts covering the full deployment lifecycle โ build, install, update, SOA management, and health checks.
bash deploy.shbash deploy-dns-server.shsudo -u dnsuser /var/nx9-dns-server/soa-update.shsudo -u dnsuser /var/nx9-dns-server/preprocess-key.shbash dns_wrapper.shbash start.shA full suite of diagnostic scripts ships with the repository. Real-world benchmarks from the wiki: 3,256 QPS for A records, 1,845 QPS for DNSSEC-signed responses.
Benchmarks from performance_test.sh ยท 5,000 queries on modest 2-core test server. Production hardware (4+ cores, 2GB RAM) reaches 20,000โ40,000 QPS.
bash dnscheck.sh bash dnscheck.sh 192.168.1.10 bash dnscheck.sh 192.168.1.10 example.com โ A record: 203.0.113.10 โ AAAA record: 2001:db8::1 โ MX: 10 mail.example.com. โ RRSIG validation successful โ All tests passed!
bash dns_dump.sh example.com bash dns_dump.sh example.com 192.168.1.10 bash dns_dump.sh example.com > zone_backup.txt
dig @localhost example.com A dig @localhost example.com MX dig @localhost example.com DNSKEY +dnssec dig @localhost example.com A +dnssec +trace dig @localhost example.com A +tcp
sudo journalctl -u dns-server.service sudo tail -f /var/log/nx9-dns-server/server.log docker logs -f nx9-dns # Enable debug logging sudo systemctl set-environment LOG_LEVEL=debug sudo systemctl restart dns-server
| Symptom | Common Cause | Fix |
|---|---|---|
| SERVFAIL | DB permissions, bad DNSSEC key, wrong record format | Check logs with journalctl |
| No response | Firewall blocking :53, wrong bind address | netstat -tulpn | grep 53 |
| DNSSEC fail | Key path wrong, DS not submitted to registrar | Re-run dnscheck.sh with +trace |
| DB error | Wrong file permissions, corrupt schema | sqlite3 dns.db .schema |
| API unreachable | Service not enabled, wrong bind, firewall | curl http://localhost:8081/api/v1/health |
# DNS health one-liner dig @192.168.1.10 example.com A +short \ | grep -q "^[0-9]" && echo "DNS OK" || echo "FAILED" # API health curl -s http://localhost:8081/api/v1/health \ | grep -q "ok" && echo "API OK" # Prometheus scrape config scrape_configs: - job_name: 'nx9-dns-server' scrape_interval: 15s static_configs: - targets: ['192.168.1.10:9100'] # Import Grafana dashboard # Dashboard JSON: tools/grafana/nx9-dns-dashboard.json
Full CRUD for zones and records, batch operations, JWT authentication, rate limiting, and webhook notifications โ the complete planned API surface.
# Zone management GET /api/v1/zones # List all zones POST /api/v1/zones # Create new zone GET /api/v1/zones/{zone} # Zone details PUT /api/v1/zones/{zone} # Update zone properties DELETE /api/v1/zones/{zone} # Remove zone # Record management GET /api/v1/zones/{zone}/records # List all records POST /api/v1/zones/{zone}/records # Create record GET /api/v1/zones/{zone}/records/{id} # Get record PUT /api/v1/zones/{zone}/records/{id} # Update record DELETE /api/v1/zones/{zone}/records/{id} # Delete record # Batch operations POST /api/v1/zones/{zone}/records/batch # Bulk create/update/delete # Auth: Bearer JWT token # Rate limiting: per-token, configurable
Rust with Rocket or Axum. JWT-based auth with scoped API tokens. Rate limiting and API versioning.
TypeScript with React or Svelte. Dashboard, record editor, DNSSEC key management, audit log timeline.
Local accounts + optional OAuth/LDAP. RBAC roles. 2FA. Password policies. API token management.
Core DNS engine + DNSSEC are production-ready. Web UI, REST API, and clustering are actively being designed and are open for contributors.
Core DNS and DNSSEC are solid. The Web UI, API, and User Management are wide open โ bring your Rust, TypeScript, or documentation skills.
Frontend components (React/Svelte), API integration, responsive design, DNSSEC key management UI, audit log timeline.
Rocket or Axum backend, /api/v1/zones endpoints, JWT auth, rate limiting, batch operations, webhooks.
Authentication, RBAC roles, 2FA, audit logging, password policies, API token management, optional OAuth/LDAP.
Unit tests, integration tests, automated CI pipelines, GitHub Actions, performance benchmarking.
Improve wiki pages, add examples, contribute to CONTRIBUTING.md and CODE_OF_CONDUCT.md.
# 1. Fork the repository gh repo fork thakares/nx9-dns-server # 2. Create a feature branch git checkout -b feature/your-amazing-feature # 3. Commit with clear messages git commit -m 'Add some amazing feature' # 4. Push and open a PR git push origin feature/your-amazing-feature
This project is dual-licensed. The open-source edition is released under GNU GPLv3. A commercial license (COMMERCIAL-LICENSE.md) is also available for organisations that need to embed nx9-dns-server in proprietary products.
DNSSEC_KEY_FILE is not set, the server runs unsigned โ completely valid for development, internal networks, or zones where DNSSEC isn't needed. DNSSEC is recommended for internet-facing production zones.DNS_FORWARDERS to proxy them upstream (e.g. to 8.8.8.8 or 1.1.1.1). For a recursive resolver, use Unbound or PowerDNS Recursor.sqlite3 /var/nx9-dns-server/dns.db). Use INSERT OR REPLACE INTO dns_records VALUES (...); to add/update records, and always run soa-update.sh afterward to increment the SOA serial. A Web UI and REST API are in development.cp /var/nx9-dns-server/dns.db backup/dns.db.bak. Also back up your DNSSEC *.key and *.private files, and your environment variable configuration (systemd unit or docker-compose.yml).METRICS_ENABLE=true). Keep :8080/:8081 bound to localhost unless reverse-proxied with TLS.Self-hosted ยท RFC-compliant ยท DNSSEC-secured ยท Built with ๐ฆ Rust
45 GitHub stars ยท 3 forks ยท 29 commits ยท Active development