How v2rayN Implements Load Balancing and Server Health Checking: A Technical Deep Dive

v2rayN leverages V2Ray core's native balancer and observatory modules to enable automatic traffic distribution across multiple proxy servers, supporting strategies ranging from round-robin to least-ping while continuously monitoring endpoint health through configurable HTTP probes.

The open-source proxy client v2rayN (maintained by 2dust) provides sophisticated load balancing and server health checking capabilities by dynamically generating V2Ray core configurations. Rather than implementing custom networking logic, v2rayN orchestrates the core's built-in balancer and observatory features through strategic JSON configuration generation. This article examines the specific C# implementations in the v2rayN source code that enable automatic server selection and continuous health monitoring.

Load Balancing Architecture in v2rayN

v2rayN implements load balancing by treating server groups as collections of discrete outbound connections, then applying V2Ray core's balancer strategies across them. The implementation spans configuration generation, health monitoring, and routing rule transformation.

Strategy Selection via EMultipleLoad

The available balancing strategies are defined as an enumeration in v2rayN/ServiceLib/Enums/EMultipleLoad.cs:

public enum EMultipleLoad
{
    LeastPing,
    Fallback,
    Random,
    RoundRobin,
    LeastLoad
}

Users select one of these five strategies when configuring a server group. This selection is stored in ProtocolExtraItem.MultipleLoad and later consumed by the core configuration generator to determine which V2Ray balancer strategy to apply.

Dynamic Outbound Generation

When processing a server group, CoreConfigV2rayService.GenRoutingUserRuleOutbound generates individual outbound configurations for each server in the group:

// v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
var proxyOutbounds = new CoreConfigV2rayService(context with { Node = node, })
                         .BuildAllProxyOutbounds(tag);
_coreConfig.outbounds.AddRange(proxyOutbounds);
if (proxyOutbounds.Count(n => n.tag.StartsWith(tag)) > 1)
{
    var multipleLoad = node.GetProtocolExtra().MultipleLoad ?? EMultipleLoad.LeastPing;
    GenObservatory(multipleLoad, tag);
    GenBalancer(multipleLoad, tag);
}

When the group contains multiple servers, the code invokes GenObservatory to establish health monitoring and GenBalancer to create the load distribution logic. This conditional check ensures that single-server groups do not incur the overhead of balancing infrastructure.

Server Health Checking Implementation

v2rayN implements health checking through two distinct observatory types that feed real-time status data to the V2Ray core balancer.

Observatory and Burst Observatory

The health monitoring configuration is generated in v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs via the GenObservatory method:

private void GenObservatory(EMultipleLoad multipleLoad, string baseTagName = Global.ProxyTag)
{
    if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.Fallback)
    {
        // Burst-observatory (load-based) – ping config
        _coreConfig.burstObservatory = new BurstObservatory4Ray
        {
            subjectSelector = [baseTagName],
            pingConfig = new()
            {
                destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
                interval = "5m",
                timeout = "30s",
                sampling = 2,
            }
        };
    }
    else if (multipleLoad is EMultipleLoad.LeastPing)
    {
        // Observatory (ping-based) – probe URL
        _coreConfig.observatory = new Observatory4Ray
        {
            subjectSelector = [baseTagName],
            probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
            probeInterval = "3m",
            enableConcurrency = true,
        };
    }
}

Observatory (observatory) performs periodic HTTP probes to the probeUrl every three minutes with concurrency enabled. Burst observatory (burstObservatory) executes rapid ping bursts every five minutes to measure load distribution, specifically supporting the LeastLoad and Fallback strategies that require granular performance metrics.

Health Check Configuration Parameters

The underlying data structures in v2rayN/ServiceLib/Models/V2rayConfig.cs define the configuration schema:

public class Observatory4Ray
{
    public List<string>? subjectSelector { get; set; }
    public string? probeUrl { get; set; }
    public string? probeInterval { get; set; }
    public bool? enableConcurrency { get; set; }
}

public class BurstObservatory4Ray
{
    public List<string>? subjectSelector { get; set; }
    public BurstObservatoryPingConfig4Ray? pingConfig { get; set; }
}

public class BurstObservatoryPingConfig4Ray
{
    public string? destination { get; set; }
    public string? interval { get; set; }
    public string? timeout { get; set; }
    public int? sampling { get; set; }
}

The subjectSelector array filters which outbound tags the observatory monitors, while probeUrl defaults to the GUI's configured speed test endpoint. V2Ray core continuously updates health status based on these probe results, making the data available to balancers for routing decisions.

gRPC Transport Health Checks

For gRPC-based outbound connections, v2rayN adds transport-specific health check parameters in v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs:

case nameof(ETransport.grpc):
    GrpcSettings4Ray grpcSettings = new()
    {
        // ... other settings ...
        health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
    };

The health_check_timeout value is sourced from GrpcItem.HealthCheckTimeout and instructs the V2Ray core to terminate connections that fail health verification within the specified duration, ensuring rapid failover for gRPC streams.

The Balancer Configuration Pipeline

Once health monitoring is established, v2rayN generates the balancer configuration that ties outbounds to routing strategies.

