---
title: 'The Domain Name System'
source: 'https://academia.sh/en/courses/how-the-internet-works/domain-name-system'
course: 'How the Internet Works'
language: en
updated: '2026-08-17T18:07:03+00:00'
license: 'CC BY-SA 4.0'
---

# The Domain Name System

The distributed database that translates a name into an address, the resolution chain from root to authoritative server, and time-to-live-based caching.

The previous lesson established the structure of a name and how authority over it is
delegated. It was said that the registry does not hold the name's address, only the
information "ask these servers about this name."

This lesson covers the machinery that carries out that asking: the **domain name
system**. The question to answer is how the name `example.test` is translated into an
address, and why that translation is not redone from scratch on every request.

## Why Not a Single File

Keeping a file that maps names to addresses is what was done while the network held few
machines. Every machine carried a copy; when a name was added, the file was updated and
redistributed to everyone.

This scheme has three distinct limits, and all three are about scale. The file grows in
proportion to the number of names. Every change requires every copy to be refreshed, and
the frequency of change grows with the number of machines. And a single file means a
single authority: every name request has to go through the same point.

The domain name system overcomes all three limits by using a hierarchy. The database is
split, authority over each part is delegated to a separate party, and no one holds the
whole.

## The Roles in the Chain

Four distinct roles exist in resolution.

The **stub resolver** lives inside the machine making the request. The application's
call to translate a name into an address goes to this component in the operating
system. It does not perform resolution itself; it forwards the question to a resolver
and waits for the answer.

The **recursive resolver** is the server that takes on the work. It is usually a server
run by the access provider or the organization. It carries out every step needed to
find the answer and holds the cache itself.

The **root servers** sit at the top of the hierarchy. They do not know addresses
themselves; they know only which servers each top-level name has been delegated to.

The **authoritative servers** are the servers that hold a name's answers as the source
of record. For `example.test`, the authoritative server is the one the name owner
designated, and it is the same one reported during registration in the previous lesson.

## The Resolution Chain

Assuming `example.test` is not present in any cache at all, the query follows these
steps:

1. The application asks the stub resolver in the operating system for the address of
   `example.test`.
2. The stub resolver forwards the question to the recursive resolver and waits for the
   answer.
3. The recursive resolver asks a root server. The root does not know the address; it
   says which servers the authority for the top-level name `test` has been delegated to.
4. The recursive resolver asks one of those servers. It does not know the address
   either; it says which servers the authority for the name `example.test` has been
   delegated to.
5. The recursive resolver asks the authoritative server and receives the address.
6. The answer returns to the stub resolver, and from there to the application.

The division of labor separates two distinct query forms. The question the stub
resolver asks the recursive resolver is **recursive**: "find this name's answer and
bring it to me." The questions the recursive resolver asks the root and intermediate
servers are **iterative**: "tell me if you know this, and if you do not, tell me who to
ask."

This distinction is not accidental. If the root and top-level servers had to ask someone
else and assemble the answer for every incoming question, their load would be
unsustainable. Each intermediate server's work is a single, fixed-cost step; assembly is
concentrated in one recursive resolver.

The fact that the answer at every step takes the form "I do not know the address, ask
this one" is a direct reflection of the authority delegation from the previous lesson.
The length of the chain is determined not by the number of labels in the name but by how
many times authority has been delegated.

## The Shape of a Record

Query tools let these answers be seen directly. The command below queries the name that
denotes the machine itself; because the answer is produced locally, it is the same
everywhere.

```sh
dig +noall +answer localhost A
```

```
localhost.		0	IN	A	127.0.0.1
```

This single line shows a record's five fields:

| Field | Value in the example | Meaning |
|---|---|---|
| Name | `localhost.` | The name the record belongs to; the trailing dot denotes the root |
| Time to live | `0` | How many seconds the answer may be kept in cache |
| Class | `IN` | The address family; always this value for the internet |
| Type | `A` | The record's type; `A` carries an IPv4 address |
| Data | `127.0.0.1` | The record's content |

