Skip to content
Codeloom
System Design

System Design: Design a DNS System

Design a scalable Domain Name System. Covers recursive and authoritative resolution, zone file management, caching, anycast deployment, and handling billions of lookups daily.

·8 min read · By Codeloom
Advanced 12 min read

What you'll learn

  • Understand recursive vs. authoritative DNS resolution
  • Design zone file storage and propagation
  • Implement multi-level caching with TTL management
  • Deploy DNS servers globally using anycast
  • Handle DNS-based load balancing and failover

Prerequisites

None — this post is self-contained.

The Domain Name System translates human-readable names like api.example.com into IP addresses. It is one of the oldest and most critical pieces of internet infrastructure, handling trillions of lookups daily. Despite its apparent simplicity, DNS involves hierarchical delegation, aggressive caching, eventual consistency, and global deployment — making it a rich system design topic.

Functional Requirements

  • Resolve domain names to IP addresses (A, AAAA records).
  • Support additional record types: CNAME, MX, TXT, SRV, NS.
  • Allow domain owners to manage DNS records through an API and dashboard.
  • Propagate record changes to all authoritative servers within minutes.
  • Support DNS-based load balancing (returning different IPs based on geography or health).

Non-Functional Requirements

  • Latency: resolve queries in under 10ms from the nearest server.
  • Availability: DNS must be among the most available services on the internet. Target 100% uptime through redundancy.
  • Throughput: each server must handle hundreds of thousands of queries per second.
  • Consistency: after a record change, all servers must converge within 5 minutes.

DNS Resolution Flow

Understanding the resolution flow is essential before designing the system.

When a user types api.example.com into a browser:

  1. The browser checks its local DNS cache. If found and not expired, use it.
  2. The OS resolver checks its cache. If found, return it.
  3. The OS sends a query to the configured recursive resolver (typically provided by the ISP or a public resolver like 8.8.8.8).
  4. The recursive resolver checks its cache. If not found, it starts the resolution process.
  5. The recursive resolver queries a root name server: “Who handles .com?” The root responds with the .com TLD servers.
  6. The recursive resolver queries a .com TLD server: “Who handles example.com?” The TLD responds with the authoritative name servers for example.com.
  7. The recursive resolver queries the authoritative name server for example.com: “What is the A record for api.example.com?” The authoritative server responds with the IP address.
  8. The recursive resolver caches the result (respecting the TTL) and returns it to the client.

Our system needs to implement both the authoritative component (step 7) and optionally the recursive resolver component (steps 4-7).

Authoritative DNS Server Design

The authoritative server is the source of truth for a domain’s DNS records. When queried, it looks up the requested record and returns the answer.

Zone file storage. DNS records for a domain are organized into a zone. A zone contains all records for a domain and its subdomains (unless delegated to another zone). Store zones in a database:

dns_records
  id            UUID
  zone_id       UUID
  name          VARCHAR   (e.g., "api.example.com")
  type          ENUM      (A, AAAA, CNAME, MX, TXT, etc.)
  value         VARCHAR   (e.g., "203.0.113.42")
  ttl           INT       (seconds)
  priority      INT       (for MX records)
  updated_at    TIMESTAMP

Index on (name, type) for fast lookups.

Query processing. When a query arrives for api.example.com type A:

  1. Look up the zone for example.com.
  2. Search for a record matching the name and type.
  3. If a CNAME is found instead of an A record, follow the CNAME chain.
  4. Return the answer with the appropriate TTL.

For performance, load the entire zone into memory at startup and serve queries from RAM. Zone data is typically small (megabytes, even for large domains). Watch for database changes and reload the zone when records are updated.

Recursive Resolver Design

The recursive resolver does the heavy lifting of walking the DNS hierarchy on behalf of clients.

Caching is everything. A recursive resolver without caching would be painfully slow and would overwhelm upstream servers. Cache every response, keyed by (name, type), with a TTL from the response’s TTL field.

Cache structure:

  • Positive cache: stores successful responses. Entries expire after the record’s TTL.
  • Negative cache: stores NXDOMAIN (name does not exist) responses. These are cached with the SOA record’s minimum TTL, preventing repeated lookups for non-existent names.

Iterative resolution. The resolver maintains a queue of pending queries. For each query not in cache, it iteratively queries root, TLD, and authoritative servers. It caches intermediate results (NS records for TLDs, for example) to skip steps on future queries.

