hashicorp/consul
DNS interface
Consul agents expose a DNS server (default port 8600) that resolves service and node queries against the catalog. Most operators use this as their primary discovery interface — it's what makes "service-name.service.consul" work in any application without integration code.
Purpose
The DNS interface translates standard DNS questions (A, AAAA, SRV, ANY, TXT, PTR) into catalog lookups, applies ACL filtering, and returns answers within strict latency budgets. It supports synthetic record families (addr.<ip>.consul), tag filtering (tag.web.service.consul), datacenter routing (web.service.dc1.consul), prepared queries (web.query.consul), and partition/namespace selectors in Enterprise.
Directory layout
agent/
├── dns.go # The legacy monolithic DNS server (72 KB)
├── dns_ce.go # CE-only routing helpers
├── dns/ # Newer DNS subpackage (request context helpers)
│ ├── buffer_response_writer.go
│ ├── context.go
│ └── context_test.go
internal/
└── dnsutil/ # Standalone DNS utilities (parsing, name composition)The legacy agent/dns.go is still the active server. The new agent/dns/ package and internal/dnsutil/ exist to incrementally factor out the monolith.
Query format
| Pattern | Resolves to |
|---|---|
web.service.consul |
Healthy web instances in the local DC |
web.service.dc1.consul |
Healthy web instances in DC dc1 (forwarded over WAN if needed) |
<tag>.web.service.consul |
Filter by tag |
web.connect.consul |
Connect-enabled service-mesh proxy endpoints for web |
web.virtual.consul |
The synthetic mesh-internal virtual IP for web |
web.query.consul |
Prepared query named web (with failover semantics) |
node-name.node.consul |
Node addresses |
addr.<hex-ip>.consul |
Synthetic record echoing the IP |
_<service>._<protocol>.service.consul |
RFC 2782 SRV style |
The naming machinery is in internal/dnsutil/ and the dispatch logic is in agent/dns.go::dispatch.
How it works
sequenceDiagram
participant App as Application resolver
participant Agent as Local agent (dns.Server)
participant Cache as agent/cache (or submatview)
participant Server as Consul server (RPC)
App->>Agent: DNS QUERY web.service.consul A
Agent->>Agent: parse name → (service=web, dc=local, tag=)
Agent->>Cache: HealthServices(web)
alt cache hit (TTL valid)
Cache-->>Agent: list of nodes/health
else cache miss
Cache->>Server: Health.ServiceNodes (blocking)
Server-->>Cache: nodes
Cache-->>Agent: list
end
Agent->>Agent: filter unhealthy + apply ACL token
Agent-->>App: A records (deterministic shuffle)Agents cache catalog responses (agent/cache/ + the health-services cache type) so repeated DNS hits don't always go to the leader. TTLs come from agent config (dns_config.service_ttl, etc., in agent/config/).
ACL handling
The agent's default token (or a query-override token via DNS RFC 1035 isn't possible, so it's always the agent token) is applied:
- The configured
acl.tokens.defaulttoken must be allowed to read every node and service that appears in the answer. - ACL-filtered services are silently omitted; this is the same behavior as the HTTP catalog endpoints, so DNS clients see only what they're permitted to see.
ACL filtering is implemented in agent/consul/aclfilter/ and called from the catalog reads that DNS performs.
Cross-datacenter resolution
When a DNS query specifies a non-local DC (web.service.dc2.consul), the agent forwards an RPC to the local server, which uses WAN federation to forward across DCs. WAN gossip and the WAN connection pool live in agent/consul/server_serf.go and agent/consul/wanfed/. For the newer cluster peering model, peer-aware DNS uses web.service.peer-name.consul.
Recursors
Configurable upstream DNS servers can be queried for non-*.consul records. Recursor settings come from agent config (recursors, recursor_strategy). Implementation lives in the recursive resolution loop inside agent/dns.go.
Performance and limits
- A response is built incrementally and truncated to fit UDP unless EDNS0 is in play. The truncation logic and TC-bit handling sit in
agent/dns.go. dns_config.enable_truncateanddns_config.udp_answer_limit(inagent/config/) control how many records get squeezed in.- The agent applies a deterministic shuffle so two consecutive queries don't return the same first answer.
- All DNS responses tag the underlying
consul.dns.*metrics (latency, query count, error count).
Integration points
- Cache: the
agent/cache-types/health-services.goand friends define how DNS pulls from the agent cache. - gRPC DNS service:
proto-public/pbdns/defines a public DNS-over-gRPC service used by Consul Dataplane to query without binding a UDP port. Implementation:agent/grpc-external/services/dns/. - Prepared queries:
web.query.consulresolves through the prepared-query endpoint (agent/consul/prepared_query_endpoint.go) which can apply failover, near-mode (closest DC by RTT), and templating.
Entry points for modification
- Add a new query family: extend the
dispatchswitch at the top ofagent/dns.go. - Tune cache TTLs: see the per-type stanzas under
agent/config/source_default.goand the runtime overlay inagent/config/runtime.go. - Add a synthetic record: mirror
addr.<ip>.consulhandling indispatchAddr. - gRPC DNS: plug the new resolution path into
agent/grpc-external/services/dns/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.