MonaDNS is a transparent DNS-based traffic steering tool and resolver. It intercepts DNS queries for configured domains, responds with "Fake IPs", and automatically manages Linux networking rules (nftables and iproute2) to steer traffic destined to these fake IPs into specific network interfaces and routing tables.
This functionality provides a lightweight, transparent proxying mechanism akin to the fake-ip features found in tools like Clash or Surge, designed to run directly on Linux routers or gateways.
- Fake IP DNS Resolution: Intercepts DNS requests for specified domains and returns dynamically allocated IPv4 and IPv6 addresses from a configured subnet.
- Multi-Interface Traffic Steering: Automatically maintains
nftableschains andip rulesto mark and route traffic destined to the allocated Fake IPs through multiple designated interfaces and routing tables. - IP-based Steering: Beyond DNS-based steering, MonaDNS can also steer traffic based on destination IP subnets.
- Automated NAT/Masquerade: Optionally applies SNAT or Masquerade to the steered traffic per interface.
- TCP MSS Clamping: Built-in support for MSS clamping to prevent fragmentation issues on tunnel interfaces (e.g., WireGuard).
- Upstream DNS Support: Resolves non-intercepted domains via standard upstream resolvers including Quad9, Cloudflare, Google, or custom servers via UDP, DNS-over-TLS (DoT), or DNS-over-HTTPS (DoH).
- Domain & IP Lists Management: Supports adding individual domains/subnets or syncing lists from external sources.
- GeoSite & GeoIP Support: Integrated support for V2Ray-compatible
geosite.datandgeoip.datfiles for bulk domain and IP management. - Virtual Geo Protocols: Reference geo categories directly in your lists using
geosite://<category>orgeoip://<category>(e.g.,geosite://google,geoip://cn). - Integrated Web Interface: A modern web UI built with Svelte 5 and TailwindCSS for managing configuration, domains, IPs, and lists.
- REST API: Fully documented OpenAPI (Swagger) endpoints for programmatic management.
- Prometheus Metrics: Built-in Prometheus exporter for monitoring DNS query metrics and traffic statistics.
- Direct Router Installation: Install MonaDNS directly on a Linux-based router (e.g., OpenWrt) to provide transparent steering for all connected clients.
- Sidecar Gateway: Run MonaDNS on a separate device (like a Raspberry Pi) in your network. Set the Fake IP subnets (
198.18.0.0/15andfd32:bfcc:fba0:1337::/64by default) as static routes on your main router pointing to the MonaDNS device. Clients using MonaDNS as their DNS server will have their traffic automatically routed through it for configured domains. - VPN Server Smart Routing: Deploy MonaDNS on a VPN server (WireGuard, OpenVPN). By pushing MonaDNS as the DNS server to VPN clients, you can steer specific client traffic through different exit nodes or local interfaces based on the requested domain or destination IP.
Important
If you deploy MonaDNS as a sidecar gateway, make sure the return path is symmetric. If the sidecar and clients are on the same L2 network or subnet, Linux may send reply packets directly back to the client instead of through the main router. That bypasses the router's stateful path, so conntrack/NAT on the router may not see the return traffic for established flows. The result is asymmetric routing: short connections may appear to work, but long-lived TCP sessions can stall, be reset, or stop working after the router's state expires. In practice, ensure that both directions of the flow traverse the same gateway, or use SNAT/masquerade/policy routing so replies are forced back through the router that owns the connection state.
The project is structured into two main components:
A high-performance Rust application built with hickory-dns (for DNS handling), axum (for the HTTP API), and nftables / rtnetlink (for Linux network management).
- Serves as the primary DNS server.
- Manages multiple egress interfaces using
fwmarkand custom routing tables. - Embeds and serves the pre-built Svelte frontend.
- Stores state and rules in an SQLite database.
- Modifies Linux networking state (requires appropriate capabilities, e.g.,
CAP_NET_ADMINorroot).
A Single Page Application (SPA) built with Svelte 5 and Vite. It interacts with the backend REST API to allow users to manage the DNS rules, IP rules, subscriptions, and core network configurations.
-
Linux OS: MonaDNS heavily relies on Linux-specific networking APIs (
nftablesandrtnetlink). -
Root Privileges / Capabilities: Running the backend requires root access or
CAP_NET_ADMINto manipulate network interfaces, routing tables, and firewall rules. -
Routing Tables: For each interface you want to steer traffic through, you must ensure a default route exists in the corresponding routing table. For example, if you use interface
wg0with table100:sudo ip route add default dev wg0 table 100
If you use
wg-quick, you can setTable = 100in your Wireguard configuration to add the route automatically. -
System Settings:
sudo sysctl -w net.ipv4.conf.all.rp_filter=0 # Disable reverse path filtering sudo sysctl -w net.ipv4.ip_forward=1 # Enable forwarding
MonaDNS can be configured via environment variables and a TOML configuration file.
| Variable | Description | Default |
|---|---|---|
MONADNS_CONFIG_PATH |
Path to the TOML configuration file | /opt/monadns/config.toml |
MONADNS_DB_PATH |
Path to the SQLite database | /opt/monadns/db.sqlite |
MONADNS_DNS_BIND |
Address and port to bind the DNS server | [::]:5553 |
MONADNS_HTTP_BIND |
Address and port to bind the HTTP API / UI | [::]:8080 |
MONADNS_METRICS_BIND |
Optional address to bind the Prometheus exporter | (Disabled) |
MONADNS_API_PASSWORD |
Optional password for configuration (uses X-Api-Key header) |
(Disabled, no password) |
The configuration file defines the network interfaces, subnets for Fake IPs, and upstream DNS resolvers. This configuration is fully editable via the Web UI.
default_interface = "wg0"
ipv4_subnet = "198.18.0.0/15"
ipv6_subnet = "fd32:bfcc:fba0:1337::/64"
export_enabled = false
[[interfaces]]
name = "wg0"
fwmark = 1
table_id = 100
tcp_mss_clamp = 1280
ipv4_snat = "10.10.10.4"
[[interfaces]]
name = "eth0"
fwmark = 2
table_id = 101
[upstream_resolver]
type = "Quad9Https"Quad9Https,CloudflareHttps,GoogleHttpsCustom:[upstream_resolver] type = "Custom" nameservers = [ { addr = "1.1.1.1", protocol = "Plain" }, { addr = "8.8.8.8", protocol = "Tls", tls_dns_name = "dns.google" } ]
MonaDNS supports the widely used geosite.dat and geoip.dat binary formats for efficient management of large domain and IP lists.
- Add Geo Sources: In the "Geo Sources" tab, add URLs to your
.datfiles (e.g., from v2fly/domain-list-community or v2fly/geoip). - Use Categories: Once synced, you can use categories from these files in your Domain or IP lists using the following formats:
geosite://google- All domains in the Google category.geosite://cn- All domains associated with China.geoip://ir- All IP subnets for Iran.
- Efficiency: MonaDNS decodes these files once and stores them in its database. Matching against these lists is highly optimized using a domain specificity fast-path.
Note
Regex rules are not supported yet.
When running, the application exposes a Swagger UI for exploring and testing the REST API. You can access it by navigating to:
http://<MONADNS_HTTP_BIND>/swagger
MonaDNS can be run as a Docker container. Since it needs to manage the host's networking stack (nftables, routing tables), it requires elevated privileges and usually runs in host network mode.
The easiest way to run MonaDNS is using Docker Compose.
To build the Docker image locally:
docker build -t monadns .The Dockerfile uses a multi-stage build to compile both the Svelte frontend and the Rust backend, resulting in a lean final image based on Debian.
cd frontend
pnpm install
pnpm devcd resolver
cargo run(Note: Running the backend typically requires root privileges due to its interaction with nftables)
Note
Make sure to build the frontend first, so it gets embedded into the resolver binary.
cd frontend
pnpm buildcd resolver
cargo build