HydraDNS

Official registry Search and web

Manage a self-hosted DNS firewall: block domains, create policies, read query logs and metrics.

Install

Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).

OCI package ghcr.io/hydradns/hydra-cli:0.1.0
Claude Code
claude mcp add hydradns -e 'HYDRA_API_URL=http://host.docker.internal:8080' -e 'HYDRA_TOKEN=<your hydra_token>' -e 'MCP_ROLE=admin' -- docker run -i --rm ghcr.io/hydradns/hydra-cli:0.1.0 mcp
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "hydradns": {
            "command": "docker",
            "args": [
                "run",
                "-i",
                "--rm",
                "ghcr.io/hydradns/hydra-cli:0.1.0",
                "mcp"
            ],
            "env": {
                "HYDRA_API_URL": "http://host.docker.internal:8080",
                "HYDRA_TOKEN": "<your hydra_token>",
                "MCP_ROLE": "admin"
            }
        }
    }
}

Environment variables

  • HYDRA_API_URL required Base URL of the HydraDNS controlplane API. Inside a container, 'localhost' means the container itself, not the host running the controlplane. On Docker Desktop (Mac/Windows) the default below resolves automatically. On plain Docker Engine (Linux) either run the container with --add-host=host.docker.internal:host-gateway, or use --network host with http://localhost:8080 instead. Full instructions: docs/mcp.md in the repo, 'Running it -> As the container image'.
  • HYDRA_TOKEN required secret Bearer token for the HydraDNS API, obtained via `hydra login` or the setup wizard. Grants whatever access the token's account has; MCP_ROLE narrows what the MCP tool layer will call, not what the token itself can do against the API directly.
  • MCP_ROLE Permission scope for this MCP session: admin (default, every tool), operator (every mutating tool except toggle_engine), or reporter (read-only tools only). Any unrecognized value safe-defaults to reporter.

Documentation

From the project's README on GitHub, refreshed weekly. Links open on GitHub.

Contents
  1. HydraDNS
  2. HydraDNS vs Pi-hole
  3. Why HydraDNS
  4. Quick Start
  5. Architecture
  6. Dashboard
  7. CLI
  8. MCP Server (AI Integration)
  9. Development
  10. Project Structure
  11. Deployment
  12. Documentation
  13. Configuration
  14. Contributing & Community
  15. License

HydraDNS

A self-hosted DNS firewall in Go. Blocks ads, malware and trackers network-wide, like Pi-hole. API-first control plane, a real dashboard, a CLI, and a built-in Model Context Protocol server so Claude or any MCP agent can manage policy for you.

Screenshots and product site at hydradns.app (a marketing site with static screenshots, not an interactive demo)


HydraDNS vs Pi-hole

HydraDNSPi-hole
CoreGo, gRPC control/data plane splitC (pihole-FTL), embedded web server
Setupdocker compose up -d pulls the published core + dashboard images once a release exists, and builds them from source before thatinstaller script or Docker
AI management (MCP)✅ built in (hydra mcp, 14 tools: block/unblock, policies, logs, metrics, anomaly explain)❌ third-party community bridges only
DoH bypass blocking✅ curated DoH bootstrap endpoints blocked at query time⚠️ Firefox canary domain only; add third-party lists for the rest
Policiespriority-based allow/block/redirect via API or UI; CLI covers block/unblock/list/delete (no generic create yet)groups, regex, and per-client rules (more mature today)
Maturityyoung, pre-1.0, moving fast10+ years, huge community, built-in DHCP

Choose Pi-hole today for battle-tested stability, regex rules, and community support. Choose HydraDNS for a hackable Go codebase, an API-first control plane, and AI-agent management over MCP that self-hosted alternatives only get through third-party bridges.

Honest limits: like every DNS-layer filter, HydraDNS cannot stop a client that hardcodes a DoH server by raw IP. Pair it with a firewall rule on 443/853 to close that path. For the fuller list (no TLS on the dashboard/gRPC yet, no DNSSEC, regex/wildcard policies not enforced, and more), see docs/limitations.md.


