Skip to content

Server routing (bridge)

Server routing ties two of your servers together so user traffic splits by direction: some sites open straight from the entry node, the rest travel a tunnel to the exit server. Users notice nothing: same subscription, same addresses, same entry speed.

Two directions

Russian and foreign sites see different IPs, and you choose which side sees which address.

Shadowsocks transport

One chacha20-ietf-poly1305 tunnel between the servers, all traffic including UDP.

Everything through the panel

Profiles, groups and the service user are created automatically via the API.

No node needed in advance

The script installs it over SSH itself, together with Docker and the ufw port.

A second bridge type: VLESS Route

Every user picks their own exit: the profile gets an extra host in the subscription.

The menu item lives at 4. Node extensions → Server routing. The first screen asks which bridge you need:

root@server: ~
Server routing
 Details in the wiki: https://wiki.egam.es/configuration/server-routing/

1.Bridge via Shadowsocks (configured: 1)
    RU sites through one IP, foreign sites through another; for every user at once
2.Bridge via VLESS Route (configured: 0)
    a personal exit for everyone: the profile gets one more host in the subscription, the route number is baked into the link

0.Exit

Select action (0-2):

You need a panel on this server and a second server.

usersame subscriptionentry nodedecides where traffic goesRU sites and IPssee the entry node IPforeign trafficsees the exit server IPstraightSS tunnelchacha20-ietf-poly1305

The module moves service traffic into a private network without breaking anything on the public path: an outbound Shadowsocks and rules are added to the entry node’s profile config, saying where Russian traffic goes and what stays direct. On the exit server a node comes up with a single service inbound: it serves nothing to users, it only terminates the tunnel.

The main setup question is where Russian traffic should go. Each option’s hint spells out the consequences right in the menu:

root@server: ~
Where should Russian traffic (sites and IPs) go?

1.Straight from the entry node
    when the entry node is inside RU: RU sites see the entry IP, foreign sites see the exit IP
2.Through the bridge
    when the entry node is abroad: RU sites see the exit IP, foreign sites see the entry IP

Select action (1-2):

“Through the bridge” mode. Foreign sites see the clean foreign entry IP, while Russian services get the native address of the exit server. No “you are logging in from abroad” captchas on Russian services.

Service Sees the IP of
yandex.ru/internet and RU sites exit server
ifconfig.me and foreign entry node

You can flip the mode any time with the single Set up again command: the direction switches without a reinstall because every object is reused.

The wizard asks a few questions in a row. First the exit server: it comes from the servers with configured SSH access (or you type an address by hand, and the script then offers to set up access):

root@server: ~
Exit server (from configured SSH targets):

1.root@203.0.113.20:22 (Kazan)
2.Enter an address manually

Then the port, the direction (screen above), the bridge name and the entry node’s profile:

root@server: ~
Bridge SS port (Enter — 9999): 9999
Bridge name shown in the panel (Enter — Kazan): Kazan

Which profile gets routed through the bridge?
 Traffic flows through it by the routes you picked — choose the entry node's profile

1.Default-Profile (2 inbounds)
  1. Exit server. The screen above. If the server has no node yet, the script offers to install everything remotely: Node 203.0.113.20 is not in the panel. Install it on this server over SSH (docker + remnanode)? (y/n). The exit server needs neither a domain nor certificates: it just listens on the port and terminates the tunnel.
  2. Bridge port. Defaults to 9999; EnterEnterEnter keeps it.
  3. Traffic direction. The table above.
  4. Bridge name. One name for all panel objects; EnterEnterEnter fills in the SSH server’s name: squad Kazan bridge, service user bridge-kazan, node and profile Kazan-bridge.
  5. Entry node profile. The outbound and rules land in it. More profiles can be attached later, for example when a second entry node appears.

While you were answering, the wizard already brought everything up:

