NSerfDiscovery.Yarp 0.1.5-beta

This is a prerelease version of NSerfDiscovery.Yarp.
dotnet add package NSerfDiscovery.Yarp --version 0.1.5-beta
                    
NuGet\Install-Package NSerfDiscovery.Yarp -Version 0.1.5-beta
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NSerfDiscovery.Yarp" Version="0.1.5-beta" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NSerfDiscovery.Yarp" Version="0.1.5-beta" />
                    
Directory.Packages.props
<PackageReference Include="NSerfDiscovery.Yarp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NSerfDiscovery.Yarp --version 0.1.5-beta
                    
#r "nuget: NSerfDiscovery.Yarp, 0.1.5-beta"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NSerfDiscovery.Yarp@0.1.5-beta
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NSerfDiscovery.Yarp&version=0.1.5-beta&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=NSerfDiscovery.Yarp&version=0.1.5-beta&prerelease
                    
Install as a Cake Tool

Yarp.ReverseProxy.NSerfDiscovery

NuGet

A dynamic service discovery plugin that connects NSerf with YARP reverse proxy.

Services automatically advertise their routing configuration via NSerf gossip protocol, and the gateway dynamically builds its routing table without manual configuration.

Installation

dotnet add package NSerfDiscovery.Yarp

Requirements

  • .NET 8.0 or later
  • Yarp.ReverseProxy 2.1.0 or later

How it works

  • Service side: Each service instance publishes its YARP configuration as JSON in a NSerf tag (default: yarp:config).
  • Gateway side: The gateway reads these tags through NSerf's service discovery and dynamically builds YARP routes and clusters.
  • Dynamic updates: When services join/leave the cluster, the gateway automatically rebuilds its routing configuration.

Gateway Setup

The gateway uses AddNSerfGateway to configure both NSerf and YARP in one call:

using Yarp.ReverseProxy.NSerfDiscovery.Extensions;

var builder = WebApplication.CreateBuilder(args);

var nodeName = Environment.GetEnvironmentVariable("SERF_NODE_NAME") ?? "gateway";
var seedNode = Environment.GetEnvironmentVariable("SERF_JOIN");

// Configure YARP with NSerf Gateway
builder.Services
    .AddReverseProxy()
    .AddNSerfGateway(options =>
    {
        options.NodeName = nodeName;
        options.BindAddress = "0.0.0.0:7946";
        if (!string.IsNullOrEmpty(seedNode))
        {
            options.SeedNodes = [seedNode];
        }
    });

var app = builder.Build();

app.MapReverseProxy();
app.MapGet("/health", () => Results.Ok(new { status = "healthy", node = nodeName }));

await app.RunAsync();

Service Setup

Services use AddNSerfService to automatically advertise themselves and their YARP configuration:

using Yarp.ReverseProxy.NSerfDiscovery.Extensions;

var builder = WebApplication.CreateBuilder(args);

var serviceName = Environment.GetEnvironmentVariable("SERVICE_NAME") ?? "orders-api";
var instanceId = Environment.GetEnvironmentVariable("INSTANCE_ID") ?? $"{serviceName}-{Guid.NewGuid():N}";
var servicePort = int.Parse(Environment.GetEnvironmentVariable("SERVICE_PORT") ?? "8080");
var seedNode = Environment.GetEnvironmentVariable("SERF_JOIN");

// Configure NSerf Service with YARP config
builder.Services.AddNSerfService(builder.Configuration, options =>
{
    options.ServiceName = serviceName;
    options.Port = servicePort;
    options.InstanceId = instanceId;
    options.BindAddress = "0.0.0.0:7946";
    
    if (!string.IsNullOrEmpty(seedNode))
    {
        options.SeedNodes = [seedNode];
    }
});

var app = builder.Build();

app.MapGet("/api/orders", () => Results.Ok(new { service = serviceName, instance = instanceId }));

await app.RunAsync();

Service Configuration (appsettings.json)

Services define their YARP routes and clusters in the ReverseProxyExport section:

{
  "ReverseProxyExport": {
    "ServiceName": "orders-api",
    "Routes": {
      "orders-route": {
        "RouteId": "orders-route",
        "ClusterId": "orders-cluster",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "orders-cluster": {
        "ClusterId": "orders-cluster",
        "LoadBalancingPolicy": "RoundRobin"
      }
    }
  }
}

Advanced: Manual YARP Config

You can also provide YARP configuration manually via environment variable:

var customYarpConfig = Environment.GetEnvironmentVariable("YARP_CONFIG_JSON");

builder.Services.AddNSerfService(builder.Configuration, options =>
{
    options.ServiceName = serviceName;
    options.Port = servicePort;
    options.YarpConfigJson = customYarpConfig; // Override appsettings.json
    // ...
});

Features

  • Zero-configuration service discovery: Services automatically register and deregister
  • Dynamic routing: Gateway routing table updates in real-time as services scale
  • Load balancing: Multiple instances of the same service are automatically load-balanced
  • Health checks: Supports YARP's active and passive health checking
  • Session affinity: Cookie-based sticky sessions
  • Flexible matching: Path, host, header, and query parameter-based routing

Architecture

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│  Service A  │         │  Service B  │         │  Service C  │
│  Instance 1 │         │  Instance 1 │         │  Instance 1 │
└──────┬──────┘         └──────┬──────┘         └──────┬──────┘
       │                       │                       │
       └───────────────────────┼───────────────────────┘
                               │ NSerf Gossip Protocol
                               │
                      ┌────────▼────────┐
                      │   YARP Gateway  │
                      │  (Auto-config)  │
                      └─────────────────┘
                               │
                      ┌────────▼────────┐
                      │  HTTP Requests  │
                      └─────────────────┘

Docker Compose Example

Here's a complete example using Docker Compose to run a gateway with two service instances:

version: '3.8'

services:
  gateway:
    build:
      context: .
      dockerfile: TestGateway/Dockerfile
    environment:
      SERF_NODE_NAME: gateway
      SERF_JOIN: "service1:7946"
    ports:
      - "8080:8080"

  service1:
    build:
      context: .
      dockerfile: TestService/Dockerfile
    environment:
      SERVICE_NAME: orders-api
      INSTANCE_ID: orders-api-1
      SERVICE_PORT: "8080"
      SERF_NODE_NAME: orders-api-1
    ports:
      - "8101:8080"

  service2:
    build:
      context: .
      dockerfile: TestService/Dockerfile
    environment:
      SERVICE_NAME: orders-api
      INSTANCE_ID: orders-api-2
      SERVICE_PORT: "8080"
      SERF_NODE_NAME: orders-api-2
      SERF_JOIN: "service1:7946"
    ports:
      - "8102:8080"

Run the stack:

docker compose up --build

Test the gateway (requests will be load-balanced across both service instances):

# Via gateway (load balanced)
curl http://localhost:8080/api/orders

# Direct to service1
curl http://localhost:8101/api/orders

# Direct to service2
curl http://localhost:8102/api/orders

# View gateway's YARP configuration
curl http://localhost:8080/debug/yarp-config
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.5-beta 404 11/19/2025