Why HydraDNS

I spent 15 months building an enterprise next-generation firewall in Go, and kept wishing the self-hosted version of that tooling existed: something a home or small-office network could run, with a real API and a control plane you could drive from a script or an AI agent instead of a settings page. HydraDNS is that tool. The built-in Model Context Protocol server comes from the same work I do upstream as a CNCF Jaeger contributor, where I build MCP tooling for observability.

It is pre-1.0 and moving fast. If it is useful to you, a star and an issue both help.

Built by Roshan Singh (@lopster568).


Quick Start

# Clone (single repo, no submodules)
git clone https://github.com/hydradns/hydradns.git
cd hydradns

# Start everything
docker compose up -d

# Verify DNS is working
dig @localhost example.com

# Check the dashboard
open http://localhost:3000

Port 53 already in use? On Linux or WSL2, systemd-resolved may already hold port 53. Free it before starting: sudo systemctl disable --now systemd-resolved (then set a DNS server in /etc/resolv.conf), or edit the port mapping in docker-compose.yml. See docs/pi-deployment.md for details.

That's it. DNS filtering is active. Give this machine a static IP and point your router's DNS to it. See docs/pi-deployment.md for static IP setup on Linux, macOS, and Windows plus per-router DNS instructions.


Architecture

                    +-----------+
                    |  Browser  |
                    +-----+-----+
                          |
                    +-----v-----+
                    |  Dashboard |  :3000  (Next.js)
                    +-----+-----+
                          |
                    +-----v-----+
         +--------->  Control   |  :8080  (Go + Gin REST API)
         |          |   Plane   |
         |          +-----+-----+
         |                |  gRPC :50051
         |          +-----v-----+
  CLI/MCP|          |   Data    |  :53    (DNS UDP/TCP)
  hydra  +--------->   Plane    |
                    +-----+-----+
                          |
               +----------+----------+
               |          |          |
          +----v---+ +----v---+ +----v---+
          |Blocklist| | Policy | |Upstream|
          | Engine  | | Engine | |Resolvers|
          +--------+ +--------+ +--------+
ServiceDirectoryTechPort
Core (Control + Data Plane)apps/coreGo 1.26, Gin, gRPC, GORM/SQLite8080, 53
Dashboardapps/uiNext.js 16, React 19, TypeScript, Tailwind3000
Scannerapps/scannerGo, network detection—
CLI + MCPapps/cliGo, Cobra, JSON-RPC 2.0—
DNS Query Pipeline

Every DNS query is scored by a heuristic threat detector (domain entropy, DGA-pattern, length, subdomain depth); scoring is non-blocking and only tags the query log, with no auto-block yet. The query then goes through this pipeline with early exit:

  1. DoH bootstrap interception: known DoH provider bootstrap hostnames get NXDOMAIN so browsers fall back to system DNS
  2. Blocklist check: in-memory membership test; if the domain is blocked, respond per BLOCK_RESPONSE (default: A/AAAA → 0.0.0.0/::; nxdomain and refused also available)
  3. Policy evaluation: Bloom filter for O(1) negative lookup, then exact match. Highest priority wins
  4. Response cache: TTL-respecting LRU for allowed queries; blocked/redirect responses are never cached
  5. Upstream forward: pool-per-resolver with failover (1.5s per-attempt timeout, 2 retries)

Dashboard

The web dashboard at localhost:3000 lets you:

  • View real-time query statistics (total, blocked, allowed, block rate)
  • Toggle the DNS engine on/off
  • Manage blocklist sources (add/remove/view domain counts)
  • Create and delete DNS policies (block, allow, redirect)
  • Search and filter query logs


CLI

The hydra CLI wraps the control plane API for terminal-based management.

# Build the CLI
cd apps/cli && go build -o hydra .

# First boot: create the admin account and store the API token
hydra setup

# Check status
hydra status

# Block a domain
hydra block ads.example.com

# View query logs
hydra logs