root@server: ~
Fetching the node link key from the panel...
Link key fetched
Installing Docker on the remote server (may take a couple of minutes)...
Writing the node compose file...
Node files written to the server
Starting the node container...
Waiting for the node to reach the panel (attempt 1)...
Node connected to the panel
Creating the bridge internal squad...
Squad created
Creating the bridge service user...
Service user created
Patching profile 'Default-Profile': outbound + routing rules...
Profile 'Default-Profile' updated, the config reaches its nodes
Opening the bridge port in ufw on 203.0.113.20...
Port opened in ufw

At the end the wizard checks connectivity live: whether the node is connected to the panel, whether the bridge port answers, and reminds you how to verify the result from a client.

Under the hood: rules in the profile

In “Through the bridge” mode the profile config gets an outbound and these rules:

{ "ip": ["geoip:private"], "outboundTag": "BLOCK" },
{ "protocol": ["bittorrent"], "outboundTag": "BLOCK" },
{ "ip": ["geoip:ru"], "outboundTag": "SR_BRIDGE_SS" },
{ "domain": ["geosite:category-ru"], "outboundTag": "SR_BRIDGE_SS" }

In “Straight from the entry node” mode geoip:ru and geosite:category-ru switch their outboundTag to DIRECT, and a catch-all rule is added: all remaining profile traffic goes to SR_BRIDGE_SS. With extended RU lists one more rule with ext:geosite-ruex.dat:ru-available-only-inside is appended, always assigned to the bridge.

root@server: ~
Bridges via Shadowsocks: Kazan

 Bridge: 203.0.113.20:9999
 Node 203.0.113.20 connected to the panel
 Port 9999 answers from this server

1.Attach another profile
    that profile's entry node goes through the same exit
2.RU lists: extended (restore stock)
    one switch for all bridges; the list file lands on entry nodes
3.Set up again
    full setup from scratch: exit server, port, mode, profile
4.Rename the bridge
    the squad, user, node and profile all get the new name
5.Remove routing
    profiles return to direct egress; the bridge node and profile stay

0.Exit

Select action (0-5):

There can be several bridges: each with its own exit server, its own panel objects and its own attached profiles. If a profile already belongs to another bridge, the script warns and offers to reattach it.

The country bridge decides for everyone: one direction per profile. The second type hands the choice to the user. The profile gets an extra host in the subscription, for example “Germany → Moscow”, and selecting it sends all of that user’s traffic through the bridge server: both Russian and foreign sites see its IP. The regular host keeps working as before.

Subscription host RU sites see Foreign sites see
Germany (regular) by the profile’s common rules by the profile’s common rules
Germany → Moscow bridge server IP bridge server IP

Hosts switch in the client any time, without a reinstall or a second subscription: a phone can live on “Germany → Moscow” for the banks while the computer stays on plain “Germany”.

Germanyregular hostGermany → Moscowroute is baked into the UUIDentry nodeone and the samecommon profile rulesas without the bridgeexit serverRU and foreign sites see the bridge IPno vlessRoutevlessRoute: 2SS tunnel

The route number is a native host field in the panel (vlessRouteId). The panel bakes the number into the link UUID, the entry node reads it and sends the connection through the right outbound. The user configures nothing: they just pick a host with a clear name.

A line per exit

As many lines as exit servers, each with its own route number and its own statistics on the service user in the panel.

Exit reuse

A second line to the same exit needs no new server: same tunnel, own service user, own traffic accounting.

Several entries

The Attach another profile item adds a host “its entry → this exit” on the second entry node under the same route number.

Geo does not interfere

Route rules sit above geo rules: an assigned user goes to their exit entirely, the rest live by the common rules.

Under the hood: rules and UUID

The entry node’s profile gets an outbound and rules with route numbers:

{ "vlessRoute": 1, "outboundTag": "DIRECT" },
{ "vlessRoute": 2, "outboundTag": "SR_BRIDGE_SS_kazan" }

Route 1 is service traffic: direct egress from the entry node; a host for it is created only if the profile is already country-split. The number lives in the third UUID group: user 9d5f42a1-7b3c-4d8e-… on route 2 gets a link with 9d5f42a1-7b3c-0002-…, and that pair of bytes is what the node reads as the number.