When the same command is run for an actual domain name, the format stays the same and
the values change. This course gives no such output: the time to live depends on when
the query is made, the returned address on where it is made from, and both begin going
stale the moment they are written on a page. Run the command yourself and the five
fields you see will match the ones above.

The details of record types are the next lesson's subject.

## Caching and Time to Live

If the chain above were run from scratch on every request, opening a single page would
require multiple network round trips, and the root servers would see every query made
anywhere in the world. What makes the system operable is the storing of answers.

Every record carries a **time to live** with it: how many seconds the answer can be used
without being asked again. The recursive resolver keeps the answer in its cache for this
period and answers the same question without running the chain when it recurs.

The party that sets this duration is the authoritative server issuing the answer — in
other words, the name owner. This gives the name owner direct control and requires
managing a trade-off.

The program below shows this behavior. Because the clock is supplied externally, the
output is the same on every run.

```python
class NameCache:
    """TTL-limited name->address cache. Clock is supplied externally."""

    def __init__(self) -> None:
        self.records: dict[str, tuple[str, int]] = {}
        self.upstream_query_count = 0

    def resolve(self, name: str, now: int, ttl: int, address: str) -> str:
        record = self.records.get(name)
        if record is not None and record[1] > now:
            return f"from cache  (remaining TTL: {record[1] - now})"
        self.upstream_query_count += 1
        self.records[name] = (address, now + ttl)
        return f"upstream query (new TTL: {ttl})"


cache = NameCache()
for second in [0, 100, 250, 300, 400]:
    result = cache.resolve("example.test", now=second, ttl=300, address="192.0.2.10")
    print(f"t={second:>3}s  {result}")

print(f"total upstream queries: {cache.upstream_query_count}")
```

```
t=  0s  upstream query (new TTL: 300)
t=100s  from cache  (remaining TTL: 200)
t=250s  from cache  (remaining TTL: 50)
t=300s  upstream query (new TTL: 300)
t=400s  from cache  (remaining TTL: 200)
total upstream queries: 2
```

Five resolution requests led to only two upstream queries. On an actual recursive
resolver, the ratio is far sharper because many users share the same cached record.

The trade-off is this:

- A **long time to live** reduces upstream queries and speeds up answers. In exchange,
  when a change is made, the old answer stays in use until the duration expires.
- A **short time to live** lets a change propagate quickly. In exchange, it raises query
  load and average response time.

This has a direct counterpart in operating practice: before moving a server, the time to
live is lowered; the move is carried out, and once propagation completes the duration is
raised back. Skip the advance lowering, and the old address keeps living in caches for
as long as the old duration.

This produces a consequence worth keeping in mind throughout: **a domain name change
does not appear everywhere at once.** Different users get different answers for a while,
depending on the state of their own caches. This is not a fault; it follows directly from
the definition of caching.

Queries with a negative answer are also stored. When a name that does not exist is
queried, the answer "no such name exists" is also kept in cache for a while; this is why
a newly created name does not appear immediately.

The cache does not live in a single place. The stub resolver, the recursive resolver and
the requesting application can each hold a separate cache. Tracing which layer a delay
or a stale answer came from therefore requires examining the layers separately.

## Summary

- Keeping names in a single file does not scale; the domain name system splits the
  database by hierarchy and delegates authority over each part.
- The resolution chain starts at the root, descends to the top-level name's server and
  from there to the authoritative server; the answer at each step points not to the next
  address but to the next authority.
- The stub resolver asks the recursive resolver recursively; the recursive resolver asks
  intermediate servers iteratively. This division of labor keeps the load on root and
  top-level servers fixed.
- A record consists of name, time to live, class, type and data fields.
- Time to live is set by the name owner and manages the trade-off between query load and
  how fast a change propagates.
- Caching happens across multiple layers and covers negative answers too; a change does
  not appear everywhere at the same moment.

## Next Step

This lesson showed a record's five fields and passed over the type field with a single
example, `A`. Yet a name does not hold only an address underneath it: redirection to
another name, mail routing, verification texts and authority declarations are stored in
the same layout. The next lesson covers these types, which problem each answers, and the
combination rules among them.