# Manage blocklists
hydra blocklists
hydra blocklists add --id steven-black --name "StevenBlack" \
  --url "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"

# Manage policies
hydra policies
hydra policies delete my-policy-id

# Engine control
hydra engine enable
hydra engine disable

# View metrics
hydra metrics

Set HYDRA_API_URL to point at a remote instance (default: http://localhost:8080).


MCP Server (AI Integration)

HydraDNS includes a built-in Model Context Protocol server, letting AI assistants manage your DNS firewall conversationally.

# Start MCP server (JSON-RPC 2.0 over stdio)
hydra mcp
Claude Code Setup

Add to your Claude Code MCP config:

{
  "mcpServers": {
    "hydradns": {
      "command": "/path/to/hydra",
      "args": ["mcp"],
      "env": {
        "HYDRA_API_URL": "http://localhost:8080"
      }
    }
  }
}
Available MCP Tools
ToolDescription
get_statusEngine status and query statistics
toggle_engineEnable or disable DNS engine
block_domainBlock a domain (creates a policy)
unblock_domainRemove a block policy
list_policiesList all DNS policies
list_blocklistsList blocklist sources
get_query_logsRecent DNS query logs
get_metricsLatency percentiles and performance grade
create_policyCreate an allow/block/redirect policy
delete_policyDelete a policy by ID
bulk_unblockRemove block policies for many domains at once
get_weekly_summaryWeek-over-week query and block summary
explain_anomalyExplain a block-rate or volume anomaly
compare_to_last_monthCompare current stats against the previous month

Example conversation: Say "Block all social media domains" and Claude calls block_domain for each domain.


Development

Prerequisites
  • Go 1.26+
  • Node.js 20+
  • Docker & Docker Compose
Working on a Service

Each service lives under apps/ in this repo. Work inside its directory:

cd apps/core
make build        # Compile controlplane & dataplane
make test         # Run tests with coverage
make fmt          # Format code
make vet          # Vet code
make lint         # golangci-lint

cd apps/ui
npm run dev       # Dev server on :3000
npm run build     # Production build

cd apps/cli
go build -o hydra .  # Build CLI binary
Full Stack Commands (from root)
make setup        # One-time local setup (.env)
make start        # docker compose up -d
make stop         # docker compose down
make update       # git pull --ff-only
make logs         # Tail all logs
make build-core   # Rebuild core service
make restart-core # Rebuild + restart core

Project Structure

hydradns/
├── apps/
│   ├── core/           # Go DNS engine + API
│   │   ├── cmd/        #   controlplane + dataplane binaries
│   │   ├── internal/   #   blocklist, dnsengine, policy, storage
│   │   ├── configs/    #   config.yaml + policies.json
│   │   └── proto/      #   gRPC protobuf definitions
│   ├── ui/             # Next.js dashboard
│   ├── scanner/        # Network detection worker
│   └── cli/            # CLI + MCP server
│       ├── cmd/        #   Cobra commands
│       ├── api/        #   HTTP client for control plane
│       └── mcp/        #   MCP JSON-RPC server
├── docker-compose.yml  # Full stack orchestration
├── Makefile            # Convenience commands
└── scripts/            # Setup scripts

Deployment

docker compose up -d

Core runs as a combined container (controlplane + dataplane) with:

  • SQLite database persisted in a Docker volume
  • Health check on /health endpoint
  • Automatic blocklist fetching on startup
Raspberry Pi / VPS
curl -fsSL https://raw.githubusercontent.com/hydradns/hydradns/main/scripts/install.sh | bash

Then give the device a static IP and point your router's DNS server to it. Full walkthrough (static IP on Linux/macOS/Windows, router config): docs/pi-deployment.md.


Documentation

  • Deployment Guide: install on a Raspberry Pi or any always-on machine; static IP setup (Linux, macOS, Windows), per-router DNS configuration, troubleshooting
  • Hardware Guide: choosing a device to run HydraDNS on
  • Known Limitations: what's not implemented yet, with impact and workarounds
  • MCP Server Guide: the 14 tools, roles, and client configuration for the built-in MCP server
  • Release Runbook: what release.yml publishes and how tags are cut
  • Public Demo: hosting a read-only, public instance of the dashboard

Configuration

Env VariableDefaultDescription
HYDRA_CONFIG/app/configs/config.yamlPath to config file
HYDRA_DB/app/data/hydradns.dbSQLite database path
HYDRA_POLICIES/app/configs/policies.jsonPolicy file path
CORS_ORIGINShttp://localhost:3000,http://127.0.0.1:3000 (compose sets http://localhost:3000)Comma-separated allowed CORS origins
CORS_ALLOW_SAME_HOSTtrueAlso allow the dashboard when it is opened by the box's own IP address (Origin host equals the API host and is an IP or localhost). Named hosts need a CORS_ORIGINS entry
TRUSTED_PROXIES(empty)Comma-separated CIDRs/IPs allowed to set X-Forwarded-For for client-IP purposes (login/setup throttle, audit log). Empty means no proxy is trusted, so the real socket address is always used
HYDRA_API_URLhttp://localhost:8080CLI/MCP API target
HYDRA_TOKEN(none; falls back to ~/.hydra/token)CLI/MCP bearer token
MCP_ROLEadminScopes MCP tool access: admin, operator (no toggle_engine), or reporter (read-only)
HYDRA_DEMO_MODEfalseTurns this instance into a public, read-only demo (rejects all mutations, seeds a fixed-password demo user and synthetic data, masks client IPs). See demo/README.md (not for a normal install)
HYDRA_ANONYMIZE_CLIENT_IPSfalseHash (HMAC-SHA256) client IPs before writing them to the query log instead of storing them as-is. This is pseudonymisation, not anonymisation, and it's off by default
HYDRA_ANON_SECRET(generated per-install)HMAC key used only when HYDRA_ANONYMIZE_CLIENT_IPS is enabled
BLOCK_RESPONSEzeroAnswer for blocked domains: zero (A 0.0.0.0), nxdomain, or refused
BLOCKLIST_UPDATE_INTERVAL6hHow often blocklist sources are re-downloaded from their URL
BLOCKLIST_POLL_INTERVAL5sHow often the dataplane checks the DB for blocklist changes (add, toggle, delete, finished download) and rebuilds the in-memory blocklist; 0 disables
QUERY_LOG_RETENTION_DAYS7Delete query logs older than N days; 0 disables
QUERY_LOG_MAX_ROWS1000000Keep at most N newest query-log rows; 0 disables
QUERY_LOG_CLEANUP_INTERVAL1hHow often the query-log retention loop above runs
NEXT_PUBLIC_API_URLhttp://localhost:8080Dashboard API URL override (build time). By default the dashboard uses the page's own hostname on port 8080
NEXT_PUBLIC_SHOW_BYPASS_PANELunset (hidden)Build-time flag to show the DoH-bypass-attempts panel on the dashboard

Contributing & Community

Contributions are welcome, HydraDNS is pre-1.0 and there is a lot to build.

License

Apache-2.0

Frequently asked questions

What is HydraDNS?

Manage a self-hosted DNS firewall: block domains, create policies, read query logs and metrics.

How do I install HydraDNS?

It runs on your machine. In Claude Code: claude mcp add hydradns -e 'HYDRA_API_URL=http://host.docker.internal:8080' -e 'HYDRA_TOKEN=<your hydra_token>' -e 'MCP_ROLE=admin' -- docker run -i --rm ghcr.io/hydradns/hydra-cli:0.1.0 mcp. For other clients, paste the JSON configuration from this page into their MCP settings.

Does HydraDNS need an API key?

Yes. It expects HYDRA_API_URL, HYDRA_TOKEN, which you set in its configuration.

Is HydraDNS open source?

Yes. Its code is on GitHub under the Apache-2.0 license.

Who maintains HydraDNS?

It is published by hydradns. AI.tn lists it but does not maintain or endorse it.

From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.