DEV Community

Cover image for How Does One Server Find Another Server? DNS, Private DNS, and Service Discovery Explained
Anik Sikder
Anik Sikder

Posted on Originally published at aniksikder.me

How Does One Server Find Another Server? DNS, Private DNS, and Service Discovery Explained

Hey developers! 👋

Welcome back to our networking and infrastructure deep-dive series.

In the previous article

we learned something surprising:

Two servers inside AWS, Azure, GCP, or a private datacenter can communicate without touching the public internet.

No ISP.

No public IP.

No internet routing.

Just private networking.

But that raises a much bigger question.

A question every backend developer eventually encounters.


The Mystery Nobody Talks About

Imagine you have:

Web Server
    |
    |
    ▼
PostgreSQL Server
Enter fullscreen mode Exit fullscreen mode

Your application needs to connect to PostgreSQL.

Simple.

Right?

Not really.

Because the web server doesn't magically know:

Where PostgreSQL is.
Enter fullscreen mode Exit fullscreen mode

Servers are not humans.

They cannot read dashboards.

They cannot open AWS Console.

They cannot browse Kubernetes UI.

So the real question becomes:

How does one server discover another server?

This seemingly simple question led to the creation of:

  • DNS
  • Private DNS
  • Service Discovery
  • Consul
  • Eureka
  • Kubernetes DNS
  • Cloud Map
  • Service Meshes

And ultimately became one of the most important problems in distributed systems.

Today we're going deep into that rabbit hole.


Imagine a New City

Suppose you move into a new city.

You need to find:

Hospital
Bank
Police Station
Restaurant
Enter fullscreen mode Exit fullscreen mode

Do you memorize every building's GPS coordinates?

Of course not.

Instead you use:

Names
Enter fullscreen mode Exit fullscreen mode

Such as:

city-hospital.com
Enter fullscreen mode Exit fullscreen mode

instead of:

104.22.18.150
Enter fullscreen mode Exit fullscreen mode

Computers do exactly the same thing.

Humans prefer names.

Networks use IP addresses.

Something must translate between them.

That something is DNS.


DNS: The Internet's Phonebook

DNS stands for:

Domain Name System
Enter fullscreen mode Exit fullscreen mode

Its job is simple:

Name -> IP Address
Enter fullscreen mode Exit fullscreen mode

Example:

google.com

↓

142.250.x.x
Enter fullscreen mode Exit fullscreen mode

When you visit:

https://google.com
Enter fullscreen mode Exit fullscreen mode

your computer first asks:

What's the IP address of google.com?
Enter fullscreen mode Exit fullscreen mode

DNS replies:

142.250.x.x
Enter fullscreen mode Exit fullscreen mode

Only then can the connection begin.

Without DNS:

The internet would be a giant spreadsheet of IP addresses.
Enter fullscreen mode Exit fullscreen mode

Nobody wants that.


What Actually Happens?

When your browser requests:

google.com
Enter fullscreen mode Exit fullscreen mode

the flow looks like:

Browser
   |
   ▼
DNS Resolver
   |
   ▼
Root DNS
   |
   ▼
.com Nameserver
   |
   ▼
Google Nameserver
   |
   ▼
IP Address Returned
Enter fullscreen mode Exit fullscreen mode

Then:

Browser
   |
   ▼
Google Server
Enter fullscreen mode Exit fullscreen mode

Connection established.

This process often completes in milliseconds.

Billions of times every day.


But Cloud Infrastructure Has a Problem

Public DNS works great for public websites.

But what about:

Backend API
Redis
PostgreSQL
RabbitMQ
Elasticsearch
Enter fullscreen mode Exit fullscreen mode

These services are not public.

In fact:

They should NEVER be public.
Enter fullscreen mode Exit fullscreen mode

Example:

api.company.com
Enter fullscreen mode Exit fullscreen mode

can be public.

But:

postgres.company.com
Enter fullscreen mode Exit fullscreen mode

should almost never be exposed to the internet.

So where do we keep internal server names?

This is where Private DNS enters the picture.


Private DNS: The Hidden Phonebook

Think of Private DNS as:

An internal company directory.
Enter fullscreen mode Exit fullscreen mode

Only employees can access it.

Not outsiders.

Example:

db.internal
redis.internal
auth.internal
payments.internal
Enter fullscreen mode Exit fullscreen mode

These names resolve only inside the private network.

Outside users see:

Nothing.
Enter fullscreen mode Exit fullscreen mode

Inside AWS this is common.

Example:

ip-10-0-4-25.ec2.internal
Enter fullscreen mode Exit fullscreen mode

