Hey everyone! π I'm Naman, a full-stack developer passionate about networking and system design. Recently, I built my own DNS server using Go, and in this blog, I'll walk you through the fundamentals of DNS and how did i create my own DNS server from scratch. Let's dive in!
π What is DNS and Why is it Important?
DNS (Domain Name System) is like the phonebook of the internet. It translates human-friendly domain names (like google.com) into machine-readable IP addresses (like 142.250.182.206).
Imagine if every time you wanted to visit a website, you had to type in a long, numeric IP address. DNS eliminates that hassle by handling the lookup for you!
π‘ Example:
When you type namanraj.tech in your browser:
- Your computer asks a DNS server: "What is the IP address of namanraj.tech?"
- The DNS server replies with the correct IP (e.g., 76.76.21.93).
- Your browser connects to that IP and loads the website.
π How DNS Works (Step-by-Step Resolution Process)
1. Stub Resolver Initiates the Query
The Stub Resolver is a small built-in DNS client in your operating system (Windows, Linux, macOS).
Its job is to check if the domainβs IP is already cached in your local system.
If the IP is not found, the stub resolver forwards the request to a Recursive DNS Resolver for further lookup
2. Recursive Resolver Handles the Query
- The Recursive Resolver is a DNS server that helps find the IP address on behalf of your device.
- Common recursive resolvers include:
- Google Public DNS (8.8.8.8)
- Cloudflare DNS (1.1.1.1)
- Your ISPβs DNS resolver
3. Query to the Root DNS Server
- The recursive resolver first asks a Root Name Server for guidance.
-
What is a Root Server?
- Root servers are the highest level in the DNS hierarchy, acting as the entry point for all DNS lookups.
- There are 13 root servers worldwide, and they know where to find the TLD (Top-Level Domain) servers for different domains.
- Since the root server does not store individual website IPs, it replies:
- "I donβt have the IP for namanraj.tech, but ask a .tech TLD server"
Here is a dig command that queries the root DNS server (a.root-servers.net) for the nameservers (NS records) responsible for the com top-level domain (TLD). It essentially asks the root server which DNS servers handle requests for domain names ending in .com.
It returns the list of nameservers that are authoritative for the com TLD. These are the servers that handle DNS queries for domains ending in .com.
4. Query to the TLD Name Server
- The recursive resolver now asks the TLD Name Server for .tech
- What is a TLD Server?
- A Top-Level Domain (TLD) Name Server manages all domains with a specific extension, like:
- .com domains β Managed by .com TLD servers
- .org domains β Managed by .org TLD servers
- .tech domains β Managed by .tech TLD servers
- Since the TLD server still does not store the IP of namanraj.tech, it responds:
- "I donβt have the IP, but ask the Authoritative Name Server for namanraj.tech"
This dig command queries the DNS server at a.gtld-servers.net for the nameservers (NS records) responsible for the domain namanraj.tech. It essentially asks the a.gtld-servers.net server which DNS servers handle requests for namanraj.tech.
It will list the nameservers that are authoritative for the namanraj.tech domain. These are the servers that handle DNS queries for namanraj.tech
AUTHORITY SECTION: If the DNS server does not have the direct answer, it may provide a list of nameservers that are closer to the authoritative source
5. Query to the Authoritative Name Server
- The recursive resolver now queries the Authoritative Name Server.
- What is an Authoritative Name Server?
- It is the final authority for a domain.
- It stores and manages DNS records (A, CNAME, MX, etc.) for a domain (we will come to this later).
- The authoritative name server for namanraj.tech (e.g., ns1.vercel-dns.com) replies:
- "The IP address for namanraj.tech is 76.76.21.93."
This dig command queries the DNS system to find the nameservers responsible for the domain namanraj.tech. It asks which DNS servers handle requests for this domain.
It will give the list of nameservers that are associated with namanraj.tech domain that stores and manages DNS records. Here, they are:
- ns2.vercel-dns.com
- ns1.vercel-dns.com
6. Recursive Resolver Returns the IP
- The recursive resolver caches the response for future use.
- It sends the final IP address (76.76.21.93) back to your device.
- Now that your device knows the correct IP, it sends an HTTP request to 76.76.21.93.
- The web server at that IP responds, and namanraj.tech loads in your browser!
π οΈ Visualizing DNS: The Complete Resolution Flow (Step-by-Step)
Let's say you visit namanraj.tech , here's what happens :
ποΈ Types of DNS Records
DNS records define how a domain functions by mapping domain names to IP addresses, handling emails, and managing domain settings. Here are the most important ones:
1. A Record (Address Record) π
- Maps a domain to an IPv4 address, allowing browsers to locate websites.
- Example: namanraj.tech β 192.0.2.1
- Use Case: When a user types namanraj.tech, the A record helps find the web serverβs IP.
This dig command dig namanraj.tech A queries the DNS system to find the A (Address) record for the domain namanraj.tech. It asks for the IP address associated with this domain.
2. AAAA Record (IPv6 Address Record) π
- Similar to an A record but stores an IPv6 address instead of IPv4.
- An AAAA (Quad-A) record is similar to an A record but maps a domain to an IPv6 address. IPv6 uses a 128-bit address format, supporting trillions of IP addresses solving the issue of IPv4 exhaustion.
- Example: google.com β 2607:f8b0:4005:80b::200e
- Use Case: Websites that support IPv6 use AAAA records for better future-proofing.
3. CNAME (Canonical Name Record) π
- Creates an alias by pointing one domain to another domain instead of an IP.
- Example: www.namanraj.tech β namanraj.tech
- Use Case: Ensures www.namanraj.tech always resolves to the same IP as namanraj.tech without maintaining separate A records.
4. MX Record (Mail Exchange Record) π§
- Defines the mail server responsible for handling email delivery for a domain.
- Example: namanraj.tech β mail.google.com (Priority: 10)
- Use Case: When someone emails you@namanraj.tech, the MX record ensures it reaches the correct mail server.
The answer section lists one MX record for google.com:
- Priority: 10
- Mail server: smtp.google.com
In MX records, the priority value determines the order of mail servers to be used. Lower values have higher priority. If the primary server is unavailable, the next server with a higher priority value is used.
5. NS Record (Name Server Record) π‘
- Specifies the authoritative name servers that manage a domainβs DNS settings.
- Example: namanraj.tech β ns1.vercel-dns.com, ns2.vercel-dns.com
- Use Case: These servers handle all DNS queries for namanraj.tech.
6. TXT Record (Text Record) π
A TXT (Text) Record is a DNS record that allows a domain owner to store text-based information in the DNS. Unlike A or MX records, which are used for website and email routing, TXT records do not affect how a domain functions directly. Instead, they are mostly used for verification, security, and authentication purposes
- Stores arbitrary text, often used for security & authentication (SPF, DKIM, verification).
- Example: "v=spf1 include:_spf.google.com ~all" (SPF record for email validation)
- Use Case: Prevents email spoofing and domain ownership verification.
- The answer section lists multiple TXT records for google.com, including:
- google-site-verification values
- docusign values
- onetrust-domain-verification value
- globalsign-smime-dv value
- v=spf1 include:_spf.google.com ~all (SPF record for email validation)
- facebook-domain-verification value
- MS (Microsoft) verification value
- apple-domain-verification value
- cisco-ci-domain-verification value
7. SOA Record (Start of Authority) π
The SOA (Start of Authority) record is a special DNS record that provides important administrative details about a domainβs DNS settings. It acts as the "boss" of the DNS zone and ensures everything is properly managed.
- Contains administrative information about a domain, including the primary DNS server and refresh intervals
- Example: Primary NS: ns1.vercel-dns.com, Admin: hostmaster.nsone.net
- Use Case: Ensures DNS records are properly synchronized across name servers.
β‘ Building My Own DNS Server in Go: A Journey Through Networking Fundamentals π
To truly understand how DNS works, I decided to build my own custom DNS server in Goβand in this Part, Iβll walk you through how I did it, the challenges I faced, and what I learned along the way.
GitHub Repo (Feel free to star β or contribute!)
Why Build a DNS Server?
- How DNS queries/responses work (UDP vs. TCP)
- Authoritative vs. Recursive DNS (mine is authoritative)
- Real-world use cases (local development, ad-blocking, privacy)
This project was less about replacing Cloudflare and more about learning by doing.
Setting Up the Project
- Installing Go & Fiber - Click Here
- Dependencies Used (github.com/miekg/dns for DNS handling, Fiber for API).
- In-memory storage (Simple map[string]string for records)
- Project Structure Overview :
/my-dns-server
βββ main.go # Entry point of the DNS server
βββ go.mod # Dependency management
βββ go.sum # Dependency checksum file
Building the DNS Server: Core Implementation
1. Initializing the DNS Server (startDNSServer)
Before handling requests, we need to launch our DNS server. Here's the foundation:
func startDNSServer() {
// Set up DNS server
server := &dns.Server{
Addr: "0.0.0.0:9090", // Bind to all interfaces
Net: "udp",
}
// Handle DNS requests
dns.HandleFunc(".", handleDNSRequest)
// Start server
log.Printf("Starting DNS server on port 9090...")
err := server.ListenAndServe()
if err != nil {
log.Fatalf("Failed to start DNS server: %v", err)
} else {
log.Printf("DNS server is running on port 9090")
}
}
Key Decisions:
- UDP Protocol: Chosen for performance (DNS mostly uses UDP for quick queries)
- Port 9090: Avoids needing root privileges (vs standard port 53)
- UDPSize: Supports large DNS responses (like DNSSEC)
2. Concurrent Operation with Fiber
Since DNS runs continuously, we launch it in a goroutine alongside our Fiber API:
func main() {
go startDNSServer() // Run DNS in background
startWebServer() // Start Fiber API
}
Why This Matters:
- The DNS server keeps running while:
- The Fiber API manages records
- You query DNS from multiple clients simultaneously
3. DNS Server (UDP Port 9090)
The core DNS functionality listens for UDP requests and responds with stored records.
func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative = true // Mark this server as authoritative
found := false
for _, q := range r.Question {
log.Printf("Query: %s (%s)", q.Name, dns.TypeToString[q.Qtype])
if q.Qtype == dns.TypeA {
if ip, exists := dnsRecords[q.Name]; exists {
rr, _ := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, ip))
m.Answer = append(m.Answer, rr)
found = true
}
}
}
if !found {
m.Rcode = dns.RcodeNameError // NXDOMAIN
}
w.WriteMsg(m)
}
Why Using UDP and not TCP?
DNS primarily uses UDP (User Datagram Protocol) instead of TCP for 3 key reasons:
- UDP is connectionless (no handshake like TCP).
- A DNS query + response fits in 1 UDP packet (usually < 512 bytes).
- UDPβs lightweight nature lets them serve more queries with fewer resources.
4. Web Management API (Fiber, Port 3000)
A simple REST API to add DNS records dynamically:
// Web interface to manage DNS records
app.Get("/records", func(c *fiber.Ctx) error {
return c.JSON(dnsRecords)
})
app.Post("/records", func(c *fiber.Ctx) error {
type Record struct {
Domain string `json:"domain"`
IP string `json:"ip"`
}
var record Record
if err := c.BodyParser(&record); err != nil {
return c.Status(400).SendString("Bad request")
}
// Ensure domain ends with dot (DNS standard)
if record.Domain[len(record.Domain)-1] != '.' {
record.Domain += "."
}
dnsRecords[record.Domain] = record.IP
return c.SendString("Record added successfully")
})
Challenges & Lessons Learned
1. DNS Queries Are UDP-Based (Mostly)
- By default, DNS uses UDP (faster for small requests).
2. The Importance of the Trailing Dot
- "example.com" β "example.com." (fully qualified domain name).
3. Port Binding Permissions
- Ports below 1024 require admin rights (sudo on Linux).
- Used port 9090 for testing to avoid permission issues.
4. No Recursion (Yet!)
- My server is authoritative-only (only answers for records it knows).
- A recursive resolver would query other DNS servers if it doesnβt have an answer.
Practical Uses for a Custom DNS Server
- Local Development
- Ad-Blocking
- Network Privacy
Try It Yourself!
1. Clone the repo:
git clone https://github.com/Denyme24/my-dns-server.git
cd my-dns-server
go run main.go
2. Start querying!
Final Thoughts
Building a DNS server was a fantastic learning experienceβI now appreciate how much work goes into something we use daily without thinking.
What would you add to this project? Let me know in the comments! π
(P.S. If you found this useful, consider starring the repo! β)
Top comments (12)
it was an interesting read!
thanks!!
This was very informative. Thank you Naman.
glad, you find it informative. will keep writing such blogs.
Hey, great write up man. you say 'feel free to contribute' on your github. will you consider PRs ?
Yeah, the contributions are open for everyone, so go ahead.
Really well explained with the snippets! Easy to understand
so glad that you actually read the whole blog.
Hey, nice!
Do you accept any code improvements if I try?
yeah, sure!!
The flow is absolutely well structured. Nice Read.
Thanks for the feedback.