The VLESS Route wizard starts with the exit machine: a new server over SSH or an “Exit of bridge …” line from the ones already configured:

root@server: ~
Exit server

1.New server (SSH)
2.Exit of bridge 'Kazan' (203.0.113.20:9999)
  1. Exit server. The screen above: same line, own service user.

  2. Port and name. Defaults are 9999 and the SSH server’s name; EnterEnterEnter keeps both.

  3. Entry node profile. VLESS only: the route rides inside the link UUID, Trojan and Shadowsocks have none. Profiles already taken by other bridges are marked in the list.

  4. Route number. A free number is suggested for you, taken ones are not offered:

    root@server: ~
    Route number (Enter picks 2): 2
  5. Host name. Defaults to “entry → exit”, for example “Germany → Moscow”.

From there the script works on its own: if a node container already runs on the server without a panel record, it offers to adopt it as is, without a reinstall. ufw on the exit server is enabled automatically: ports already listening publicly stay open, everything else closes.

root@server: ~
Bridge via VLESS Route: Kazan (route 2)

 Bridge: 203.0.113.20:9999
 Node 203.0.113.20 connected to the panel
 Port 9999 answers from this server
 Host in the subscription: Germany → Kazan

1.Hide the host from subscriptions
    the rules stay, the host simply disappears from user subscriptions
2.Attach another profile
    a second entry node through the same exit and route number
3.Set up again
    full setup from scratch: exit server, profile, route number, host
4.Rename the bridge
    the squad, user and subscription host get the new name
5.Remove routing
    the host is deleted, the rules go, everyone returns to the profile's common rules

0.Exit

Select action (0-5):

Tear-down is soft: the host leaves subscriptions, the rules leave the profile, and a user with an old cached link simply falls back to the common rules. Nothing breaks.

The stock geosite:category-ru rule covers all domains in .ru, .su, .рф plus name lists. But Russian services living on foreign domains never make it in and stay behind.

The RU lists item connects the daily-updated lists from the runetfreedom project. One rule is added for domains reachable only from inside Russia (ext:ru-available-only-inside):

root@server: ~
Enable the extended RU lists (domains reachable only from inside RU)? Stock rules stay, one ext: rule is added (y/n): y
Downloading geosite.dat from runetfreedom...
File fetched, checksum matches
Mounting the lists into node node.example.com...
Adding the ext:ru-available-only-inside rule to bridged profiles...
Rule added, profiles updated: 1
Extended RU lists enabled on: node.example.com

Nothing is replaced

Stock rules stay as they are; the new rule is added on top.

Always a Russian exit

Domains on the list get an RU egress in every bridge mode.

Installed on entry nodes

The entry node makes the routing decisions, so the list file lands there, separate from Xray’s stock geodata.

Self-updating

Every day at 4:30 a fresh copy is downloaded from mirrors and checksum-verified. Nodes restart only when the file actually changed.

Under the hood: files and cron
  • Directory/usr/local/remnawave_reverse
    • Directoryserver-routing
      • kazan.bridge (bridge: server, port, mode, panel objects)
      • ruex.state (extended lists state)
      • ruex-update.sh (daily updater)
      • ruex-update.log (last 50 log lines)

The crontab -l line looks like: 30 4 * * * bash /usr/local/remnawave_reverse/server-routing/ruex-update.sh. The updater downloads the dat file from four mirrors, checks sha256 and spreads it over the entry nodes; node restarts go through the panel API and only on a real file change.

Connect as a user from a regular squad (the one with the entry node) and open two sites:

yandex.ru/internet shows the entry node’s IP, ifconfig.me shows the exit server’s IP.

Remove routing returns profiles to direct egress, deletes the service user and the squad, and closes the ufw port on the second server. The bridge node and profile stay: they no longer route anything, and you can delete them in the panel by hand if they are not needed.