or

db.production.internal
Enter fullscreen mode Exit fullscreen mode

Only resources inside the VPC can resolve these names.

The public internet has no idea they exist.


Real Production Example

Suppose you're building an e-commerce platform.

Architecture:

Internet
    |
    ▼
Load Balancer
    |
    ▼
Web Servers
    |
    ▼
API Service
    |
    ├────────► PostgreSQL
    |
    ├────────► Redis
    |
    └────────► RabbitMQ
Enter fullscreen mode Exit fullscreen mode

Question:

How does API Service find PostgreSQL?

Do we hardcode:

10.0.12.55
Enter fullscreen mode Exit fullscreen mode

inside the application?

Never.

Because servers change.

Instances restart.

Containers move.

Databases failover.

IP addresses evolve.

Hardcoded IPs become operational nightmares.

Instead:

DATABASE_HOST = "postgres.internal"
Enter fullscreen mode Exit fullscreen mode

Now infrastructure can move PostgreSQL anywhere.

DNS updates.

Application remains unchanged.

Beautiful.


The IP Address Problem

Modern infrastructure is dynamic.

Very dynamic.

Consider Kubernetes.

A container crashes.

A new container starts.

The new container may receive:

Completely different IP
Enter fullscreen mode Exit fullscreen mode

within seconds.

Imagine every service storing IPs manually.

The system would collapse.

This is why names matter.

Names remain stable.

IPs do not.


Enter Service Discovery

DNS solved:

Find a machine
Enter fullscreen mode Exit fullscreen mode

Service Discovery solves:

Find a service
Enter fullscreen mode Exit fullscreen mode

Those sound similar.

They are not.

Let's see why.


The Microservice Nightmare

Imagine:

User Service

Order Service

Payment Service

Notification Service

Inventory Service

Recommendation Service
Enter fullscreen mode Exit fullscreen mode

Each service runs:

Multiple Instances
Enter fullscreen mode Exit fullscreen mode

Example:

Payment Service

payment-1
payment-2
payment-3
payment-4
Enter fullscreen mode Exit fullscreen mode

Now User Service wants Payment Service.

Which instance should it choose?

payment-1 ?
payment-2 ?
payment-3 ?
payment-4 ?
Enter fullscreen mode Exit fullscreen mode

Hardcoding doesn't work.

Static DNS doesn't fully solve it.

We need something smarter.


What Service Discovery Actually Does

A service registry keeps track of:

Who exists
Where they are
Whether they are healthy
Enter fullscreen mode Exit fullscreen mode

Think of it like:

Air Traffic Control
Enter fullscreen mode Exit fullscreen mode

for services.

Every service announces:

I'm alive.
Here's my address.
Enter fullscreen mode Exit fullscreen mode

Registry stores that information.

Other services ask:

Where is Payment Service?
Enter fullscreen mode Exit fullscreen mode

Registry responds:

payment-2
payment-3
payment-4
Enter fullscreen mode Exit fullscreen mode

Now requests can flow.


Netflix Solved This Years Ago

When Netflix moved to microservices they faced a huge challenge.

Thousands of services.

Millions of requests.

Servers constantly scaling.

They created:

Eureka
Enter fullscreen mode Exit fullscreen mode

A service discovery platform.

Services register themselves.

Other services discover them dynamically.

Without manual configuration.

This became one of the foundational patterns of cloud-native architecture.


Kubernetes: Service Discovery on Steroids

Kubernetes has built-in service discovery.

Suppose you deploy:

payment-service
Enter fullscreen mode Exit fullscreen mode

with:

4 Pods
Enter fullscreen mode Exit fullscreen mode

Kubernetes automatically creates:

payment-service.default.svc.cluster.local
Enter fullscreen mode Exit fullscreen mode

Your application simply uses:

PAYMENT_URL = "http://payment-service"
Enter fullscreen mode Exit fullscreen mode

No IP required.

No server management.

No lookup tables.

Kubernetes DNS handles everything.

Behind the scenes:

payment-service
        |
        ▼
Cluster DNS
        |
        ▼
Healthy Pods
Enter fullscreen mode Exit fullscreen mode

Magic?

Not really.

Just extremely sophisticated automation.


What Happens During a Request?

Imagine:

Order Service
Enter fullscreen mode Exit fullscreen mode

calls:

Payment Service
Enter fullscreen mode Exit fullscreen mode

Flow:

Order Service
       |
       ▼
DNS Query
       |
       ▼
Cluster DNS
       |
       ▼
Payment Service Endpoints
       |
       ▼
