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
WeightManagerfromStrategyWeightentries using the cost formulavalue * cost^0.5 - Retrieves health data via
observer.GetObservation - Constructs
nodeobjects containing RTT, deviation, failure count, and computedRTTDeviationCost - Applies Baselines and Expected settings in
selectLeastLoadto 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:
-
Configuration parsing — The
strategyConfigLoaderininfra/conf/router_strategy.goreads the strategy field and loads the appropriate configuration object. -
Balancer construction — Inside
app/router/config.go, theBuildmethod instantiates aBalancerthat embeds the concrete strategy (random, leastping, roundrobin, or leastload). -
Runtime selection — The
Balancer.PickOutboundmethod callsstrategy.PickOutbound(candidates), passing the filtered list of available outbounds.
Key Implementation Files
app/router/strategy_random.go— Implements RandomStrategy withdice.Rollselectionapp/router/strategy_leastping.go— Implements LeastPingStrategy with RTT comparisonapp/router/balancing.go— Defines RoundRobinStrategy and genericBalancerlogicapp/router/strategy_leastload.go— Implements LeastLoadStrategy with weight management and cost calculationinfra/conf/router_strategy.go— Maps strategy names to config structs likeStrategyLeastLoadConfigapp/router/config.go— Contains theBuildmethod that wires strategies to balancers
Summary
- Four strategies are available:
random,leastping,roundrobin, andleastload - Observatory integration provides health data to
random,leastping, andleastload, whileroundrobinoperates 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
strategyfield, with strategy-specific settings defined in thesettingsobject
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →