# How Xray-core's Balancer Override Feature Enables Dynamic Load Balancing Changes

> Learn how Xray-core's balancer override feature allows dynamic load balancing changes via API calls without proxy restarts or config edits.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: deep-dive
- Published: 2026-04-21

---

**Xray-core's balancer override feature lets operators change load balancing targets at runtime via API calls without restarting the proxy or editing configuration files.**

The **balancer override** mechanism in XTLS/Xray-core bridges the gap between static configuration and operational flexibility. Proxy administrators often need to reroute traffic during incidents, maintenance windows, or performance optimizations—this feature solves that by injecting runtime overrides directly into the routing layer. This article breaks down the implementation from API surface to core routing logic, referencing actual source paths and method signatures from the Xray-core repository.

---

## API Entry Point: The `api bo` Command

The override feature exposes a clean CLI interface through [`main/commands/all/api/balancer_override.go`](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/balancer_override.go). This command constructs and dispatches gRPC requests to the internal routing service.

### Command Structure

```bash

# Force a specific balancer to use a designated outbound

xray api bo -b <balancer_tag> <outbound_tag>

# Remove the override and restore normal load balancing

xray api bo -b <balancer_tag> -r

```

The `-b` flag specifies the **balancer tag** to target, while the positional argument or `-r` flag determines whether to set or clear the override.

### gRPC Client Construction

Inside [`balancer_override.go`](https://github.com/XTLS/Xray-core/blob/main/balancer_override.go), the command instantiates a `RoutingService` client and builds an `OverrideBalancerTargetRequest`:

```go
request := &command.OverrideBalancerTargetRequest{
    BalancerTag: *balancerTag,
    Target:      args.First(),
}

```

The `Target` field carries the forced outbound tag—or remains empty when removing an override.

---

## RoutingService RPC Implementation

The server-side handling lives in [`app/router/command/command.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/command/command.go). The `OverrideBalancerTarget` method receives requests and delegates to the underlying router through the `routing.BalancerOverrider` interface.

### Type Assertion and Delegation

```go
func (s *RoutingService) OverrideBalancerTarget(ctx context.Context, request *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
    if bo, ok := s.router.(routing.BalancerOverrider); ok {
        return &OverrideBalancerTargetResponse{}, bo.SetOverrideTarget(request.BalancerTag, request.Target)
    }
    return nil, errors.New("router does not implement BalancerOverrider")
}

```

This design maintains clean separation: the command layer handles transport, while the router layer manages state.

---

## Core Router: Override Storage and Retrieval

The [`app/router/balancing.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing.go) file implements the actual override logic. The `Router` struct maintains a map of `balancers[tag]*Balancer`, and each `Balancer` carries a private `override` field.

### SetOverrideTarget Implementation

```go
func (r *Router) SetOverrideTarget(tag, target string) error {
    if b, ok := r.balancers[tag]; ok {
        b.override.Put(target)  // thread-safe write
        return nil
    }
    return errors.New("cannot find tag: " + tag)
}

```

The `Put` method synchronizes access to prevent race conditions during concurrent API calls and routing decisions.

### GetOverrideTarget Implementation

```go
func (r *Router) GetOverrideTarget(tag string) (string, bool) {
    if b, ok := r.balancers[tag]; ok {
        return b.override.Get(), true
    }
    return "", false
}

```

This method supports the `api bi` (balancer-info) command for operational visibility.

---

## Thread-Safe Override Data Structure

The [`app/router/balancing_override.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing_override.go) file defines the `override` struct, which wraps a string with `sync.RWMutex` protection.

### Implementation Details

```go
type override struct {
    target string
    mu     sync.RWMutex
}

func (o *override) Get() string {
    o.mu.RLock()
    defer o.mu.RUnlock()
    return o.target
}

func (o *override) Put(target string) {
    o.mu.Lock()
    defer o.mu.Unlock()
    o.target = target
}

func (o *override) Clear() {
    o.Put("")
}

```

Read-heavy operations (routing decisions) use `RLock` for concurrency, while write operations (API updates) acquire exclusive locks.

---

## Balancing Decision Point: PickOutbound

The critical integration occurs in [`app/router/balancing.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing.go) within the `PickOutbound` method. Every routing decision checks the override before consulting the load-balancing strategy.

### Override Short-Circuit Logic

```go
func (b *Balancer) PickOutbound(candidates []string) (string, error) {
    // Check for active override first
    if o := b.override.Get(); o != "" {
        return o, nil  //forced selection, bypass strategy
    }
    
    // No override: delegate to configured strategy
    return b.strategy.PickOutbound(candidates)
}

```

When an override exists, the balancer returns it immediately. When empty or cleared, normal strategy behavior resumes—whether **round-robin**, **least-latency**, **random**, or custom implementations.

---

## Operational Visibility: The `api bi` Command

The [`main/commands/all/api/balancer_info.go`](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/balancer_info.go) command complements the override feature by exposing current state. It calls `GetOverrideTarget` and displays both the active override and the strategy's normal selection.

### Typical Output

```

Balancer Tag: myBalancer
Strategy: roundRobin
Override Target: fast-proxy (active)
Principle Target: server-03

```

This visibility prevents configuration drift and aids incident response.

---

## Practical Code Examples

### CLI Override Operations

```bash

# Emergency traffic diversion: force all traffic through emergency proxy

xray api bo -b production-balancer emergency-proxy

# Maintenance complete: restore automatic load balancing

xray api bo -b production-balancer -r

# Verify current state

xray api bi -b production-balancer

```

### Programmatic Override in Go

```go
package main

import (
    "context"
    "log"
    "time"

    "github.com/xtls/xray-core/app/router/command"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := command.NewRoutingServiceClient(conn)

    // Apply override: force balancer to use "direct" outbound
    _, err = client.OverrideBalancerTarget(ctx, &command.OverrideBalancerTargetRequest{
        BalancerTag: "api_balancer",
        Target:      "direct",
    })
    if err != nil {
        log.Fatal("override failed:", err)
    }
    log.Println("Override applied successfully")

    // Clear override to restore normal behavior
    _, err = client.OverrideBalancerTarget(ctx, &command.OverrideBalancerTargetRequest{
        BalancerTag: "api_balancer",
        Target:      "", // empty string clears override
    })
    if err != nil {
        log.Fatal("clear failed:", err)
    }
    log.Println("Override cleared, normal balancing restored")
}

```

---

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`main/commands/all/api/balancer_override.go`](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/balancer_override.go) | CLI command parsing and gRPC client construction |
| [`app/router/command/command.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/command/command.go) | `RoutingService` RPC handler with `OverrideBalancerTarget` |
| [`app/router/balancing.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing.go) | Core router with `SetOverrideTarget`, `GetOverrideTarget`, and `PickOutbound` override logic |
| [`app/router/balancing_override.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing_override.go) | Thread-safe `override` struct with `sync.RWMutex` |
| [`main/commands/all/api/balancer_info.go`](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/balancer_info.go) | Status query command for operational visibility |

---

## Summary

- **Xray-core's balancer override** enables runtime traffic redirection without process restarts or configuration reloads
- The feature flows through **CLI → gRPC → RoutingService → Router → Balancer** with clean interface boundaries
- **Thread-safe storage** via `sync.RWMutex` protects concurrent reads (routing decisions) and writes (API updates)
- **Override short-circuits** the normal `PickOutbound` strategy, forcing a specific outbound tag when active
- **Empty target clears** the override, immediately restoring the configured load-balancing strategy

---

## Frequently Asked Questions

### How do I check if a balancer override is currently active?

Use the `xray api bi -b <balancer_tag>` command or call the `GetBalancerInfo` RPC. The response includes both the `override` field (active forced target) and the `principleTarget` (what the strategy would normally select).

### Does setting an override persist across Xray-core restarts?

No. The balancer override is **purely in-memory**. When the process restarts, all overrides are cleared and balancing resumes according to the static configuration file. For persistent changes, modify the config and restart.

### Can multiple balancers have independent overrides simultaneously?

Yes. Each balancer maintains its own `override` struct. The `balancers` map in [`app/router/balancing.go`](https://github.com/XTLS/Xray-core/blob/main/app/router/balancing.go) stores per-balancer state, so you can force `balancer-A` to `outbound-1` while `balancer-B` continues normal operation or points to `outbound-2`.

### What happens to existing connections when an override is applied?

Existing connections are **not affected**. The override only influences new routing decisions made after the `SetOverrideTarget` call completes. Established TCP streams, UDP sessions, and active WebSocket connections continue through their originally selected outbound.