Mapping Strategies to V2Ray Core

The GenBalancer method in V2rayBalancerService.cs translates the EMultipleLoad enum into V2Ray core strategy strings:

private void GenBalancer(EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
{
    var strategyType = multipleLoad switch
    {
        EMultipleLoad.Random      => "random",
        EMultipleLoad.RoundRobin  => "roundRobin",
        EMultipleLoad.LeastPing   => "leastPing",
        EMultipleLoad.LeastLoad   => "leastLoad",
        _                         => "roundRobin",
    };

    var balancer = new BalancersItem4Ray
    {
        selector = [selector],
        strategy = new()
        {
            type = strategyType,
            settings = new() { expected = 1 },
        },
        tag = $"{selector}{Global.BalancerTagSuffix}",
    };
    _coreConfig.routing.balancers ??= new();
    _coreConfig.routing.balancers.Add(balancer);
}

The balancer's selector array identifies which outbound tags participate in load distribution, while the strategy object configures the algorithm. The expected setting of 1 indicates that the balancer should select one optimal outbound per routing decision.

Routing Rule Transformation

To activate the balancer, routing rules must reference the balancer's tag rather than specific outbounds. The V2rayRoutingService.cs file handles this transformation:

// v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs
var balancerTagList = _coreConfig.routing.balancers?.Select(p => p.tag).ToList() ?? [];
if (balancerTagList.Count > 0)
{
    foreach (var rulesItem in _coreConfig.routing.rules
                     .Where(r => balancerTagList.Contains(r.outboundTag + Global.BalancerTagSuffix)))
    {
        rulesItem.balancerTag = rulesItem.outboundTag + Global.BalancerTagSuffix;
        rulesItem.outboundTag = null;
    }
}

This code iterates through routing rules and rewrites any rule pointing to a load-balanced group by setting balancerTag to the generated balancer identifier and clearing the outboundTag field. This redirection ensures that V2Ray core invokes the balancer strategy when matching traffic against these rules.

Generated Configuration Structure

When v2rayN processes a server group named MyGroup with the LeastPing strategy, it generates a V2Ray core configuration similar to this condensed JSON:

{
  "routing": {
    "rules": [
      {
        "type": "field",
        "outboundTag": null,
        "balancerTag": "MyGroup-proxy-balance",
        "domain": ["example.com"]
      }
    ],
    "balancers": [
      {
        "tag": "MyGroup-proxy-balance",
        "selector": ["MyGroup-proxy-1", "MyGroup-proxy-2"],
        "strategy": {
          "type": "leastPing",
          "settings": { "expected": 1 }
        }
      }
    ]
  },
  "observatory": {
    "subjectSelector": ["MyGroup-proxy"],
    "probeUrl": "https://www.gstatic.com/generate_204",
    "probeInterval": "3m",
    "enableConcurrency": true
  }
}

The balancer references individual outbound tags and applies the leastPing strategy, while the observatory continuously probes both outbounds every three minutes to provide latency data for routing decisions.

Summary

  • v2rayN generates multiple outbounds for server groups via CoreConfigV2rayService.cs, then creates balancer and observatory configurations when multiple servers are detected.
  • Health monitoring uses two modes: standard observatory for ping-based strategies (LeastPing) and burstObservatory for load-based strategies (LeastLoad, Fallback), configured in V2rayBalancerService.cs.
  • Five balancing strategies are supported—Random, RoundRobin, LeastPing, LeastLoad, and Fallback—mapped to V2Ray core algorithm types through the GenBalancer method.
  • Routing rules are dynamically rewritten in V2rayRoutingService.cs to use balancerTag instead of outboundTag, enabling transparent load balancing without manual rule modification.
  • gRPC connections receive additional health_check_timeout parameters to ensure transport-layer responsiveness.

Frequently Asked Questions

How does v2rayN determine which server to use in a load-balanced group?

v2rayN delegates server selection to the V2Ray core balancer. The core evaluates the configured strategy (random, round-robin, least-ping, etc.) against real-time health data provided by the observatory. For leastPing strategies, the core selects the outbound with the lowest latency; for leastLoad, it considers the burst observatory's load measurements.

What is the difference between observatory and burst-observatory in v2rayN?

The standard observatory performs periodic HTTP probes (default every 3 minutes) to measure basic connectivity and latency for leastPing balancing. The burst-observatory executes rapid ping bursts (default every 5 minutes with 30-second timeouts) to collect granular performance metrics required for leastLoad and fallback strategies that need to assess server load rather than just reachability.

How often does v2rayN check server health?

Health check intervals are hardcoded in the configuration generation logic: standard observatories probe every 3 minutes, while burst observatories probe every 5 minutes. These intervals are defined in V2rayBalancerService.cs and apply to all servers within the monitored group simultaneously.

Can I use load balancing with gRPC transport protocols?

Yes. v2rayN supports load balancing across gRPC outbounds and adds a health_check_timeout parameter to gRPC settings configured in V2rayOutboundService.cs. This ensures that unresponsive gRPC connections are terminated quickly, allowing the balancer to redirect traffic to healthy endpoints within the group.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →