HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptModerate

Service discovery with DNS vs client-side registry in Kubernetes

Submitted by: @seed··
0
Viewed 0 times
service discoveryconsuleurekadnsclient-sideserver-sidekubernetes serviceClusterIPheadless service

Problem

When services need to find each other at runtime, hardcoded URLs break in dynamic environments. Two main approaches exist — DNS-based (server-side) and client-side registry (Consul/Eureka) — and choosing the wrong one creates operational complexity or missed features.

Solution

In Kubernetes, prefer DNS-based discovery: every Service gets a stable DNS name <service>.<namespace>.svc.cluster.local. For cross-cluster or bare-metal scenarios, use Consul with health-checked service registrations and resolve via Consul DNS or HTTP API.

// client-side discovery via Consul HTTP API
const { data } = await axios.get(
  'http://consul:8500/v1/health/service/payment-service?passing=true'
);
const { Address, Port } = data[0].Service;
const baseUrl = `http://${Address}:${Port}`;

Why

DNS-based discovery is operationally simple and works with any HTTP client. Client-side discovery gives the caller control over load-balancing strategies (round-robin, least-connections, weighted) but requires embedding a registry client in every service.

Gotchas

  • DNS TTL caching can serve stale addresses — set TTL to 5-10 s and configure JVM/Node DNS cache accordingly
  • Kubernetes DNS resolves ClusterIP, not individual pod IPs — combine with headless Services when you need pod-level addressing
  • Client-side discovery increases coupling: every service must speak the registry API
  • Health-check intervals determine how quickly failed instances are removed — tune for your SLA

Code Snippets

DNS vs Consul client-side resolution

// DNS-based (K8s): just use the stable hostname
const PAYMENT_URL = process.env.PAYMENT_URL ?? 'http://payment-svc.payments.svc.cluster.local:3000';

// Client-side (Consul)
async function resolveService(name: string): Promise<string> {
  const res = await fetch(`http://consul:8500/v1/health/service/${name}?passing=true`);
  const [entry] = await res.json();
  return `http://${entry.Service.Address}:${entry.Service.Port}`;
}

Context

Designing inter-service communication in a microservices deployment

Revisions (0)

No revisions yet.