DoT Block is a high-performance, caching, and filtering DNS-over-TLS (DoT) server written in Go. It acts as a secure DNS forwarder, encrypting your DNS queries and protecting you from advertisers, trackers, and malicious domains.
- DNS-over-TLS: Encrypts your DNS queries to keep them private.
- DNS-over-HTTPS (DoH) endpoint: An HTTP DoH handler is available at
/dns-querythat accepts GET requests with a?dns=<base64url>query parameter or POST requests with the raw DNS wire format in the request body. Responses are returned with content typeapplication/dns-message. - Regular DNS: Supports standard UDP and TCP DNS queries (optional, disabled by default).
- Ad & Tracker Blocking: Blocks a wide range of unwanted domains using customizable blocklists.
- High Performance: Built with Go for speed and efficiency.
- Intelligent Caching: Caches DNS responses to speed up subsequent lookups with configurable TTL flooring.
- Easy to Deploy: Can be run as a standalone binary or as a Docker container.
- Automatic TLS: Uses Let's Encrypt to automatically obtain and renew TLS certificates.
- Advanced Observability: Exports detailed Prometheus metrics including upstream health, failure reasons, and cache effectiveness.
- Latency-Aware Routing: Automatically prefers the fastest upstream resolvers based on real-time response latency and applies penalties to failing servers to ensure high availability.
- Hardened TLS: Uses a strict TLS configuration (TLS 1.2+) with forward-secrecy prioritized cipher suites to ensure maximum security for DoT connections.
- Distributed Tracing: Integrates with OpenTelemetry (OTel), providing end-to-end traces of DNS requests and correlating them with logs via
trace_idandspan_id. - Noise-Reduced Error Reporting: Integrates with Sentry, with intelligent filtering to avoid logging protocol-valid negative responses (like NXDOMAIN or NOTIMP) as errors.
- Proxy Protocol Support: Supports PROXY protocol for DoT connections, enabling correct client IP identification when running behind a proxy.
The easiest way to get started with DoT Block is to use Docker and Docker Compose.
- Clone this repository:
git clone https://github.com/your-username/dot-block.git cd dot-block - Update
docker-compose.ymlwith your domain name and email address. - Run the server:
docker-compose up --build -d
The server will be accessible at dot.your-domain.com.
For high-traffic environments, you may need to tune the network stack to avoid port exhaustion and packet loss. You can apply these settings directly in your docker-compose.yml using sysctls:
services:
dot-block:
# ... other configuration ...
sysctls:
- net.ipv4.ip_local_port_range=1024 65535
- net.core.rmem_max=26214400
- net.core.wmem_max=26214400net.ipv4.ip_local_port_range: Expands the ephemeral port range to allow more concurrent outgoing UDP requests.net.core.rmem_max&net.core.wmem_max: Increases the maximum OS receive and send buffer sizes for UDP to prevent packet drops during traffic spikes.
By default, DoT Block forwards queries to public resolvers. While these typically perform DNSSEC validation, you can implement local DNSSEC validation by using Unbound as your upstream resolver. This removes the need to trust a third-party provider for validation.
The easiest way to achieve this is by running Unbound in a separate container on the same Docker network.
Example docker-compose.yml snippet:
services:
unbound:
image: mvance/unbound:latest
container_name: unbound
restart: unless-stopped
# No ports exposed to the host; only accessible internally by dot-block
dot-block:
image: your-username/dot-block:latest
# ... other configuration ...
command: ["--upstreams=unbound:53"]
depends_on:
- unboundIn this configuration, dot-block handles the TLS termination, ad-blocking, and caching, while unbound performs the actual recursive resolution and DNSSEC validation.
For local development, you can run the server in "dev mode", which uses plain TCP instead of TLS.
- Clone this repository:
git clone https://github.com/your-username/dot-block.git cd dot-block - Run the server:
The DNS server (UDP/TCP) will be listening on port
go run main.go --dev-mode --http-port=8080
8053, DoT (plain TCP) on8853, and the HTTP server on port8080.
You can test the server using dig or openssl.
Regular DNS (UDP/TCP, if configured):
dig @dot.your-domain.com -p <DNS_PORT> example.com AProduction (TLS/HTTPS):
# DNS-over-TLS
dig @dot.your-domain.com -p 853 +tls example.com
# DNS-over-HTTPS
dig @dot.your-domain.com -p 443 +https example.com ANote that the bundled dig binary in MacOS doesn't support the +tls options, so use an alternative like kdig instead.
Local Development:
# Regular DNS (UDP)
dig @127.0.0.1 -p 8053 www.google.com A
# Regular DNS (TCP)
dig @127.0.0.1 -p 8053 www.google.com A +tcp
# DoT (plain TCP)
dig @127.0.0.1 -p 8853 www.google.com A +tcpopenssl s_client -connect dot.your-domain.com:853 -alpn dot -servername dot.your-domain.comThe server provides several HTTP endpoints for monitoring and management on the configured --http-port (default 80).
GET /metrics: Exports Prometheus metrics.GET /reload: Triggers an asynchronous reload of the blocklists.POST /check: Checks whether provided domains are blocked. Accepts a JSON array of strings or a newline-separated list of domains in the request body.GET /dns-queryandPOST /dns-query: DNS-over-HTTPS (DoH) endpoint.GET /dns-queryexpects adnsquery parameter containing the base64url-encoded DNS wire message.POST /dns-queryexpects the raw DNS wire format in the request body. Responses are returned with content typeapplication/dns-message.
If --metrics-auth is configured, /metrics and /reload are protected by basic authentication.
To use DoT Block on your iPhone or iPad, you can install a configuration profile directly from the server:
- Open Safari on your iOS device.
- Navigate to
https://dot.your-domain.com/.mobileconfig. - Tap Allow when prompted to download the configuration profile.
- Open the Settings app.
- Tap Profile Downloaded near the top.
- Tap Install in the top right corner and follow the prompts.
- Once installed, your device will use DoT Block for all DNS queries.
You can configure your browser to use DoT Block for DNS queries directly, without changing any system-wide settings.
Generic URL: https://dot.your-domain.com/dns-query
- Open Settings -> Privacy and security -> Security.
- Scroll down to Use secure DNS.
- Select With: Custom and enter your DoH URL:
https://dot.your-domain.com/dns-query.
- Open Settings -> Privacy & Security.
- Scroll down to DNS over HTTPS.
- Select Max Protection or Increased Protection.
- Under Choose provider, select Custom and enter your DoH URL:
https://dot.your-domain.com/dns-query.
Safari uses the system DNS settings. To use DoH in Safari, you must configure it at the OS level (see iOS Configuration or your macOS network settings).
To build the binary from source:
go build -ldflags="-w -s" -o dot-block .To run the tests:
go test ./...DoT Block can be configured using the following command-line flags:
| Flag | Description | Default |
|---|---|---|
--allowed-hosts |
List of domains used for the CertManager allow policy. | nil |
--blocklist-url |
List of URL blocklists (wildcard hostname format). | https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/hosts/pro.txt, https://raw.githubusercontent.com/rm-hull/dot-block/refs/heads/main/data/blocklist.txt |
--noise-filter-url |
URL of noise filter (CSV format: category,rcode,domain_suffix). | https://raw.githubusercontent.com/rm-hull/dot-block/refs/heads/main/data/noise-filter.txt |
--cache-ttl-floor |
Minimum TTL for cached entries (in seconds). If a response is not "freshness sensitive" (e.g. contains ocsp, crl, pki or is SOA/TXT), the cache TTL will be at least this value. |
3600s |
--dial-timeout |
Timeout for establishing TCP connections to upstream servers | 300ms |
--read-timeout |
Timeout for waiting for responses from upstream DNS servers | 300ms |
--write-timeout |
Timeout for writing upstream DNS queries | 100ms |
--cron-schedule:cache-reaper |
Cron spec for cache reaper. | 0 3 * * * (3:00am every day) |
--cron-schedule:downloader |
Cron spec for reloading blocklist. | @every 19h |
--cron-schedule:ip2location |
Cron spec for fetching IP2Location db. | 5 7 4 * * (7:05am on the 4th of every month) |
--data-dir |
Directory for persisting data (e.g. TLS certificate cache). | ./data |
--dev-mode |
Run the server in dev mode (no TLS, plain TCP). | false |
--dns-port |
The port to run regular DNS (UDP/TCP) server on. If omitted, the regular DNS server will not start. | 0 |
--dot-port |
The port to run DNS-over-TLS server on. | 853 |
--http-port |
The port to run the HTTP server on. | 80 |
--log-level |
The log level (DEBUG, INFO, WARN, ERROR). | INFO |
--metrics-auth |
Credentials for basic auth on /metrics (format: user:pass). |
"" |
--require-proxy-protocol |
Require PROXY protocol header for DoT connections. | false |
--trusted-proxies |
Comma-separated list of trusted proxy IP addresses or CIDR ranges. | nil |
--enable-ecs |
Enable EDNS0 Client Subnet (ECS) steering. This allows the server to send the client's network prefix to upstream resolvers for location-aware responses. | false |
-v, --version |
Print the version of the server and exit. | nil |
--upstreams |
Upstream DNS resolvers to forward queries to. (Port 53 is assumed if omitted) | 8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1, 9.9.9.9, 149.112.112.112 |
| Variable | Description | Required |
|---|---|---|
ACME_EMAIL |
Email address used for Let's Encrypt registration. | Yes (in production) |
CLOUDFLARE_API_TOKEN |
Cloudflare API token for DNS-01 challenge (CertManager). | Yes (in production) |
DEV_MODE |
Set to true to enable development mode (disables TLS). |
No |
IP2LOCATION_TOKEN |
IP2Location token for downloading geoIP locations. | Yes |
SENTRY_DSN |
DSN for Sentry error reporting. | No |
OTEL_EXPORTER_OTLP_ENDPOINT |
OpenTelemetry OTLP gRPC endpoint (e.g. localhost:4317). |
No |
OTEL_SAMPLING_RATIO |
Ratio of traces to sample (0.0 to 1.0). Defaults to 0.01 (1%). |
No |
A dashboard.json is available for importing into Grafana:
Contributions are welcome! Please feel free to open an issue or submit a pull request.
This project is licensed under the MIT License. See the LICENSE.md file for details.
