-
Pattern-based DNS routing: Match incoming DNS requests against configurable regex patterns
-
CNAME-aware routing: Secondary pattern matching on CNAME responses for intelligent resolver selection
-
Multiple resolver support: Route to different DNS resolvers based on pattern matches
-
gRPC interface: Full DNS resolution capabilities via gRPC with runtime configuration updates
-
Prometheus metrics: Comprehensive metrics for monitoring
-
Health endpoints: Kubernetes-compatible health and readiness probes
-
Request Pattern Matching: When a DNS request arrives (via DNS or gRPC), it checks if the query name matches any configured request patterns.
-
Non-recursive Lookup to Explicit Resolver: If a pattern matches, performs a non-recursive DNS lookup to the configured "explicit resolver".
-
CNAME Pattern Matching: If the response contains a CNAME record, checks if the CNAME target matches any CNAME patterns.
-
Recursive Lookup: If a CNAME pattern matches, performs a recursive lookup to the "explicit resolver".
-
System Fallback: If no CNAME match occurs (but request pattern matched), uses the system resolver. If no request pattern matches at all, uses the system resolver.
docker run -p 5353:5353/udp -p 5353:5353/tcp -p 5354:5354 -p 8080:8080 \
ghcr.io/steigr/nameserver-switcher:latest| Flag | Description | Default |
|---|---|---|
|
Newline-delimited regex patterns for matching incoming requests |
"" |
|
Newline-delimited regex patterns for matching CNAME responses |
"" |
|
DNS server for initial non-recursive lookups |
"" |
|
DNS server for recursive lookups when CNAME matches |
"" |
|
Address to listen for DNS requests |
"0.0.0.0" |
|
Port for DNS server |
5353 |
|
Address to listen for gRPC requests |
"0.0.0.0" |
|
Port for gRPC server |
5354 |
|
Address for HTTP health/metrics server |
"0.0.0.0" |
|
Port for HTTP server |
8080 |
|
Enable debug logging |
false |
|
Log all DNS requests |
true |
|
Log all DNS responses |
true |
|
Log output format: |
text |
| Variable | Description |
|---|---|
|
Newline-delimited regex patterns for request matching |
|
Newline-delimited regex patterns for CNAME matching |
|
DNS server for non-recursive lookups |
|
DNS server for recursive CNAME lookups |
|
Address for DNS server |
|
Port for DNS server |
|
Address for gRPC server |
|
Port for gRPC server |
|
Address for HTTP server |
|
Port for HTTP server |
|
Enable debug logging ( |
|
Log DNS requests ( |
|
Log DNS responses ( |
|
Log output format: |
The nameserver-switcher provides comprehensive logging capabilities with support for text and JSON output formats.
Text format (default): Human-readable logs with millisecond-precision timestamps.
JSON format: Structured logs for log aggregation systems. Each entry includes a timemillis field with Unix timestamp in milliseconds.
When enabled (default), logs all DNS requests and responses:
Text format:
2026/01/29 14:32:15.123 [INFO] DNS request received protocol=udp type=A name=example.com. from=127.0.0.1:54321
2026/01/29 14:32:15.135 [INFO] DNS response sent name=example.com. rcode=NOERROR answer_count=1 resolver=system duration_ms=12.345JSON format:
{"timemillis":1738161135123,"time":"2026-01-29T14:32:15.123Z","level":"INFO","message":"DNS request received","fields":{"protocol":"udp","type":"A","name":"example.com.","from":"127.0.0.1:54321"}}
{"timemillis":1738161135135,"time":"2026-01-29T14:32:15.135Z","level":"INFO","message":"DNS response sent","fields":{"name":"example.com.","rcode":"NOERROR","answer_count":1,"resolver":"system","duration_ms":12.345}}When debug mode is enabled, provides detailed information about pattern matching and resolution:
Text format:
2026/01/29 14:32:15.136 [DEBUG] REQUEST_PATTERN matched pattern=.*\.example\.com$ request=test.example.com
2026/01/29 14:32:15.137 [DEBUG] CNAME_PATTERN matched pattern=.*\.cdn\.* cname=test.cdn.provider.net
2026/01/29 14:32:15.138 [DEBUG] Queried nameserver resolver=explicitJSON format:
{"timemillis":1738161135136,"time":"2026-01-29T14:32:15.136Z","level":"DEBUG","message":"REQUEST_PATTERN matched","fields":{"pattern":".*\\.example\\.com$","request":"test.example.com"}}
{"timemillis":1738161135137,"time":"2026-01-29T14:32:15.137Z","level":"DEBUG","message":"CNAME_PATTERN matched","fields":{"pattern":".*\\.cdn\\..*","cname":"test.cdn.provider.net"}}Minimal logging (errors only):
nameserver-switcher --log-requests=false --log-responses=falseFull debug logging (text format):
nameserver-switcher --debug --log-requests --log-responsesProduction JSON logging:
nameserver-switcher --log-format=json --log-requests --log-responsesEnvironment variable configuration:
export DEBUG=true
export LOG_REQUESTS=true
export LOG_RESPONSES=true
export LOG_FORMAT=json
nameserver-switcherFor detailed logging documentation, see Logging Documentation.
nameserver-switcher \
--request-patterns=".*\.example\.com$" \
--cname-patterns=".*\.cdn\.provider\.net$" \
--request-resolver="8.8.8.8:53" \
--explicit-resolver="1.1.1.1:53"export REQUEST_PATTERNS=".*\.example\.com$
.*\.test\.org$"
export CNAME_PATTERNS=".*\.cdn\.com$"
export REQUEST_RESOLVER="8.8.8.8:53"
export EXPLICIT_RESOLVER="1.1.1.1:53"
nameserver-switcher# Install grpcurl if needed: brew install grpcurl
# Resolve a domain
grpcurl -plaintext -d '{"name": "www.example.com", "type": "A"}' \
localhost:5354 api.v1.NameserverSwitcherService/Resolve
# Get current configuration
grpcurl -plaintext localhost:5354 api.v1.NameserverSwitcherService/GetConfig
# Update request patterns
grpcurl -plaintext -d '{"patterns": [".*\\.newdomain\\.com$"]}' \
localhost:5354 api.v1.NameserverSwitcherService/UpdateRequestPatterns
# Get statistics
grpcurl -plaintext localhost:5354 api.v1.NameserverSwitcherService/GetStats| Metric | Type | Description |
|---|---|---|
|
Counter |
Total DNS requests by protocol and type |
|
Histogram |
Request processing duration by resolver |
|
Counter |
Resolver usage count |
|
Counter |
Request pattern match count |
|
Counter |
CNAME pattern match count |
|
Counter |
Error count by type |
|
Gauge |
Current active connections |
|
Counter |
DNS response codes |
For more detailed documentation, see the Documentation folder: