EnderCloud
Configure and operate

Server groups

Capacity, routing, matchmaking, retry, and timeout policy for a class of servers.

A server group defines traffic policy for one class of Minecraft server. Each .yml or .yaml file in this directory is parsed at orchestrator startup and synchronized to PostgreSQL.

There are two schemas:

  • hub groups run shared lobby servers and route players by current load.
  • minigame groups match queued parties into isolated sessions.

Invalid configuration stops orchestrator startup. The loader does not watch this directory, so restart the orchestrator after every change.

Common fields

Every group starts with these fields:

id: skywars-solo
type: minigame
enabled: true

variants:
  - id: sw-1s-dome
    enabled: true
    weight: 60
  - id: sw-1s-japan
    enabled: true
    weight: 40
FieldRequiredDefaultRules
idYesNoneUnique identifier with 2 to 63 lowercase letters, digits, or dashes
typeYesNonehub or minigame
enabledNotrueDisabled groups receive no new traffic
variantsYesNoneArray of final variant references. IDs cannot repeat inside a group
variants[].idYesNoneExisting final layer ID from templates/
variants[].enabledNotrueDisabled references remain synchronized but are not selected
variants[].weightYesNonePositive integer selection weight

An enabled group needs at least one enabled variant. A disabled group may have no enabled variants. The same final variant may belong to several groups with different weights.

Weights are relative, not percentages. EnderCloud first looks for variants that are underrepresented in the current warm pool. It uses weight when choosing among otherwise suitable variants. Equal weights give equal preference over time.

Capacity

All groups require the complete capacity block:

capacity:
  minimum_instances: 0
  maximum_instances: 20
  minimum_warm_instances: 1
  maximum_warm_instances: 4
FieldMinimumMeaning
minimum_instances0Floor for active physical instances in the group
maximum_instances1Strict ceiling for normal active physical instances
minimum_warm_instances0Floor for open instances that are ready or starting
maximum_warm_instances0Ceiling for open instances kept ahead of demand

The limits must satisfy:

minimum_instances <= maximum_instances
minimum_warm_instances <= maximum_warm_instances <= maximum_instances

Lifecycle states CREATING, STARTING, RUNNING, and DRAINING count as active for normal capacity. Warm pending instances are CREATING or STARTING with availability OPEN. Warm ready instances are RUNNING and OPEN.

During host maintenance, one replacement per group may temporarily exceed maximum_instances. This bounded surge prevents an open source instance from draining before its replacement is ready.

Common timeouts

Every group requires these six durations:

timeouts:
  startup: 90s
  drain: 15m
  cancelled_drain: 10s
  shutdown: 20s
  transfer: 20s
  player_stale: 30s

Group durations are positive integers followed by ms, s, m, or h. Values such as 500ms, 45s, and 4h are valid. Bare numbers and day suffixes are not valid in group YAML.

FieldStarts whenExpiry behavior
startupDocker has started and the instance enters STARTINGFail the instance if Paper has not sent SERVER_READY
drainNormal instance drain beginsStop the instance even if players are still observed
cancelled_drainA minigame session is cancelledBound active hub evacuation before forced stop
shutdownThe instance enters STOPPINGBound graceful Minecraft shutdown before forceful Docker cleanup
transferA transfer command is createdExpire the command and release session players that did not arrive
player_stalePaper last reports a player presentRemove the player from observed counts and mark session presence left

The orchestrator persists an absolute deadline when an operation starts. Editing the timeout does not alter deadlines already stored in PostgreSQL. See the timeout reference.

Hub groups

A hub group adds routing and may set timeouts.instance_lifetime:

id: hub
type: hub
enabled: true

variants:
  - id: hub
    enabled: true
    weight: 100

capacity:
  minimum_instances: 2
  maximum_instances: 5
  minimum_warm_instances: 2
  maximum_warm_instances: 4

routing:
  maximum_players_per_instance: 100
  target_players_per_instance: 70

timeouts:
  instance_lifetime: 4h
  startup: 90s
  drain: 5m
  cancelled_drain: 10s
  shutdown: 20s
  transfer: 20s
  player_stale: 30s
FieldRequiredDefaultRules
routing.maximum_players_per_instanceYesNonePositive integer and strict per-hub routing limit
routing.target_players_per_instanceYesNonePositive integer no greater than the maximum
timeouts.instance_lifetimeNo4hMaximum age before replacement and drain

