00:00

What Is DNS (Domain Name System) and How Does It Work?

Last updated:

The Domain Name System (DNS) is the internet's address book: it turns names that people remember, such as example.com, into IP addresses that computers use to connect, such as 104.20.23.154. Every time an app opens a website or calls an API, a DNS lookup usually happens first. DNS works through a hierarchy of servers and caches, so most lookups take only milliseconds.

This tutorial explains how DNS works step by step, the record types you will meet, and how Java resolves names, including a JVM cache that surprises many developers. You will also see DNS pitfalls that cause real outages.

1. What Is DNS?

Computers find each other by IP address, a number like 104.20.23.154 (IPv4) or 2606:4700:10::6814:179a (IPv6). People prefer names. DNS connects the two, much like a phone's contact list connects "Mum" to a number.

DNS is not one big database. It is split across millions of servers, and each part of a name is managed by a different owner. In shop.example.com, read from right to left: the root, then the .com top-level domain (TLD), then example.com, then shop. This split is why DNS scales to the whole internet. It also sits under every client-server call your Java code makes.

2. Why DNS Matters in System Design

DNS is more than a lookup table. Large systems use it to:

  • Spread traffic: returning several IP addresses for one name gives simple load balancing.
  • Route users to the nearest servers: GeoDNS answers with the closest region, which is how a CDN sends you to a nearby edge server.
  • Fail over: a DNS provider can stop returning an unhealthy server's address.
  • Find services: in Kubernetes, a service is reachable at a name like orders.shop.svc.cluster.local.

Because every request starts with DNS, a DNS failure can take down a healthy system. Teams often learn this during an outage.

3. How DNS Works, Step by Step

3.1 The Servers Involved

  • Stub resolver: a small client in your operating system that your app asks first. It keeps a short-lived cache.
  • Recursive resolver: a server run by your ISP, company or a public service such as 1.1.1.1 or 8.8.8.8. It does the legwork and caches answers.
  • Root servers: 13 named root servers, each run as many copies around the world. They know where every TLD is.
  • TLD servers: servers for .com, .in, .org and so on. They know which servers are in charge of each domain.
  • Authoritative servers: the servers that hold a domain's real records, usually run by a DNS provider.

3.2 A Lookup, Step by Step

How a DNS lookup works A Java app asks the operating system's stub resolver for example.com. On a cache miss, the stub resolver asks a recursive resolver. The recursive resolver asks a root server, which refers it to the .com TLD servers; asks a .com server, which refers it to the authoritative servers for example.com; and asks an authoritative server, which returns the A record. The recursive resolver caches the answer for its TTL and returns it to the stub resolver and the app. Java app InetAddress.getByName 1 7 OS stub resolver checks its local cache 2 6 Recursive resolver ISP, 1.1.1.1 or 8.8.8.8 3 4 5 Root server refers to .com servers .com TLD server refers to example.com Authoritative server answers the A record 6: answer cached for its TTL 7: the app gets the IP address
Figure 1: a cache miss walks from root to TLD to authoritative server; every resolver along the way caches the answer for its TTL.
  1. Your Java app asks the OS stub resolver for example.com.
  2. On a cache miss, the stub resolver asks the recursive resolver.
  3. The recursive resolver asks a root server, which replies with a referral: "ask the .com servers".
  4. It asks a .com server, which refers it to the authoritative servers for example.com.
  5. It asks an authoritative server, which returns the answer.
  6. The recursive resolver caches the answer and returns it to the stub resolver.
  7. Your app receives the IP address and opens a connection.

The root and TLD steps are rarely needed, because resolvers cache those referrals too. Queries normally use UDP on port 53, falling back to TCP for large answers. DNS over HTTPS (port 443) and DNS over TLS (port 853) encrypt the lookups.

4. DNS Record Types and TTL

RecordWhat it holdsExample
AAn IPv4 addressexample.com → 104.20.23.154
AAAAAn IPv6 addressexample.com → 2606:4700:10::6814:179a
CNAMEAn alias for another namewww.shop.in → shop.in
MXMail servers, with a priority (lower is preferred)gmail.com → 5 gmail-smtp-in.l.google.com
NSThe authoritative servers for a domaingmail.com → ns1.google.com
TXTFree text, used for email security and domain verificationv=spf1 ...
PTRReverse lookup: IP address to name154.23.20.104.in-addr.arpa → ...

Every record has a TTL (time to live): how many seconds resolvers may cache it. A long TTL means fewer lookups and faster answers; a short TTL means changes reach users sooner. TTL is the main dial, and it ties DNS closely to general caching trade-offs.

5. DNS in Java: Code Examples

The examples use only the JDK and run on Java 17 or later. They make real lookups, so the IP addresses and timings you see may differ.

5.1 Example 1: Resolving a Name

ResolveHostDemo.java
package com.javabytechie.dns;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class ResolveHostDemo {

    public static void main(String[] args) {
        for (String host : new String[] {"example.com", "orders.shop.invalid"}) {
            try {
                InetAddress[] addresses = InetAddress.getAllByName(host);   // every A and AAAA answer
                System.out.println(host + " resolves to:");
                for (InetAddress address : addresses) {
                    String type = address instanceof Inet6Address ? "IPv6 (AAAA)" : "IPv4 (A)";
                    System.out.println("  " + address.getHostAddress() + "  " + type);
                }
            } catch (UnknownHostException e) {
                System.out.println(host + " could not be resolved: " + e.getMessage());
            }
        }
    }
}
Output
example.com resolves to:
  104.20.23.154  IPv4 (A)
  172.66.147.243  IPv4 (A)
  2606:4700:10:0:0:0:6814:179a  IPv6 (AAAA)
  2606:4700:10:0:0:0:ac42:93f3  IPv6 (AAAA)
orders.shop.invalid could not be resolved: orders.shop.invalid: nodename nor servname provided, or not known

getAllByName returns every A and AAAA record. By default Java lists IPv4 addresses first. The .invalid top-level domain is reserved and never resolves, so it is a safe way to test the UnknownHostException path.

5.2 Example 2: The JVM's Own DNS Cache

JvmDnsCacheDemo.java
package com.javabytechie.dns;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Security;

public class JvmDnsCacheDemo {

    public static void main(String[] args) throws UnknownHostException {
        // null means "not set": the JDK then caches successful lookups for 30 seconds
        System.out.println("networkaddress.cache.ttl          = " + Security.getProperty("networkaddress.cache.ttl"));
        System.out.println("networkaddress.cache.negative.ttl = " + Security.getProperty("networkaddress.cache.negative.ttl"));

        // Must run before the first lookup to take effect
        Security.setProperty("networkaddress.cache.ttl", "10");

        String host = "example.org";
        System.out.println("first lookup took  " + timeLookup(host) + " ms (asks the network)");
        System.out.println("second lookup took " + timeLookup(host) + " ms (served from the JVM cache)");
    }

    private static long timeLookup(String host) throws UnknownHostException {
        long start = System.nanoTime();
        InetAddress.getByName(host);
        return (System.nanoTime() - start) / 1_000_000;
    }
}
Output
networkaddress.cache.ttl          = null
networkaddress.cache.negative.ttl = 10
first lookup took  10 ms (asks the network)
second lookup took 0 ms (served from the JVM cache)

Java keeps its own cache on top of the operating system's. If networkaddress.cache.ttl is not set, the JDK caches successful lookups for 30 seconds and failed ones for 10, ignoring the record's own TTL. Set the property in the java.security file or with Security.setProperty before the first lookup.

5.3 Example 3: Querying Record Types

DnsRecordLookup.java
package com.javabytechie.dns;

import java.util.Hashtable;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class DnsRecordLookup {

    public static void main(String[] args) throws NamingException {
        Hashtable<String, String> env = new Hashtable<>();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");   // the JDK's built-in DNS client
        DirContext dns = new InitialDirContext(env);

        String domain = "gmail.com";
        Attributes records = dns.getAttributes(domain, new String[] {"NS", "MX"});

        NamingEnumeration<? extends Attribute> all = records.getAll();
        while (all.hasMore()) {
            Attribute record = all.next();
            for (int i = 0; i < record.size(); i++) {
                System.out.println(record.getID() + "\t" + record.get(i));
            }
        }
        dns.close();
    }
}
Output
NS	ns4.google.com.
NS	ns2.google.com.
NS	ns1.google.com.
NS	ns3.google.com.
MX	10 alt1.gmail-smtp-in.l.google.com.
MX	40 alt4.gmail-smtp-in.l.google.com.
MX	30 alt3.gmail-smtp-in.l.google.com.
MX	20 alt2.gmail-smtp-in.l.google.com.
MX	5 gmail-smtp-in.l.google.com.

The JDK's built-in JNDI DNS provider can read any record type. Here, gmail.com lists four name servers and five mail servers; a sender tries priority 5 first. Records come back in no particular order.

6. Common Mistakes and Pitfalls

  • Trusting "DNS propagation": there is no push. Old answers live in caches until their TTL expires, so lower the TTL a day before a migration, not during it.
  • Forgetting the JVM cache: Java ignores record TTLs, so a client may keep calling a load balancer's old IP after it changes.
  • Caching failures: a lookup that fails during a short outage is remembered for 10 seconds by default.
  • A CNAME at the root of a domain: shop.in itself cannot be a CNAME; providers offer ALIAS or CNAME flattening instead.
  • One DNS provider: if it goes down, every name you own disappears; critical domains often use two.
  • Hard-coding IP addresses: they break as soon as the provider or cloud changes them.

7. Best Practices

  • Choose TTLs on purpose: minutes for records you may change, hours for stable ones.
  • Set the JVM cache TTL to match your platform, such as 30–60 seconds behind cloud load balancers.
  • Use names, not IPs, in configuration, and let DNS do service discovery.
  • Monitor DNS from several regions, and alert on resolution failures and slow lookups.
  • Protect your domain with registrar locks, two-factor login and DNSSEC where supported.

8. Summary

  • DNS turns names into IP addresses through a hierarchy: root, TLD and authoritative servers.
  • Recursive resolvers do the work and cache answers for each record's TTL.
  • A, AAAA, CNAME, MX, NS, TXT and PTR are the records you will meet most.
  • DNS also drives load balancing, geo-routing, failover and service discovery.
  • Java adds its own cache: 30 seconds for successes and 10 for failures by default.

The Domain Name System is easy to take for granted until it fails, so understanding its caches and TTLs is one of the cheapest ways to make a system more reliable. Open the FAQ tab for DNS interview questions.