Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Servidor

How to Configure Sub-Servers and Load Balancing on Your MU Server

Learn how to configure sub-servers and implement load balancing on your MU Online server to handle high player counts and maximize stability.

VI ViciadosMU Team · Updated on 4 jul 2026 · ⏱ 18 min read

Understanding Sub-Servers in MU Online Architecture

A standard MU Online private server runs as a collection of cooperating processes: a ConnectServer, a JoinServer, one or more GameServer instances, a DataServer, and supporting services such as the EventServer and CastleServer. Most beginner setups run a single GameServer — this works for small communities but becomes a bottleneck once player counts climb past a few hundred concurrent users.

Sub-servers are additional GameServer instances that run alongside the primary one. From the player's perspective, they appear as separate server channels in the login screen (e.g., "Server 1 — Lotus", "Server 2 — Vine"). Traffic is distributed among them, reducing the load on any single process and improving overall responsiveness.

Load balancing in MU Online is handled primarily by the JoinServer component, which tracks how many players are connected to each GameServer and directs incoming login requests accordingly. Understanding how each piece fits together is essential before making configuration changes.

Nota: The term "sub-server" in MU Online does not mean a physically separate machine — though it can. It refers to an additional GameServer process, whether on the same hardware or a remote host. Both approaches use identical configuration steps.

Planning Your Sub-Server Layout

Before editing any configuration files, plan your layout carefully:

  • How many GameServer instances do you need? Estimate your peak concurrent player target. A single well-tuned instance handles roughly 500–800 players comfortably. Two instances cover up to ~1,500.
  • Will sub-servers run on the same machine or separate hosts? Same-machine setups are simpler but share CPU and RAM. Separate hosts require proper LAN/WAN routing between the GameServer and the central JoinServer/DataServer.
  • How will you name and number instances? Each GameServer must have a unique Server Code (numeric ID). Assign these before you start so there are no conflicts in the ConnectServer listing.

A common two-server layout looks like this:

ConnectServer (public-facing, port 44405)
    └── JoinServer (internal, port 55557)
            ├── GameServer 0 → "Lotus"   → port 55901, code 0
            └── GameServer 1 → "Vine"    → port 55902, code 1
DataServer (internal, port 55980)
SQL Server (internal, port 1433)

Configuring the ConnectServer

The ConnectServer (ConnectServer/ConnectServer.cfg or equivalent XML file depending on your season build) maintains the list of available GameServer entries shown to clients. Each entry needs a name, an IP address, a port, and a server code.

Open the server list configuration and add an entry for each sub-server:

; ConnectServer → ServerList configuration
; Format: ServerCode → Name → IP → ClientPort → IsVisible

[ServerList]
Server0_Code    = 0
Server0_Name    = Lotus
Server0_IP      = 127.0.0.1          ; public IP if remote
Server0_Port    = 55901
Server0_Show    = 1

Server1_Code    = 1
Server1_Name    = Vine
Server1_IP      = 127.0.0.1          ; public IP if remote
Server1_Port    = 55902
Server1_Show    = 1

; JoinServer connection → used by ConnectServer internally
JoinServerIP    = 127.0.0.1
JoinServerPort  = 55557

> [!WARNING] > Do not reuse a server code that is already assigned. Duplicate server codes cause the JoinServer to misroute login tokens, which results in players being disconnected immediately after selecting a character. Always increment the code value for each new instance.

Configuring the JoinServer

The JoinServer acts as the session broker. It receives authentication tokens from the ConnectServer after a player logs in, then forwards the player to the appropriate GameServer. It does not require per-server configuration for each GameServer — instead, each GameServer registers itself with the JoinServer on startup.

Verify these settings in JoinServer/JoinServer.cfg:

; JoinServer → core settings

ListenPort          = 55557           ; port ConnectServer connects to
MaxConnections      = 10000           ; total token capacity across all servers
LoadBalanceMode     = 1               ; 0 = first available, 1 = least connections
TokenExpireSeconds  = 30              ; how long a login token stays valid

; Logging
LogLevel            = 2               ; 1=errors only, 2=info, 3=verbose
LogPath             = logs/joinserver.log

LoadBalanceMode = 1 instructs the JoinServer to route each new login to whichever GameServer currently has the fewest active connections. This is the recommended setting for balanced distribution.

Setting Up Each GameServer Instance

Each GameServer instance needs its own working directory containing its own configuration files. The cleanest approach is to copy your existing GameServer folder and rename it (e.g., GameServer0 and GameServer1).

Inside each folder, edit the main configuration file (GameServer.cfg or Setup/GameServerInfo.ini depending on build):

; GameServer1 → Vine instance
; Settings that MUST differ between instances

ServerCode          = 1               ; unique per instance → must match ConnectServer list
ServerName          = Vine
ListenPort          = 55902           ; unique per instance → client connection port
InternalPort        = 44902           ; unique per instance → JoinServer communication port

; Settings that point to shared services
JoinServerIP        = 127.0.0.1
JoinServerPort      = 55557
DataServerIP        = 127.0.0.1
DataServerPort      = 55980

; Map assignment (optional → assign specific maps to each server)
; Leave blank to mirror all maps, or restrict to reduce memory usage
EnabledMaps         = 0,1,2,3,4,7,8  ; Lorencia, Dungeon, Devias, Noria, Lost Tower, Atlans, Tarkan

MaxPlayers          = 800
LogPath             = logs/gameserver1.log

> [!TIP] > Assigning different maps to each sub-server (map sharding) is an advanced technique that reduces per-process memory usage. For example, GameServer 0 could host Lorencia, Dungeon, and Devias, while GameServer 1 hosts Lost Tower, Atlans, and Tarkan. Players moving between maps are transparently transferred to the correct server by the JoinServer. This requires careful planning of where event maps (Kalima, Crywolf, Castle Siege) are hosted.

Firewall and Network Rules

Each GameServer instance must be reachable on its own port. Open the following on your firewall for each instance:

  • The client-facing ListenPort (TCP inbound) — players connect here
  • The internal InternalPort (TCP inbound from JoinServer host only) — keep this restricted to your internal network

If your server is behind NAT, add port-forwarding rules on your router for each ListenPort. The internal ports do not need to be forwarded unless the JoinServer runs on a separate physical machine outside your LAN.

For Windows Firewall via PowerShell, add a rule for each new instance:

# Add inbound rule for GameServer1 client port
New-NetFirewallRule -DisplayName "MU GameServer1 Client" `
    -Direction Inbound -Protocol TCP `
    -LocalPort 55902 -Action Allow

Starting and Monitoring Sub-Servers

Start your server processes in this order to avoid registration failures:

  1. DataServer
  2. JoinServer
  3. ConnectServer
  4. GameServer 0 (primary)
  5. GameServer 1 (Vine), then any additional instances

Each GameServer prints a registration confirmation line in its log when it successfully connects to the JoinServer:

[INFO] Connected to JoinServer at 127.0.0.1:55557
[INFO] Server registered → Code: 1, Name: Vine, Port: 55902
[INFO] Ready to accept player connections

Monitor these logs after startup to confirm all instances are registered before announcing the server as online.

Nota: If a GameServer fails to register (no confirmation line in the log), check that the JoinServer is running, that JoinServerIP and JoinServerPort are correct in the GameServer config, and that no firewall rule is blocking the internal port between the two processes.

Ongoing Maintenance and Tuning

Once sub-servers are running, revisit the configuration periodically:

  • Player distribution logs — Review joinserver.log weekly to see if one server consistently receives more connections. If imbalanced, check whether a popular map is pinned exclusively to one instance.
  • MaxPlayers ceiling — Adjust per-server caps as your community grows. Raising them too high without matching hardware causes packet queuing and visible lag.
  • Scheduled restarts — Each GameServer instance should be restarted on a regular schedule (most admins choose a nightly maintenance window). Stagger restarts by 5 minutes so players always have at least one available server.
  • Event hosting — Blood Castle, Devil Square, and Chaos Castle are typically hosted on one designated server. Announce which server runs events so players know where to be.

Proper sub-server configuration is the single most effective step for scaling a growing MU Online community. The architecture is designed for this use case — once your configurations are correct, adding a third or fourth instance follows the same pattern with minimal additional effort.

Perguntas frequentes

What is the maximum number of players a single GameServer process can handle?

A single GameServer (GameServer.exe) instance is typically limited to around 1,000 concurrent connections depending on your hardware and configuration. Beyond that, performance degrades noticeably — map instancing becomes sluggish and combat packets are delayed. For populations above 500 sustained players, splitting load across at least two sub-servers is strongly recommended.

Can sub-servers share the same database instance?

Yes. All GameServer processes point to the same JoinServer and DataServer, which in turn connect to the central SQL database. Sub-servers do not run independent databases — they are separate network processes that share a single data layer. This is what allows characters to move between servers without data loss.

What ports need to be opened for each additional sub-server?

Each GameServer instance requires its own unique port (commonly in the range 55900–55910 for the client-facing side) and an internal port for JoinServer communication. You must open these ports on your firewall and, if behind NAT, forward them on your router. Overlapping ports between instances will cause bind failures on startup.

How do I verify that load balancing is working correctly?

Connect to your server with multiple test accounts from different machines and check the player distribution in the GameServer logs (logs/gameserver.log). The JoinServer should distribute connections round-robin or by current load, depending on your configuration. You can also query the ConnectServer status page or log file to see per-server connection counts in real time.

VI

ViciadosMU Team

Equipe editorial do ViciadosMU — portal de MU Online no ar desde 2003.

Keep reading

Related articles