The target is a soft aggregate scale-out threshold. When observed players reach the combined target capacity of running and starting hubs, the controller requests another hub up to maximum_instances. Players already above the target stay in place. New transfers still choose the least-loaded open hub until its strict maximum is reached.

When a running hub reaches its persisted lifetime deadline, EnderCloud starts one replacement using current variant selection. It drains the expired hub only after the replacement reports ready. If no capacity slot is available, the expired hub remains open. One renewal runs per group at a time.

Minigame groups

A minigame group adds a matchmaking policy and two timeouts:

id: skywars-solo
type: minigame
enabled: true

variants:
  - id: sw-1s-dome
    enabled: true
    weight: 100
  - id: sw-1s-japan
    enabled: true
    weight: 100

matchmaking:
  minimum_players: 4
  maximum_players: 12
  team_count: 12
  team_size: 1
  candidate_window: 20
  team_balance:
    minimum_players_per_team: 0
    maximum_team_spread: 1

capacity:
  minimum_instances: 0
  maximum_instances: 20
  minimum_warm_instances: 1
  maximum_warm_instances: 4

timeouts:
  startup: 90s
  drain: 15m
  cancelled_drain: 10s
  shutdown: 20s
  transfer: 20s
  player_stale: 30s
  instance_acquisition: 45s
  lobby_stale: 135s

Matchmaking fields

FieldRequiredDefaultRules
minimum_playersYesNonePositive integer no greater than maximum_players
maximum_playersYesNonePositive integer no greater than team_count * team_size
team_countYesNonePositive integer number of teams
team_sizeYesNonePositive integer maximum players in one team and maximum party size
candidate_windowNo20Positive integer number of oldest FIFO entries considered per tick
team_balance.minimum_players_per_teamNo0Integer from 0 through team_size
team_balance.maximum_team_spreadNoteam_sizeInteger from 0 through team_size

The matchmaker never splits a queue entry. A party larger than team_size is rejected before matchmaking. Feasible profiles must fit every atomic party, the player limits, and the balance rules.

minimum_players_per_team can require every used team to reach a floor. maximum_team_spread bounds the difference between the largest and smallest team in a feasible profile.

For a 4v4v4v4 mode that may start with eight players, requires at least one player per team, and allows a spread of at most two:

matchmaking:
  minimum_players: 8
  maximum_players: 16
  team_count: 4
  team_size: 4
  candidate_window: 32
  team_balance:
    minimum_players_per_team: 1
    maximum_team_spread: 2

Minigame timeouts

FieldRequiredStarts whenExpiry behavior
timeouts.instance_acquisitionYesA feasible session needs an instance but none is availableCancel the session if no instance can be reserved
timeouts.lobby_staleYesTransfers to the reserved instance beginCancel a lobby that never progresses to GAME_STARTING

The minigame plugin has sole authority to publish GAME_STARTING. EnderCloud has no partial-start timer. lobby_stale is a watchdog for an abandoned lobby, not permission for the orchestrator to start a game.

Variant references

Each referenced ID must resolve to one immediate directory under templates/ with a valid variant.yml. Because it is a final variant, that descriptor must declare a positive revision. Its ordered layer stack must resolve docker.image, docker.memory, and docker.cpu.

The group owns enabled and weight. Those fields are invalid inside variant.yml.

After changing effective template files or runtime settings:

  1. Increment the final variant revision.
  2. Restart the orchestrator.
  3. Confirm the new revision and checksum in the dashboard.
  4. Watch startup status before routing production traffic to it.

Existing instances keep their current materialized runtime. The revision affects new instance records.

Adding a group

  1. Copy the closest existing YAML file.
  2. Choose a unique ID and the correct type.
  3. Set all common capacity and timeout fields.
  4. Add routing for a hub or matchmaking plus its two timeouts for a minigame.
  5. Reference at least one complete final variant with a positive weight.
  6. Keep the group disabled until its server JAR, plugins, maps, and configuration are complete.
  7. Restart the orchestrator and check the configuration synchronization log.

The group filename does not define its ID. Use the same stem as the ID anyway, because it makes operator searches and reviews less error-prone.

Removed fields

The loader rejects the old lifecycle block, matchmaking timeout aliases, partial_start, and timeouts.ineligible_lobby. Use the canonical timeouts block and matchmaking.team_balance. Startup errors name the expected replacement.

On this page