Healthy Pod Selected
       |
       ▼
Request Sent
Enter fullscreen mode Exit fullscreen mode

All this happens in milliseconds.

Most developers never see it.

Yet every request depends on it.


Health Checks Change Everything

Modern service discovery isn't just:

Where is the service?
Enter fullscreen mode Exit fullscreen mode

It also asks:

Is the service healthy?
Enter fullscreen mode Exit fullscreen mode

Suppose:

payment-3
Enter fullscreen mode Exit fullscreen mode

crashes.

Registry detects failure.

Immediately removes it.

Now traffic goes only to:

payment-1
payment-2
payment-4
Enter fullscreen mode Exit fullscreen mode

No code changes required.

No deployment required.

Infrastructure heals itself.

This is one of the superpowers of modern cloud systems.


DNS vs Service Discovery

Many engineers confuse them.

Let's simplify.

Feature DNS Service Discovery
Finds Machines Yes Yes
Finds Services Limited Yes
Tracks Health No Yes
Dynamic Scaling Limited Excellent
Cloud Native Partial Yes
Microservices Friendly Partial Excellent

Think of DNS as:

A phonebook
Enter fullscreen mode Exit fullscreen mode

Think of Service Discovery as:

A live GPS system with traffic updates.
Enter fullscreen mode Exit fullscreen mode

Huge difference.


AWS Example

AWS provides:

AWS Cloud Map
Enter fullscreen mode Exit fullscreen mode

Services register themselves.

Applications discover services dynamically.

Instead of:

10.0.22.17
Enter fullscreen mode Exit fullscreen mode

you use:

payment.production.internal
Enter fullscreen mode Exit fullscreen mode

AWS continuously keeps records updated.

Your applications stay clean.

Infrastructure stays flexible.


The Hidden System Every Developer Uses

Whenever your application connects to:

Database
Cache
Queue
API
Microservice
Enter fullscreen mode Exit fullscreen mode

one of the following is usually working behind the scenes:

DNS

Private DNS

Service Discovery
Enter fullscreen mode Exit fullscreen mode

Without them:

Modern cloud architecture would be impossible.
Enter fullscreen mode Exit fullscreen mode

Not difficult.

Impossible.


Visual Mental Model

Application
     |
     ▼
Service Name

(payment-service)

     |
     ▼
DNS / Service Discovery

     |
     ▼
Healthy Instance

(payment-2)

     |
     ▼
TCP Connection

     |
     ▼
Response
Enter fullscreen mode Exit fullscreen mode

Simple.

Powerful.

Cloud Native.


Why System Designers Care

Junior developers often think:

Database Host = Some String
Enter fullscreen mode Exit fullscreen mode

System designers think:

How is that string resolved?

Who owns it?

What happens during failover?

How is health tracked?

How does scaling affect discovery?

How quickly do updates propagate?
Enter fullscreen mode Exit fullscreen mode

These questions separate:

Application Development
Enter fullscreen mode Exit fullscreen mode

from

Infrastructure Engineering
Enter fullscreen mode Exit fullscreen mode

Understanding service discovery is one of the first steps toward thinking like a platform engineer, DevOps engineer, cloud architect, or distributed systems designer.


TL;DR Quick Recap

  • Servers communicate using IP addresses.
  • Humans prefer names.
  • DNS translates names into IP addresses.
  • Private DNS provides internal-only naming.
  • Modern infrastructure avoids hardcoded IPs.
  • Service Discovery tracks service locations dynamically.
  • Kubernetes provides built-in service discovery.
  • Healthy instances are automatically discovered.
  • Failed instances are automatically removed.
  • Every modern cloud platform depends heavily on these mechanisms.

Final Thoughts: The Invisible Map of the Cloud 🗺️

Most developers spend years writing:

DATABASE_HOST="postgres"
Enter fullscreen mode Exit fullscreen mode

without asking:

Who told the application where postgres is?
Enter fullscreen mode Exit fullscreen mode

Behind that tiny configuration value sits an enormous ecosystem:

  • DNS Servers
  • Private DNS
  • Service Registries
  • Health Checks
  • Load Balancers
  • Cloud Networking
  • Kubernetes Controllers

All working together to answer one deceptively simple question:

"Where is the thing I'm trying to talk to?"

And surprisingly, that question powers almost every distributed system on Earth.

In the next article, we'll go even deeper:

How Does Traffic Know Which Server Should Receive the Request?

Load Balancers → Reverse Proxies → Traffic Routing → Health Checks

Because finding a service is only half the story.

Choosing the right instance is where things get really interesting. 🚀

Top comments (0)