Service Discovery
The mechanism by which microservices find and communicate with each other. Services register themselves and others can look them up by name.
What is Service Discovery?
In short
Service discovery is the mechanism that lets services in a distributed system find each other's network locations at runtime, instead of relying on hardcoded IP addresses and ports. Each service registers its address in a shared registry when it starts, and callers look the service up by name to get a current, healthy address to connect to.
What service discovery actually solves
In a monolith, code calls a function and the function is right there in the same process. In a microservices system, one service has to make a network call to another, which means it needs an address: a host and a port. The hard part is that those addresses keep changing. Containers get rescheduled, instances autoscale up and down, a pod that was at 10.0.4.12 dies and comes back at 10.0.7.31. Hardcoding addresses breaks the moment anything moves.
Service discovery removes the hardcoding. Instead of asking for 10.0.7.31:8080, a service asks for payments and gets back a list of current, healthy addresses for whatever is running the payments service right now. The mapping from a stable logical name to a set of live network locations is the whole job.
There are two roles in every system. A service registry is the source of truth that stores name to address mappings. Service registration is how an instance announces I am alive at this address, and service lookup or resolution is how a caller asks where is X right now.
How it works under the hood
When an instance starts, it registers itself with the registry, sending its name, IP, port, and metadata like version or region. The registry tracks health using heartbeats or health checks. If an instance stops sending heartbeats, or a periodic check on /health fails, the registry marks it unhealthy and stops handing out its address. This is what keeps callers from being routed to a dead instance.
There are two patterns for the lookup itself. In client-side discovery, the caller queries the registry directly, gets the full list of healthy instances, and picks one using its own load-balancing logic. Netflix Eureka with the Ribbon client worked this way. The caller does more work but there is no extra network hop.
In server-side discovery, the caller just sends the request to a fixed address like payments.internal, and a load balancer or proxy in front of the service does the registry lookup and forwards the request. Kubernetes Services and AWS load balancers work this way. The client stays dumb, which is simpler, at the cost of an extra hop through the proxy.
Many platforms also expose discovery over DNS. Kubernetes gives every service a DNS name like payments.default.svc.cluster.local, and resolving that name returns a cluster IP or the set of pod IPs. DNS is convenient because almost every language already knows how to resolve a hostname, but plain DNS caching can hand out stale results, so platforms tune TTLs down hard.
Trade-offs and when to use it
You need service discovery once you have more than a handful of services that scale or move. If you run two fixed servers that never change, a static config file or a single load balancer address is simpler and you should not add a registry. The registry itself is infrastructure you have to run, secure, and keep available.
The registry is a tempting single point of failure, so production registries are run as a replicated cluster, often using a consensus protocol like Raft (Consul, etcd) so they stay consistent and survive node loss. The trade-off is that strongly consistent registries can reject writes during a partition, while eventually consistent ones like Eureka keep serving possibly stale data to stay available. That is a direct CAP-theorem choice.
Health checking is the part people get wrong. Too aggressive and you pull healthy instances during a brief GC pause or slow startup, causing thrashing. Too lax and you route traffic to dead instances. Tune the check interval and failure threshold to your real startup and failure times, and prefer readiness checks that reflect whether the instance can actually serve traffic, not just whether the process is up.
A concrete example
Picture an online store with an orders service that needs to call inventory. The store runs five inventory pods on Kubernetes. Each pod, on startup, is registered by the kubelet and added to the inventory Service's endpoint list once its readiness probe passes.
The orders service does not know any pod IP. It opens a connection to http://inventory:8080. Kubernetes resolves inventory to the Service, and kube-proxy load balances the request across the five healthy pod IPs behind it.
Now a deploy doubles inventory to ten pods during a sale. The new pods register automatically and start receiving traffic within seconds, with no change to the orders service. When the sale ends and the cluster scales back to five, the removed pods are deregistered and stop getting requests. The orders service never noticed any of it, which is exactly the point.
Where it is used in production
Kubernetes
Every Service gets a stable DNS name and cluster IP; kube-proxy and EndpointSlices track healthy pods and load balance to them.
HashiCorp Consul
A dedicated service registry with health checks and a Raft-backed store, used as the discovery layer across mixed VM and container fleets.
Netflix Eureka
Pioneered client-side discovery on AWS; instances register with Eureka and clients pick an instance themselves, favoring availability over strict consistency.
AWS Cloud Map and ECS
Registers ECS tasks automatically and exposes them through DNS or an API so other services resolve them by name as tasks scale.
Frequently asked questions
- What is the difference between service discovery and a load balancer?
- They overlap but are not the same. Service discovery answers where are the healthy instances of service X. A load balancer answers which one of them gets this request. In server-side discovery the load balancer does both by reading the registry, but the registry is what tracks the changing set of instances and a plain static load balancer does not.
- Client-side or server-side discovery, which should I use?
- Use server-side (Kubernetes Services, a proxy, or a cloud load balancer) by default. It keeps clients simple and language-agnostic at the cost of one extra network hop. Use client-side only when you need fine control over routing or want to avoid that hop, and you are willing to ship and maintain discovery logic in every client.
- Can I just use DNS for service discovery?
- Yes, and many platforms do, because every language can resolve a hostname. The catch is caching: clients and resolvers cache DNS records, so a record can point at a dead instance until the TTL expires. Platforms handle this by setting very low TTLs and updating records the instant instances change, but plain DNS without that tuning will hand out stale addresses.
- What happens if the service registry goes down?
- If the registry is the only path to addresses, new lookups fail and you cannot route to recently changed instances. That is why registries run as replicated clusters with consensus (Consul, etcd), and why many clients cache the last known good instance list so they can keep working through a short registry outage.
- How does the registry know an instance is dead?
- Through health monitoring. Instances either send periodic heartbeats to the registry, or the registry or platform polls a health endpoint like /health on a fixed interval. After a configured number of missed heartbeats or failed checks, the instance is marked unhealthy and removed from lookup results so callers stop being routed to it.
Learn Service Discovery hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
See also
Related glossary terms you might want to look up next.
Microservices
An architecture where an application is split into small, independent services that communicate over the network. Each service owns its own data and can be deployed separately.
Load Balancer
Distributes incoming traffic across multiple servers so no single server gets overwhelmed. Like a traffic cop directing cars to different lanes.
DNS
The phonebook of the internet. Translates human-readable domain names (google.com) into IP addresses that computers understand.
Monolith
A single, unified application where all features share the same codebase and deployment. Simpler to start with but harder to scale individual parts.
Circuit Breaker
A pattern that stops calling a failing service after repeated failures, preventing cascade failures. Like an electrical circuit breaker that cuts power to prevent fires.
Bulkhead
A pattern that isolates different parts of a system so a failure in one part doesn't sink the whole ship. Named after the compartments in a ship's hull.