Concurrency. Multiple clients may request the same name simultaneously. Use request coalescing: if a resolution for api.example.com is already in progress, queue additional requests and serve them all from the same resolution result. This prevents query amplification under load.

Zone Propagation

When a domain owner updates a record through the management API, the change must propagate to all authoritative servers.

Primary-secondary replication. Designate one server as the primary and others as secondaries. When a record changes on the primary, it increments the zone’s serial number in the SOA record. Secondaries periodically check the serial number (zone transfer poll) and initiate a zone transfer (AXFR for full, IXFR for incremental) if the serial has increased.

NOTIFY mechanism. Rather than waiting for secondaries to poll, the primary sends a DNS NOTIFY message to all secondaries immediately after a change. The secondaries then initiate a zone transfer. This reduces propagation delay from minutes to seconds.

Modern approach: database-backed. Instead of file-based zone transfers, have all authoritative servers read from the same replicated database (PostgreSQL with read replicas, or a distributed store like CockroachDB). Changes are visible to all servers as soon as the database replicates. This eliminates the primary-secondary distinction and simplifies operations.

Global Deployment with Anycast

Deploy authoritative servers across multiple geographic locations and advertise the same IP address from all locations via anycast BGP. When a recursive resolver queries the authoritative IP, the query is routed to the nearest deployment by the internet’s routing infrastructure.

Benefits:

  • Low latency: queries reach the geographically closest server.
  • DDoS absorption: attack traffic is distributed across all locations rather than concentrating on one.
  • Automatic failover: if a location goes down, BGP withdraws the route, and traffic shifts to the next nearest location.

Deploy at least three anycast locations across different continents for a production DNS service.

DNS-Based Load Balancing

DNS can distribute traffic across multiple backends:

Round-robin. Return multiple A records for the same name. Clients typically use the first one, and DNS servers rotate the order on each response. This provides basic load distribution but no health awareness.

Geographic routing. Return different IP addresses based on the resolver’s location (determined by the resolver’s IP or EDNS Client Subnet). Route European users to European servers and North American users to North American servers.

Health-checked routing. Monitor the health of each backend IP. Remove unhealthy IPs from the DNS response. This provides automatic failover but is limited by DNS caching — clients may hold stale records for up to the TTL.

Weighted routing. Assign weights to IPs and probabilistically include them in responses. Use this for gradual traffic shifts during migrations.

Security: DNSSEC

DNS was designed without authentication, making it vulnerable to spoofing (an attacker returning fake IP addresses). DNSSEC adds cryptographic signatures to DNS records.

Each zone signs its records with a private key. The corresponding public key is published as a DNSKEY record and authenticated by the parent zone (the .com zone signs a DS record pointing to example.com’s key). Recursive resolvers validate the chain of signatures from the root to the queried zone.

DNSSEC adds complexity (key rotation, larger responses, signature computation) but is essential for security-critical domains.

Monitoring

  • Query rate: queries per second, broken down by record type and response code.
  • Resolution latency: time to resolve queries, both cached and uncached.
  • Cache hit ratio: for recursive resolvers, this is the primary performance indicator. Target above 90%.
  • Zone propagation delay: time between a record change and its availability on all authoritative servers.
  • SERVFAIL rate: the percentage of queries that fail. This should be near zero.

Scaling Considerations

  • Recursive resolver scaling: deploy multiple resolver instances behind a load balancer. Each instance maintains its own cache, so more instances means more aggregate cache capacity but lower per-instance hit ratios. Use consistent hashing on the query name to route queries to the same resolver for better cache efficiency.
  • Authoritative server scaling: since zones are read-only (writes go through the management API), scaling reads is straightforward. Add more anycast locations or more servers per location.
  • Large zones: for domains with millions of records (CDN providers), partition the zone across multiple server groups by name prefix.

Summary

A DNS system combines authoritative servers (the source of truth for domain records) with recursive resolvers (the caching intermediaries that walk the hierarchy). The critical design decisions are how you propagate zone changes (NOTIFY plus incremental transfers or database replication), how you deploy globally (anycast), and how aggressively you cache (positive and negative caching with request coalescing). DNS’s simplicity is deceptive — its global scale, caching semantics, and security requirements make it one of the most interesting distributed systems to design.