> Implement service registration and dynamic discovery with health checks in microservices.
Scanned 9/11/2026
Install to Claude Code
npx -y skills add Intense-Visions/harness-engineering --skill microservices-service-discovery --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Microservices Service Discovery?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/intense-visions-microservices-service-discovery-harness-engineering)More formats (shields.io, HTML) on the badges page.
# Microservices: Service Discovery
> Implement service registration and dynamic discovery with health checks in microservices.
## When to Use
- Services are deployed dynamically (Kubernetes, ECS, auto-scaling groups) and their IPs change
- You need services to find each other without hardcoded hostnames
- You want health-check-aware routing — only route to healthy instances
- You're scaling services horizontally and need load balancing across instances
## Instructions
**Kubernetes DNS-based discovery (most common — no code needed):**
```yaml
# Kubernetes handles discovery automatically via DNS
# Service A calls Service B using the DNS name: http://order-service:8080
# Kubernetes resolves this to the correct pod IPs
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: production
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
type: ClusterIP # internal only
---
# In your app code — just use the DNS name
const ORDER_SERVICE_URL = process.env.ORDER_SERVICE_URL ?? 'http://order-service:8080';
```
**Consul-based service registration (for non-Kubernetes environments):**
```typescript
import Consul from 'consul';
const consul = new Consul({ host: process.env.CONSUL_HOST ?? 'consul' });
async function registerService(): Promise<void> {
const serviceId = `order-service-${process.env.POD_NAME ?? process.env.HOSTNAME}`;
await consul.agent.service.register({
id: serviceId,
name: 'order-service',
port: parseInt(process.env.PORT ?? '8080'),
address: process.env.POD_IP ?? '0.0.0.0',
tags: ['v1', 'production'],
check: {
http: `http://${process.env.POD_IP}:${process.env.PORT}/health`,
interval: '10s',
timeout: '5s',
deregistercriticalserviceafter: '30s',
},
});
console.log(`Registered service: ${serviceId}`);
// Deregister gracefully on shutdown
const cleanup = async () => {
await consul.agent.service.deregister(serviceId);
process.exit(0);
};
process.on('SIGTERM', cleanup);
process.on('SIGINT', cleanup);
}
// Client-side discovery — resolve service address from Consul
async function resolveService(name: string): Promise<string> {
const services = await consul.health.service({
service: name,
passing: true, // only healthy instances
});
if (services.length === 0) throw new Error(`No healthy instances of ${name}`);
// Simple round-robin
const instance = services[Math.floor(Math.random() * services.length)];
return `http://${instance.Service.Address}:${instance.Service.Port}`;
}
// Usage
const orderServiceUrl = await resolveService('order-service');
const order = await fetch(`${orderServiceUrl}/orders/${orderId}`);
```
**Service discovery with caching (avoid per-request lookup):**
```typescript
class ServiceRegistry {
private cache = new Map<string, { url: string; expiresAt: number }>();
private readonly ttlMs = 30_000; // cache for 30 seconds
async resolve(serviceName: string): Promise<string> {
const cached = this.cache.get(serviceName);
if (cached && Date.now() < cached.expiresAt) return cached.url;
const url = await this.lookup(serviceName);
this.cache.set(serviceName, { url, expiresAt: Date.now() + this.ttlMs });
return url;
}
private async lookup(name: string): Promise<string> {
// Could be Consul, etcd, or DNS
return resolveService(name);
}
invalidate(serviceName: string): void {
this.cache.delete(serviceName);
}
}
const registry = new ServiceRegistry();
// Use in HTTP client
async function callOrderService(path: string): Promise<Response> {
const baseUrl = await registry.resolve('order-service');
const response = await fetch(`${baseUrl}${path}`);
if (response.status === 503) {
// Service may have moved — invalidate cache and retry once
registry.invalidate('order-service');
const freshUrl = await registry.resolve('order-service');
return fetch(`${freshUrl}${path}`);
}
return response;
}
```
## Details
**Client-side vs. server-side discovery:**
| | Client-Side | Server-Side |
|---|---|---|
| Who resolves | Each service | Load balancer / gateway |
| Example | Consul + client library | AWS ALB, Kubernetes Service |
| Complexity | Higher (each service implements) | Lower (transparent) |
| Flexibility | More control | Less control |
In Kubernetes, prefer server-side discovery — Services and DNS handle it for you.
**Health checks are required:** Discovery without health checks routes traffic to dead instances. Register health check endpoints and ensure Consul/k8s removes unhealthy instances quickly (see `microservices-health-check` skill).
**Anti-patterns:**
- Hardcoding service IPs or hostnames — breaks on any deployment change
- Resolving service addresses on every request without caching — hammers the registry
- No graceful deregistration on shutdown — registry keeps routing to the dead instance until health check catches it
**Environment variable pattern for configuration:**
```typescript
// Use environment variables for service URLs in all environments
// Development: http://localhost:8081
// Staging/Production: http://order-service (Kubernetes DNS)
const ORDER_SERVICE_URL = process.env.ORDER_SERVICE_URL;
if (!ORDER_SERVICE_URL) throw new Error('ORDER_SERVICE_URL is required');
```
## Source
microservices.io/patterns/service-registry.html
## Process
1. Read the instructions and examples in this document.
2. Apply the patterns to your implementation, adapting to your specific context.
3. Verify your implementation against the details and edge cases listed above.
## Harness Integration
- **Type:** knowledge — this skill is a reference document, not a procedural workflow.
- **No tools or state** — consumed as context by other skills and agents.
## Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!