Xray-core Routing Strategies for Load Balancing: Random, LeastPing, LeastLoad, and RoundRobin Explained

Xray-core supports four distinct load-balancing routing strategies—random, leastping, leastload, and roundrobin—that distribute traffic across candidate outbounds using uniform selection, latency-based metrics, computed load costs, or sequential rotation.

The XTLS/Xray-core repository implements a sophisticated routing system that balances outbound traffic across multiple proxy candidates. These routing strategies integrate with the observatory feature to make intelligent selection decisions based on real-time health data, enabling high-availability deployments that automatically avoid failed or high-latency nodes.

How Xray-core Routing Strategies Work

The router implements a pipeline that bridges configuration and runtime selection. According to the source code in app/router/config.go, the Build method matches the textual Strategy value from your configuration and creates a Balancer object that embeds the concrete strategy implementation.

When a routing rule requires an outbound tag, the Balancer.PickOutbound method delegates to the specific strategy's selection logic. Most strategies (except roundrobin) request the latest health observations from the extension.Observatory feature, which reports per-outbound latency, alive status, and health statistics.

Supported Load-Balancing Strategies

Random Strategy

The RandomStrategy provides uniform random selection across all candidate outbounds. Implemented in app/router/strategy_random.go, this strategy uses the dice.Roll helper function to pick candidates uniformly at random. When the observatory is enabled, it filters out dead outbounds before selection. If no candidates are alive and a fallbackTag is defined, the balancer routes traffic to the fallback.

Configuration key: "type": "random"

Least Ping Strategy

The LeastPingStrategy selects the outbound with the smallest observed latency. Defined in app/router/strategy_leastping.go, this strategy scans the observation list maintained by the observatory and keeps the node with the minimum Delay value (using int64(99999999) as a sentinel for uninitialized values). Only outbounds marked as alive in the observatory are considered for selection.

Configuration key: "type": "leastping"

Round Robin Strategy

The RoundRobinStrategy cycles through candidates sequentially without consulting health data. Defined in app/router/balancing.go, this strategy maintains an atomic index (next) that advances modulo the candidate count. Because it does not check the observatory, dead outbounds can be selected unless you configure a fallback tag to handle total failure scenarios.

Configuration key: "type": "roundrobin"

Least Load Strategy

The LeastLoadStrategy offers the most sophisticated selection logic, computing a load cost for each outbound based on RTT, failure counts, and configurable weights. Implemented in app/router/strategy_leastload.go, this strategy:

  • Builds a WeightManager from StrategyWeight entries using the cost formula value * cost^0.5
  • Retrieves health data via observer.GetObservation
  • Constructs node objects containing RTT, deviation, failure count, and computed RTTDeviationCost
  • Applies Baselines and Expected settings in selectLeastLoad to prune candidates and return the optimal node(s)

Configuration structures for this strategy reside in infra/conf/router_strategy.go within the StrategyLeastLoadConfig struct.

Configuration key: "type": "leastload"

Configuration Examples

LeastLoad with Weights and Baselines

routing:
  rules:
    - inboundTag: ["in"]
      balancer:
        outboundSelector: ["out1", "out2", "out3"]
        strategy: "leastload"
        fallbackTag: "fallback"
        settings:
          costs:
            - tag: "out1"
              weight: 1
            - tag: "out2"
              weight: 2
          baselines: [50ms, 100ms]
          expected: 2
          maxRTT: 200ms
          tolerance: 0.2

Random Strategy with Fallback

{
  "routing": {
    "rules": [
      {
        "inboundTag": ["in"],
        "balancer": {
          "outboundSelector": ["outA", "outB", "outC"],
          "strategy": "random",
          "fallbackTag": "fallback"
        }
      }
    ]
  }
}

RoundRobin Configuration

[routing.rules]
inboundTag = ["in"]

[routing.rules.balancer]
outboundSelector = ["out1","out2"]
strategy = "roundrobin"

LeastPing Setup

routing:
  rules:
    - inboundTag: ["in"]
      balancer:
        outboundSelector: ["outX","outY"]
        strategy: "leastping"

Strategy Configuration Pipeline

The system binds configuration to implementation through three specific mechanisms:

  1. Configuration parsing — The strategyConfigLoader in infra/conf/router_strategy.go reads the strategy field and loads the appropriate configuration object.

  2. Balancer construction — Inside app/router/config.go, the Build method instantiates a Balancer that embeds the concrete strategy (random, leastping, roundrobin, or leastload).

  3. Runtime selection — The Balancer.PickOutbound method calls strategy.PickOutbound(candidates), passing the filtered list of available outbounds.

Key Implementation Files

Summary

  • Four strategies are available: random, leastping, roundrobin, and leastload
  • Observatory integration provides health data to random, leastping, and leastload, while roundrobin operates blindly
  • LeastLoad offers the most customization, supporting weighted costs, RTT baselines, and expected node counts for fine-grained control
  • Fallback tags ensure traffic routing continues when all candidates fail health checks
  • Configuration occurs within router rules using the strategy field, with strategy-specific settings defined in the settings object

Frequently Asked Questions

Which routing strategy performs best for high-latency networks?

Use leastping or leastload for high-latency networks. The leastping strategy explicitly selects the outbound with the smallest observed RTT from the observatory, while leastload incorporates RTT deviation and failure counts into its cost calculation. Both avoid routing traffic through high-latency nodes, unlike random or roundrobin.

Does the roundrobin strategy check if outbounds are alive?

No. According to the implementation in app/router/balancing.go, RoundRobinStrategy maintains an atomic counter (next) and cycles through candidates modulo the list length without consulting the observatory. Dead outbounds receive traffic unless you configure a fallbackTag to handle total balancer failure.

How does the leastload strategy calculate cost for each outbound?

The strategy uses a WeightManager that applies the formula value * cost^0.5 as defined in app/router/strategy_leastload.go. It retrieves health observations including RTT and failure counts, then computes RTTDeviationCost for each node. The final selection considers optional baselines and the expected number of nodes to return, allowing bandwidth and speed prioritization.

Can I use different routing strategies for different inbound tags?

Yes. Each routing rule in the configuration defines its own balancer with an independent strategy field. You can configure leastping for latency-sensitive applications while using random for general traffic, assigning each configuration to specific inboundTag values within the same Xray-core instance.

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 →