# In-Depth Look

Learn more about Edgegap’s no-code matchmaker concepts in-depth and customize to your needs.

{% hint style="info" %}
If you need help, [please reach out to us over Discord](https://discord.gg/MmJf8fWjnt). For live games support see our [ticketing system](https://edgegap.atlassian.net/servicedesk/customer/portal/3).
{% endhint %}

## ✔️ Introduction

Matchmaking in match-based games generally aims to:

* **find other players** based on criteria like region, latency, skill, or game parameters;
* **search for servers** to join based on available capacity \[or ping, region, skill, map, mode];
* **start new server** if existing servers are full or don't satisfy player criteria.

Player experience comes first, defining our core objectives:

* high match fill rate and social feature integration (play with friends in groups),
* fast matches with controlled match quality (low latency, shared preferences),
* reliable and predictable matchmaking process with global availability.

{% hint style="success" %}
Alternatively, let players **pick a persistent (always online) server** from a list with [server-browser](https://docs.edgegap.com/learn/server-browser "mention").
{% endhint %}

**Get started within 5 minutes and test the all features for free, no credit card required.**

Upgrade when you're ready for a more powerful, private (dedicated) cluster. Native integration with Edgegap [deployments](https://docs.edgegap.com/learn/orchestration/deployments "mention") delivers best-in-class ping no matter where your players are located.

{% hint style="info" %}
Free Tier allows 3 hours of runtime after each restart. Your matchmaker will run on shared infrastructure with limited resources, suitable for testing. **After your public release, matchmaker needs to run 24/7.**
{% endhint %}

There are three essential concepts to each Matchmaker:

* [#hosting-cluster](#hosting-cluster "mention") - underlying server infrastructure, fully managed and operated by Edgegap.
* [#configuration](#configuration "mention") - set of rules and settings which define how the matchmaker operates.
* 🌐 Service Instance **-** live matchmaking service running 24/7 on the Cluster, using Configuration to match players together and produce deployment (server) assignments.

{% hint style="success" %}
[Update your matchmaker version frequently](#changelog) to **take advantage of new features and bug fixes.**
{% endhint %}

## ▶️ Start Matchmaking

Learn about matchmaking process to customize, troubleshoot, and optimize your game integration.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fm4U4bgwucX6iGEieMISU%2Fimage.png?alt=media&#x26;token=39b97619-700b-4062-9dd9-73fa08fb96c2" alt=""><figcaption><p>Matchmaking Sequence</p></figcaption></figure>

1. [Authenticate Player](#authenticate) - prevents pirated copies from playing online,
2. [Create Lobby](#create-group) - join up with your friends and share player/match preferences,
3. [**Group Up**](#group-up) **- register your Lobby as a Matchmaking Group,**
4. [**Find Match**](#find-match) **- get ready and start looking for a match (new or existing),**
   1. Assign Server & Inject Tickets - server is automatically assigned after a few seconds,
5. [**Connect and Authenticate**](#connect-to-server) **- attempt secure connection to game server,**
   1. Confirm Identity - server verifies identity of game client using third party tokens,
   2. Accept Player or Kick Player - server decides if the player is allowed to join.

**Get started quickly - add our matchmaking starter sample to your game**:

* Unreal Engine [#integration-kit](https://docs.edgegap.com/unreal-engine/developer-tools#integration-kit "mention"):
  * [install from Fab Marketplace](https://www.fab.com/listings/ff17ad88-12a1-49cf-9a41-31695ed11e16) (free for Personal use),
  * [import simple example blueprint](https://blueprintue.com/blueprint/m33u1okj/) and customize to your needs.
* Unity [#matchmaking-sdk](https://docs.edgegap.com/unity/developer-tools#matchmaking-sdk "mention") by Edgegap:
  * [install package using Unity Package Manager for free](https://github.com/edgegap/edgegap-unity-gen2-sdk?tab=readme-ov-file#install-with-git-recommended),
  * [import simple example script](https://github.com/edgegap/edgegap-unity-gen2-sdk?tab=readme-ov-file#import-simple-example) and customize for your needs,

### Authenticate

All requests must send an `Authorization`  HTTP header with your secret **Auth Token:**

<pre><code>Authorization: <a data-footnote-ref href="#user-content-fn-1">xxxxxxxx-e458-4592-b607-c2c28afd8b62</a>
</code></pre>

{% hint style="warning" %}
**Keep your tokens secret and safe! Edgegap staff will never ask you for your tokens.**
{% endhint %}

{% hint style="success" %}
**This token may be included in your game client safely, as it doesn't grant access to Edgegap API.**
{% endhint %}

Individual players can be identified using their ticket ID, available on clients and server. Optionally, add custom authentication or limits with a custom proxy using [#server-to-server-api](#server-to-server-api "mention") API.

### Group Up

Creating a Group (party) ensures players join the same team and server with their friends.

{% hint style="success" %}
Create a group marked ready to [#find-match](#find-match "mention") quickly as a **solo player without group members**.
{% endhint %}

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FzPLV5ap16XQn0vYZsnQb%2FUntitled-2024-10-29-1018.png?alt=media&#x26;token=aa7165ab-abe1-4f7d-a84e-d2c4af8f36ad" alt=""><figcaption><p>Group Lifecycle Activity Diagram</p></figcaption></figure>

#### Lobby and Group

Use a Lobby service if your game design requires setting player-controlled matchmaking preferences (e.g. character choice, difficulty, map, etc.). As players join and leave the Lobby, they also update the matchmaking Group to prepare for finding a match later.

{% hint style="success" %}
**Don't have time for a Lobby service?** Guide players to share group IDs through Discord or DMs.
{% endhint %}

<table><thead><tr><th width="390">Game Design - Feature / Requirement</th><th>Pre-Match Lobby</th><th>Matchmaker Group</th></tr></thead><tbody><tr><td><a data-footnote-ref href="#user-content-fn-2">invite friends to play with me</a></td><td>✅</td><td>✅</td></tr><tr><td>modify my player/match preferences</td><td>✅</td><td>❌</td></tr><tr><td>view other lobby member preferences</td><td>✅</td><td>❌</td></tr><tr><td>store and manage custom key-value data</td><td>✅</td><td>❌</td></tr><tr><td>notify group members that I'm ready to play</td><td>❌</td><td>✅</td></tr><tr><td>show matchmaking progress and find match</td><td>❌</td><td>✅</td></tr><tr><td>get team assignment for a player/group</td><td>❌</td><td>✅</td></tr><tr><td>retrieve game server connection details</td><td>❌</td><td>✅</td></tr></tbody></table>

Our cross-platform matchmaker supports all commercial and custom Lobby services:

<table><thead><tr><th>Lobby Service (Third Party)</th><th width="120" data-type="checkbox">Unreal Engine</th><th width="75" data-type="checkbox">Unity</th><th width="50" data-type="checkbox">PC</th><th width="90" data-type="checkbox">Consoles</th><th width="65" data-type="checkbox">VR/XR</th><th width="100" data-type="checkbox">Mobile</th></tr></thead><tbody><tr><td><a href="https://dev.epicgames.com/docs/game-services/lobbies-and-sessions/lobbies/lobbies-intro">Epic Online Services Lobby</a><br>(Epic Games)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr><tr><td><a href="https://partner.steamgames.com/doc/features/multiplayer/matchmaking#friends">Steamworks Lobby</a><br>(Valve Corporation)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr><tr><td><a href="https://heroiclabs.com/docs/nakama/concepts/groups/">Nakama Group</a><br>(Heroic Labs)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr><tr><td><a href="https://learn.microsoft.com/en-us/gaming/playfab/community/associations/groups/quickstart">Playfab Lobby</a><br>(Microsoft)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr><tr><td><a href="https://docs.braincloudservers.com/learn/key-concepts/multiplayer/lobbies/#lobby-experience">brainCloud Lobby</a><br>(bitHeads)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr><tr><td><a href="https://developer.apple.com/documentation/gamekit/connecting-players-with-their-friends-in-your-game">Gamekit Friends</a><br>(Apple)</td><td>true</td><td>true</td><td>false</td><td>false</td><td>false</td><td>true</td></tr><tr><td>Custom Lobby<br>(your company)</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td><td>true</td></tr></tbody></table>

Lobby owner (the player sending invites) must also create the matchmaking group.

**Store your group's ID in your shared lobby data**, so other lobby members can easily find and join a matchmaking group associated with the third party lobby. Players invited to the group use the group ID to **create their memberships (join)**, and to **securely store their** [**matchmaking attributes**](#matchmaking-rules)**.**

{% hint style="warning" %}
**Once a group starts matchmaking, it can't be joined.** [#abandon-queue](#abandon-queue "mention") and create a new one.
{% endhint %}

#### Ping Optimization

If [#configuration](#configuration "mention") includes [`latencies` rule](#rule-example-elo_rating) all group members send their [ping-beacons](https://docs.edgegap.com/learn/orchestration/ping-beacons "mention") measurements to **prevent matching players in distant regions** or much higher/lower ping (latency).

{% code title="Example Game Client Ping Measurements in milliseconds" %}

```json
{
  "Chicago": 224.4,
  "Frankfurt": 23.2,
  "Tokyo": 167.4
}
```

{% endcode %}

#### **Abandon Queue**

Group owner may delete the group, automatically deleting all group memberships. Deleting the group after matchmaking started will cancel all memberships, and delete them shortly after.

Group members (besides owner) may delete their memberships (leave group) at any time before [#find-match](#find-match "mention"). Deleting a membership afterwards will cancel matchmaking for the whole group.

{% hint style="info" %}
Once matchmaking is cancelled, members are [removed from matchmaking automatically](#matchmaking-profiles) and notified through membership `status:CANCELLED`  in their next status polling response.
{% endhint %}

Once cancelled, if the group wishes to restart matchmaking, the group owner must re-create the group, share the new group ID to members, and have them re-create their memberships.

**Once a match is found, group can't be deleted** (`409 Conflict`), and will be [removed automatically](#connect-to-server). Your server should allow some time (e.g. 60s) for players to connect before assuming a player abandoned. In such case your server may:

* replace the leaver with an AI character to immediately start the match,
* or create a [backfill](#backfill-match) to find a new player to replace the leaver,
* or proceed without replacing the leaver, if your game design permits variable player count.

### Find Match

To start looking for a match, all members and the owner must mark themselves ready.

{% hint style="success" %}
To let the group owner **start matchmaking immediately, mark memberships ready at creation**. Once the owner marks themselves ready the matchmaking will start, since everyone is ready.
{% endhint %}

{% hint style="info" %}
For best experience, **provide status updates to players using in-game UI**.
{% endhint %}

**All players must poll their membership in regular intervals** (recommended 3-5s) to detect when matchmaking starts, and to communicate matchmaking progress through in-game UI.

Players should **save their membership and group IDs persistently**, so that in case of game client crash they can restart the game and resume without losing matchmaking progress.

Once we find enough players to put into the same team adhering to your [#matchmaking-rules](#matchmaking-rules "mention"), players will be notified in their membership response with `status:TEAM_FOUND`.

Deleting a membership at this stage will result in all group memberships being cancelled and all other groups' assigned to the same team returning to `status:SEARCHING` .

Teams continue matchmaking with other teams using the overlapping values across their groups (or average in case of `number_difference` ) until enough teams are assembled. Memberships indicate this with response  `status:MATCH_FOUND` , which means your [deployment is being started](https://docs.edgegap.com/orchestration/deployments#id-1.-start-a-deployment).

**Matchmaker aims to maximize match fill rate, and will not proceed to `MATCH_FOUND` until either:**

1. enough teams are matched with the configured maximum team size,
2. or if [#rule-expansion](#rule-expansion "mention") defined AND expansion time reached, AND enough teams are matched with the configured minimum team size,
3. or the configured ticket expiration time elapsed AND enough teams are matched with the configured minimum team size.

If neither scenario succeeds before configured ticket expiration, group and tickets are cancelled.

{% hint style="info" %}
Experiencing long queue times during testing, or with players in less popular regions? Set a lower ticket expiration period (e.g. 30s) and re-create group (or tickets) on client side upon expiration.
{% endhint %}

Ticket expiration automatically resets whenever a group (or a player) is matched to a team.

{% hint style="success" %}
Store `team_id`  and `match_id` in your game backend to display team members information in-game.
{% endhint %}

{% hint style="info" %}
Every player receives a **unique Ticket ID, which can be used to** [#authenticate](#authenticate "mention") **with game servers.**
{% endhint %}

If the player has been matched and assigned to a game server, their ticket is deleted automatically. Players who abandon queue after `status:HOST_ASSIGNED`  can be replaced with [backfill](#backfill-match).

Once players receive `status:HOST_ASSIGNED`  they proceed to [#connect-to-server](#connect-to-server "mention").

### Connect to Server

A few seconds after finding a match, memberships proceed to `status:HOST_ASSIGNED`  indicating that your [deployment is now ready and your game server is initializing](https://docs.edgegap.com/orchestration/deployments#id-3.-deployment-ready).

Each player reads their `ticket_id`  and  `assignment`  and attempt connection using the [**FQDN**](#user-content-fn-3)[^3] **(deployment URL)** and the **External Port**. Your game server may be still initializing at this time, so **players must retry connection several times**, until exceeding your usual server initialization time:

{% tabs %}
{% tab title="Unreal Engine" %}
To **connect from PIE (Editor)** during development and testing, press the tilde key `~`  and type `open {URL}:{port}`  and wait for your editor to load the map.

To **connect from a game client build** (and in live production environment) try

* Unreal Engine [⚡ Integration Kit](https://docs.edgegap.com/learn/unreal-engine-games/developer-tools#integration-kit):
  * [install from Fab Marketplace](https://www.fab.com/listings/ff17ad88-12a1-49cf-9a41-31695ed11e16) (free for Personal use),
  * [import simple example blueprint](https://blueprintue.com/blueprint/m33u1okj/) and customize to your needs.

{% hint style="success" %}
In case of failed connections or dark screen consult our [troubleshooting guide](https://docs.edgegap.com/unreal-engine#troubleshooting-and-faq).
{% endhint %}
{% endtab %}

{% tab title="Unity" %}
To **connect your Unity Editor** or **game client** to your cloud deployment, input:

* **Deployment** **URL** pointing to the server's IP, usually in `NetworkManager` component.
* **External port** mapping to the [server's internal listen port](https://docs.edgegap.com/orchestration/application-and-versions#port-mapping), usually in a Transport component.

{% hint style="success" %}
In case of connection timeout or other issues consult our [troubleshooting guide](https://docs.edgegap.com/unity#troubleshooting-and-faq-4).
{% endhint %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
We do not ask players to confirm the match, as we aim to provide shortest possible time to gameplay, high match fill rate, and minimize queue dodging and match cancellations.
{% endhint %}

Players should **save their assignment ID persistently between game restarts**, so that in case of game client crash they can retrieve the connection details and attempt reconnecting.

{% hint style="success" %}
See [](https://docs.edgegap.com/learn/matchmaking "mention") and our SDKs with automatic reconnect function and more.
{% endhint %}

### Backfill Match

Optionally, some games may have special matchmaking needs, such as:

* allow new players to join games in progress (friends or "randoms"),
* replace players who abandon (leavers) after server starts to avoid restarting match,
* allow spectators to join and observe tournament or friends’ matches (e-sports),
* centralize players in larger servers to provide more social interactions (MMOs).

Backfill is a **server-owned ticket representing players currently connected to the server.** This ensures newly added players will respect your matchmaking rules when matched with current players.

{% hint style="warning" %}
[#backfill-match](#backfill-match "mention") to replace Seat/Match sessions. Matchmaker only supports Default session.
{% endhint %}

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FJhdLAJYga4z2sjxLj6LM%2FUntitled-2024-10-29-1018.png?alt=media&#x26;token=279136ba-a15e-46b0-a119-8419b8aca044" alt=""><figcaption><p>Backfill Scenarios Visualized</p></figcaption></figure>

{% hint style="success" %}
**Backfills ignore** `player_count`  **rule, and always match exactly one group**. `backfill_group_size`  controls team capacity with round-robin strategy, filling teams evenly in a controlled manner.
{% endhint %}

**The steps to complete a successful backfill are:**

1. Server creates one Backfill per team missing players, using values from:
   * Real `assignment`  data retrieved from [#injected-environment-variables](https://docs.edgegap.com/orchestration/deployments#injected-environment-variables "mention") (deployment).
   * Currently connected players' `tickets`:
     * from [#injected-variables](#injected-variables "mention") (matchmaker), previous backfills' `assigned_ticket` response, or mock data manipulated to match specific players,
     * replace `backfill_group_size`  values with possible group sizes [up to available capacity](#user-content-fn-4)[^4],
2. Game clients create new tickets (memberships) and include `backfill_group_size`  values:
   * `"1"`  if the player is matchmaking alone.
   * [`"2"`  if the player is a part of a matchmaking group with 2x members total](#user-content-fn-5)[^5].
   * `"new"`  if players enabled starting new games in addition to joining in-progress games.
3. Game clients proceed to [#find-match](#find-match "mention") and pair players with the matching backfill.
4. If the backfilled group didn't completely fill the team, the server may repeat this process with the newly backfilled players' tickets, to add more players and reach desired team sizes.

{% hint style="info" %}
To create a Backfill-only profile, set `min_team_size`  to 999,999 and disable ticket + ticket matches.
{% endhint %}

<details>

<summary>🥛 Backfill Example (Backfill Showcase)</summary>

```json
{
  "profile": "backfill-example",
  "attributes": {
    "assignment": {
      "request_id": "cd28e6c66554",
      "fqdn": "cd28e6c66554.pr.edgegap.net",
      "public_ip": "192.168.2.14",
      "ports": {
        "game": {
          "internal": 7777,
          "external": 56890,
          "link": "cd28e6c66554.pr.edgegap.net:56890",
          "protocol": "UDP"
        },
        "web": {
          "internal": 22,
          "external": 57440,
          "link": "cd28e6c66554.pr.edgegap.net:57440",
          "protocol": "TCP"
        },
        "server": {
          "internal": 80,
          "external": 50110,
          "link": "cd28e6c66554.pr.edgegap.net:50110",
          "protocol": "TCP"
        }
      },
      "location": {
        "city": "Montreal",
        "country": "Canada",
        "continent": "North America",
        "administrative_division": "Quebec",
        "timezone": "America/Toronto"
      }
    }
  },
  "tickets": {
    "c3d057h5h6f7j889fk43": {
      "player_ip": "174.25.48.238",
      "attributes": {
        "beacons": {
          "New York": 12.2,
          "Los Angeles": 45.3,
          "Paris": 78.3
        },
        "backfill_group_size": [
          "2",
          "1"
        ]
      },
      "group_id": "192bb97e-7fd6-4d86-8ce4-61c53c9fef16",
      "id": "c3d057h5h6f7j889fk43",
      "created_at": "2024-08-20T13:38:05.251393+00:00"
    },
    "cqg0bg9583s738h9dkf6": {
      "player_ip": "217.34.85.142",
      "attributes": {
        "beacons": {
          "New York": 21.0,
          "Los Angeles": 30.2,
          "Paris": 101.1
        },
        "backfill_group_size": [
          "2",
          "1"
        ]
      },
      "group_id": "aea7df3c-d391-4ea3-a3ec-dded422fe7c8",
      "id": "cqg0bg9583s738h9dkf6",
      "created_at": "2024-08-20T13:38:05.251393+00:00"
    }
  },
  "assigned_ticket": null
}
```

</details>

<details>

<summary>🥛 Backfill Assignment Example (Backfill Showcase)</summary>

```json
{
  "profile": "backfill-example",
  "attributes": {
    "assignment": {
      "request_id": "cd28e6c66554",
      "fqdn": "cd28e6c66554.pr.edgegap.net",
      "public_ip": "192.168.2.14",
      "ports": {
        "game": {
          "internal": 7777,
          "external": 56890,
          "link": "cd28e6c66554.pr.edgegap.net:56890",
          "protocol": "UDP"
        },
        "web": {
          "internal": 22,
          "external": 57440,
          "link": "cd28e6c66554.pr.edgegap.net:57440",
          "protocol": "TCP"
        },
        "server": {
          "internal": 80,
          "external": 50110,
          "link": "cd28e6c66554.pr.edgegap.net:50110",
          "protocol": "TCP"
        }
      },
      "location": {
        "city": "Montreal",
        "country": "Canada",
        "continent": "North America",
        "administrative_division": "Quebec",
        "timezone": "America/Toronto"
      }
    }
  },
  "tickets": {
    "c3d057h5h6f7j889fk43": {
      "player_ip": "174.25.48.238",
      "attributes": {
        "beacons": {
          "New York": 12.2,
          "Los Angeles": 45.3,
          "Paris": 78.3
        },
        "backfill_group_size": [
          "2",
          "1"
        ]
      },
      "group_id": "192bb97e-7fd6-4d86-8ce4-61c53c9fef16",
      "id": "c3d057h5h6f7j889fk43",
      "created_at": "2024-08-20T13:38:05.251393+00:00"
    },
    "cqg0bg9583s738h9dkf6": {
      "player_ip": "217.34.85.142",
      "attributes": {
        "beacons": {
          "New York": 21.0,
          "Los Angeles": 30.2,
          "Paris": 101.1
        },
        "backfill_group_size": [
          "2",
          "1"
        ]
      },
      "group_id": "aea7df3c-d391-4ea3-a3ec-dded422fe7c8",
      "id": "cqg0bg9583s738h9dkf6",
      "created_at": "2024-08-20T13:38:05.251393+00:00"
    }
  },
  "assigned_ticket": {
    "profile": "backfill-example",
    "player_ip": "244.13.201.244",
    "attributes": {
      "beacons": {
        "New York": 30.2,
        "Los Angeles": 10.5,
        "Paris": 123.9
      },
      "backfill_group_size": [
        "new",
        "1"
      ]
    },
    "id": "cqg0bg550h7uujd77khg",
    "group_id": "e0cf41c0-f88f-456e-a032-03b1d6821a9a",
    "created_at": "2024-08-20T13:38:08.251393+00:00",
    "status": "HOST_ASSIGNED"
  }
}
```

</details>

{% hint style="info" %}
See [Mirror Seat Management](https://docs.edgegap.com/docs/sample-projects/mirror-on-edgegap#bonus-seat-sessions-management) and [FishNet Seat Management](https://docs.edgegap.com/docs/sample-projects/fishnet-on-edgegap#bonus-seat-sessions-management) for **player connection monitoring**.
{% endhint %}

Once game server initialization concludes, **your server should**:

* **Start abandonment timer for each new player.** We recommend indicating loading progress to connected players with a loading scene/level - either a full fledged 3D scene, a lobby-like social UI, or a loading screen with a progress bar.
* **Keep track of new player connections or existing players leaving over time**:
  1. New players must announce ticket ID to the server for authentication and to map their connection to matchmaker [#injected-variables](#injected-variables "mention") or `assigned_ticket` (if backfilled).
  2. Create new Backfills for unused player capacity (leavers) throughout server lifespan.
  3. Renew expired Backfills, which are deleted after `ticket_expiration_period`.
* **Clean up (delete) any leftover Backfills** once the [#id-5.-deployment-stopped](https://docs.edgegap.com/orchestration/deployments#id-5.-deployment-stopped "mention"):
  * Unity - [`OnApplicationQuit`](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MonoBehaviour.OnApplicationQuit.html) callback or custom game end callback,
  * Unreal Engine - [`OnWorldDestroyed`](https://forums.unrealengine.com/t/call-function-before-quit-game/344954/2) , [`PreExit`](https://forums.unrealengine.com/t/event-on-close/298087/2) , or a custom game end callback.

{% hint style="info" %}
Use [GetEnvironmentVariable in C#](https://learn.microsoft.com/en-us/dotnet/api/system.environment.getenvironmentvariable?view=net-8.0) or [GetEnvironmentVariable in C++](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Core/GenericPlatform/FGenericPlatformMisc/GetEnvironmentVariable) to get variable values.
{% endhint %}

Any profile can be used for Backfill as long as valid server assignment and at least one ticket is provided. See [#backfill-showcase](https://docs.edgegap.com/learn/matchmaking/..#backfill-showcase "mention") for a minimal example.

## ⚙️ Configuration

Matchmaker API is generated from a JSON configuration specified when you create a new (or quick-restart) Matchmaker. You may specify any number of profiles with varying rules and expansions:

{% hint style="success" %}
See [](https://docs.edgegap.com/learn/matchmaking "mention") for our SDKs and detailed example scenarios.
{% endhint %}

<details>

<summary>🍀 Simple Example (Minimal Recommended Configuration)</summary>

<pre class="language-json"><code class="lang-json">{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "simple-example": {
      "ticket_expiration_period": "5m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m",
      "application": {
        "name": "<a data-footnote-ref href="#user-content-fn-6">my-game-server</a>",
        "version": "<a data-footnote-ref href="#user-content-fn-7">2024.01.30-16.23.00-UTC</a>"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 1,
              "min_team_size": 2,
              "max_team_size": 2
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 100,
              "max_latency": 200
            }
          }
        },
        "expansions": {}
      }
    }
  }
}
</code></pre>

</details>

<details>

<summary>🏁 Advanced Example (Complete Example Configuration)</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "allowed_cors_origins": [
    "https://*.my-game-server.com"
  ],
  "profiles": {
    "advanced-example": {
      "ticket_expiration_period": "5m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m"
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 1,
              "min_team_size": 4,
              "max_team_size": 4
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 125,
              "max_latency": 125
            }
          },
          "elo_rating": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 50
            }
          },
          "selected_game_mode": {
            "type": "string_equality"
          },
          "selected_map": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "backfill_group_size": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "30": {
            "elo_rating": {
              "max_difference": 150
            },
            "beacons": {
              "difference": 125,
              "max_latency": 250
            }
          },
          "60": {
            "elo_rating": {
              "max_difference": 200
            }
          },
          "180": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 1,
              "max_team_size": 4
            },
            "beacons": {
              "difference": 99999,
              "max_latency": 99999
            }
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary>🎾 Custom Lobby Example</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "custom-lobby-example": {
      "ticket_expiration_period": "3m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m"
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 1,
              "min_team_size": 4,
              "max_team_size": 4
            }
          },
          "lobby_id": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "10": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 1,
              "max_team_size": 4
            }
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="1f95b">🥛</span> Backfill Configuration Example</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "backfill-example": {
      "ticket_expiration_period": "30s",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m",
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 1,
              "min_team_size": 4,
              "max_team_size": 4
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 100,
              "max_latency": 200
            }
          },
          "backfill_group_size": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {}
      }
    }
  }
}
```

</details>

<details>

<summary>⚔️ Competitive Game Example</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "casual-example": {
      "ticket_expiration_period": "5m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m",
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "min_team_size": 5,
              "max_team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 125,
              "max_latency": 150
            }
          },
          "selected_maps": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "backfill_group_size": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "30": {
            "beacons": {
              "difference": 125,
              "max_latency": 250
            }
          },
          "180": {
            "beacons": {
              "difference": 99999,
              "max_latency": 99999
            }
          }
        }
      }
    },
    "competitive-example": {
      "ticket_expiration_period": "5m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m"
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "min_team_size": 5,
              "max_team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 125,
              "max_latency": 150
            }
          },
          "versus_ranks": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "120": {
            "beacons": {
              "difference": 125,
              "max_latency": 250
            }
          }
        }
      }
    },
    "challenger-example": {
      "ticket_expiration_period": "5m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m"
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 2,
              "min_team_size": 5,
              "max_team_size": 5
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 125,
              "max_latency": 150
            }
          },
          "elo_rating": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 50
            }
          }
        },
        "expansions": {
          "120": {
            "beacons": {
              "difference": 125,
              "max_latency": 250
            }
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary>🤝 Cooperative Game Example</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "cooperative-example": {
      "ticket_expiration_period": "3m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m",
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "type": "player_count",
            "attributes": {
              "team_count": 1,
              "min_team_size": 4,
              "max_team_size": 4
            }
          },
          "beacons": {
            "type": "latencies",
            "attributes": {
              "difference": 125,
              "max_latency": 150
            }
          },
          "selected_difficulty": {
            "type": "string_equality"
          },
          "selected_map": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "player_level": {
            "type": "number_difference",
            "attributes": {
              "max_difference": 10
            }
          },
          "backfill_group_size": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "moderation_flags": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "30": {
            "beacons": {
              "difference": 125,
              "max_latency": 250
            },
            "player_level": {
              "max_difference": 20
            }
          },
          "60": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 2,
              "max_team_size": 4
            }
          },
          "150": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 1,
              "max_team_size": 4
            }
          }
        }
      }
    }
  }
}
```

</details>

<details>

<summary>🎈 Social Game Example</summary>

```json
{
  "version": "3.2.2",
  "inspect": true,
  "max_deployment_retry_count": 3,
  "profiles": {
    "social-example": {
      "ticket_expiration_period": "3m",
      "ticket_removal_period": "1m",
      "group_inactivity_removal_period": "5m",
      "application": {
        "name": "my-game-server",
        "version": "2024.01.30-16.23.00-UTC"
      },
      "rules": {
        "initial": {
          "match_size": {
            "attributes": {
              "team_count": 1,
              "min_team_size": 50,
              "max_team_size": 50
            },
            "type": "player_count"
          },
          "beacons": {
            "attributes": {
              "difference": 125,
              "max_latency": 150
            },
            "type": "latencies"
          },
          "selected_mode": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "backfill_group_size": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          },
          "moderation_flags": {
            "type": "intersection",
            "attributes": {
              "overlap": 1
            }
          }
        },
        "expansions": {
          "15": {
            "beacons": {
              "difference": 125,
              "max_latency": 250
            },
            "match_size": {
              "team_count": 1,
              "min_team_size": 20,
              "max_team_size": 50
            }
          },
          "30": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 10,
              "max_team_size": 50
            }
          },
          "150": {
            "match_size": {
              "team_count": 1,
              "min_team_size": 1,
              "max_team_size": 50
            }
          }
        }
      }
    }
  }
}
```

</details>

{% hint style="warning" %}
Editing a running matchmaker will **trigger a quick reload**, deleting all tickets and causing short downtime.
{% endhint %}

<details>

<summary><code>The application configuration is not valid for profile XYZ.</code></summary>

* We couldn’t find your [application-and-versions](https://docs.edgegap.com/learn/orchestration/application-and-versions "mention"), please verify `application`  values.

</details>

<details>

<summary><code>Docker image for '2024.01.30-16.23.00-UTC' is not cached.</code></summary>

[**🌟 Upgrade to Pay as You Go tier**](https://app.edgegap.com/user-settings?tab=memberships) **to unlock** [**instant deployments with Caching**](https://docs.edgegap.com/orchestration/deployments#id-1.-start-a-deployment)**.**

* 4GB+ uncached images may take longer to deploy, resulting in [#id-4.-deployment-error](https://docs.edgegap.com/orchestration/deployments#id-4.-deployment-error "mention"). Consider optimizing your server image size ([Unreal Engine](https://docs.edgegap.com/unreal-engine#optimize-server-build-size) / [Unity](https://docs.edgegap.com/unity#optimize-server-build-size)).
* You may proceed anyway, though we recommend testing your deployment time.

</details>

### Profiles (Queues) <a href="#matchmaking-profiles" id="matchmaking-profiles"></a>

Profiles represent entirely separated matchmaking queues, sharing the same matchmaker version. You can **configure any number of profiles for each matchmaker.** Splitting up your player base in multiple profiles may result in longer queue times for your players.

Each matchmaker profile uses an [App Version](https://docs.edgegap.com/learn/orchestration/application-and-versions) as a template to start new deployments (servers).

{% hint style="success" %}
Some game modes may require more vCPU / RAM, especially if they support a higher number of players. Each **matchmaker may include multiple profiles**, each linked to an app version with adjusted resources.
{% endhint %}

### Rules <a href="#matchmaking-rules" id="matchmaking-rules"></a>

Every player and group joins matchmaking queue and finds matches using  `initial` rules at first.

Each entry in profile at path `.rules.initial` represents a rule, where:

* **key** is a string value to name the rule however you prefer; e.g. `match_size` , and
* **value** is an object defining the type and attributes of the rule, adhering to our standard ruleset.

{% hint style="info" %}
All rules have to be met simultaneously to initiate host assignment and start or find a deployment.
{% endhint %}

**Operators (Rule Type)**

**`player_count`** is a special rule defining how many players need to match to initiate assignment.

{% hint style="warning" %}
Rule `player_count`  **is required and may only be defined once** in your initial configuration rules.
{% endhint %}

Matchmaker always strives to maximize match fill rate, up to specified `max_team_size` :

1. if max team size is reached the match is made immediately,
2. otherwise, players wait in queue to fill match until [expansion](#rule-expansion) (or expiration) is about to elapse,
3. shortly before [expanding](#rule-expansion) (or expiring), if partial match is possible (≥ min and < max team size), this match will be made with all players in same expansion stage (assuming other rules pass).

{% hint style="success" %}
For cooperative, free-for-all, or asymmetric team size game modes, set your `"team_count": 1` .
{% endhint %}

Team count may be configured to compose multiple balanced teams for competitive games:

* **group attributes are calculated as average/overlap** of the group's **player attributes,**
* **team attributes are calculated as average/overlap** of the team's **group attributes.**

Assuming a fixed team size of 4 players:

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FSFEzBayk3XNEUTIynjNd%2Fimage.png?alt=media&#x26;token=11a43fdf-3a99-4caa-8d2b-639e0f3cc06f" alt=""><figcaption><p>Example Match Scenarios</p></figcaption></figure>

{% hint style="info" %}
**Groups match in teams without overfilling,** only if a team has sufficient capacity to fit the whole group.
{% endhint %}

**`string_equality`** matches players with the exact same string value.

<details>

<summary>Rule Example: <code>selected_game_mode</code></summary>

`selected_game_mode`  rule will match players case sensitively:

:white\_check\_mark: Alice + Bob + Dave may match,

:x: Alice + Erin, or Charlie + Frank will never match.

| "Free For All" | "Capture The Flag" | "capture the flag" |
| -------------- | ------------------ | ------------------ |
| Alice          | Erin               | Frank              |
| Bob            | Charlie            |                    |
| Dave           |                    |                    |

</details>

**`number_difference`** matches players within the absolute numerical difference from each other.

<details>

<summary>Rule Example: <code>elo_rating</code></summary>

`elo_rating`  rule above with `"max_difference": 50` initially:

:white\_check\_mark: Alice + Bob may match, or Bob + Charlie may match,

:x: Alice + Bob + Charlie will never match.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FxtTxZ7TPSa8a8WtnGNAE%2Fimage.png?alt=media&#x26;token=c48b843b-c4e5-47bf-9154-07254a788067" alt=""><figcaption></figcaption></figure>

</details>

**`latencies`** is a special rule optimizing the ping of player matches:

* reduce client-server latency by removing regions with high latency (above threshold),
* improve match fairness by grouping players with similar latency (below difference).

<details>

<summary>Rule Example: <code>beacons</code></summary>

`beacons` rule configured with `"difference": 100, "max_latency": 200`  will match:

:white\_check\_mark: Alice and Bob may match:

* Tokyo is discarded (>200 ms),
* latency for Chicago within 100 ms absolute difference.

<table><thead><tr><th width="180">Beacon City</th><th width="80">Match</th><th width="132">abs(A - B) [ms]</th><th width="164">Alice [ms]</th><th width="164">Bob [ms]</th></tr></thead><tbody><tr><td>Chicago</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span></td><td>75.0</td><td>12.3</td><td>87.3</td></tr><tr><td>Los Angeles</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><a data-footnote-ref href="#user-content-fn-8">113.2</a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td>145.6</td><td>32.4</td></tr><tr><td><del>Tokyo</del></td><td>n/a</td><td>n/a</td><td><a data-footnote-ref href="#user-content-fn-9"><del>233.2</del></a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><a data-footnote-ref href="#user-content-fn-9"><del>253.2</del></a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr></tbody></table>

:x: Alice and Charlie will never match:

* no beacons have < 200 ms latency for both players,
* Alice lives in North America - Illinois,
* Charlie lives in Asia - Japan.

<table><thead><tr><th width="180">Beacon City</th><th width="80">Match</th><th width="132">abs(A - B) [ms]</th><th width="164">Alice [ms]</th><th width="164">Charlie [ms]</th></tr></thead><tbody><tr><td><del>Chicago</del></td><td>n/a</td><td>n/a</td><td>12.3</td><td><a data-footnote-ref href="#user-content-fn-9"><del>215.6</del></a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td><del>Los Angeles</del></td><td>n/a</td><td>n/a</td><td>145.6</td><td><a data-footnote-ref href="#user-content-fn-9"><del>238.3</del></a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td><del>Tokyo</del></td><td>n/a</td><td>n/a</td><td><a data-footnote-ref href="#user-content-fn-9"><del>233.2</del></a> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td>24.2</td></tr></tbody></table>

</details>

{% hint style="warning" %}
Rule `latencies`  is **optional and may only be defined once in your initial configuration** rules.
{% endhint %}

Some players with high ping to all beacons due to ISP[^10] issues or slow connection (e.g. wireless/mobile) may cause lags and degrade game experience for others. To mitigate this issue:

* Gradually expand allowed latency maximum and difference (see [Advanced Example Config](https://docs.edgegap.com/learn/matchmaking/..#advanced-example)),
  * players with high ping may have to wait longer than usual to find a match.
* Alternatively, allow players to override measurement with manual region selection, only sending fake ping values for the player-selected regions only (e.g. 25ms for fast match),
  * this may negatively impact player experience of the players' teammates and opponents.

{% hint style="info" %}
**High beacon ping doesn’t always result in high server ping**. Deployments are available in more locations than beacons. Beacons are orchestrated in real time to prioritize global coverage and reliability.
{% endhint %}

{% hint style="success" %}
See [](https://docs.edgegap.com/learn/matchmaking "mention") for **automated ping measurement using our SDKs**.
{% endhint %}

{% hint style="danger" %}
Beacons are automatically rescaled in real time - adding/removing/replacing existing beacons. Your clients and backend should account for this and **reload list of beacons before each matchmaking round**.
{% endhint %}

**`intersection`** matches players with one or more overlapping string values, case sensitively.

<details>

<summary>Rule Example: <code>selected_map</code></summary>

`selected_map` rule above with `"overlap": 1`  will match:

:white\_check\_mark: Alice + Bob + Charlie may match, or Alice + Bob + Dave may match,

:x: Alice + Bob + Charlie + Dave will never match.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FcQSUAnRkZKVyuGjFWSYL%2Fimage.png?alt=media&#x26;token=bf1a4e28-230a-4c9a-9ef6-a0dea4dbecdb" alt=""><figcaption></figcaption></figure>

</details>

#### Rule Expansion

Optionally, **`expansions`**  modify a rule’s attributes after a period of time spent in queue to relax  limitations and expand the pool of players which can be matched, **resulting in faster matches**.

<details>

<summary>Example Scenario: Expansions</summary>

[Initially, we require 1 team composed of exactly 4 players (possibly split in groups)](https://docs.edgegap.com/learn/matchmaking/..#advanced-example) with:

* maximum of 125 ms latency against the same (any one) beacon,
* latency difference of 125 ms or less between lowest/highest value for the same beacon,
* skill rating difference of 50 points or less between lowest and highest ranking player,
* the exact same (case sensitive) selected game mode,
* at least one matching map selection (case sensitive) amongst players,
* at least one matching [backfill group size](#backfill-match) value amongst players.

In the example above, we **expand the search by modifying attributes** after:

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>30 seconds:</td><td><ul><li>4 players</li><li><strong>150 skill rating range</strong></li><li><strong>max 250 ms latency</strong></li></ul></td></tr><tr><td>60 seconds:</td><td><ul><li>4 players</li><li><strong>200 skill rating range</strong></li><li>max 250ms latency</li></ul></td></tr><tr><td>3 minutes (180s):</td><td><ul><li><strong>1-4 players</strong></li><li>200 skill rating range</li><li><strong>any latency</strong></li></ul></td></tr></tbody></table>

</details>

<details>

<summary>Fine-Tuning Expansions</summary>

Predicting player preferences is similar to shooting at a moving target. Start with a less restrictive rule set at release and optimize afterwards in iterations with [#analytics](#analytics "mention").

These questions may help frame your thinking process:

* How long is my play session?
  * 5 min play session means each player rejoins queue every 5 min, which will indirectly reduce queue time due to more players queueing at any given time.
* What is my [peak CCU](#user-content-fn-11)[^11] and my [low tide CCU](#user-content-fn-12)[^12]?
  * If there's high variance (more than 60%) between low/high, you may need a separate profile for low tide to wait longer in each expansion, to accumulate more players.
* What is my players' geographic distribution?
  * Even spread across multiple time zones means the peak and low tide have lower variance, but doesn't improve match speed since players in different regions shouldn't match if your [#ping-optimization](#ping-optimization "mention") is configured correctly (would result in high ping).
* What is my skill rating distribution (per region)?
  * Skill distribution typically follows [Bell curve](https://www.simplypsychology.org/normal-distribution.html) (natural distribution), so with each standard deviation from average, you find less players further away from average. Players with average values will be matched fast, in first expansion, but extremes are a problem.
  * We recommend to increase the expanded difference amount with each expansion, e.g. 25 -> 50 -> 100, so you account for having less players on the curve extremes.
  * If you have any challenger tier (pro players) we recommend a separate profile with custom skill setup, since the sample is smaller and often doesn't follow the macro trend across the entire player base. (leaning towards extremes, inverted bell curve)
* How can I improve matchmaking speed and match fill rate with small player base?
  * Learn as much about your players and their preferences as possible!
  * Consider removing some rules or relaxing restrictions initially.
  * Relax team size or team count over time - partial match is better than no match.
  * Increase duration between expansions to accumulate more players.
  * Contact us for more tips and tricks, specifically for your game design.

</details>

{% hint style="info" %}
Expansions of any rule’s attribute will **overwrite previous values** of that attribute.
{% endhint %}

## 📌 Injected Variables

Your server might need to know details about it’s players. Player attributes, resolved match values, and other values are injected to your deployment alongside the usual [#injected-variables](https://docs.edgegap.com/orchestration/application-and-versions#injected-variables "mention").

Preview unformatted **🏁 Advanced Example Variables:**

```
MM_MATCH_PROFILE=advanced-example
MM_EXPANSION=initial
MM_TICKET_IDS=["cusfn10msflc73beiik0","cusfn18msflc73beiil0"]
MM_TICKET_cusfn10msflc73beiik0={"id":"cusfn10msflc73beiik0","created_at":"2025-02-21T22:17:42.3886970Z","player_ip":"174.93.233.25","group_id":"b2080c27-19c9-4fb0-8fe7-4bf1e5d285d1","team_id":"cusfn1gmsflc73beiim0","attributes":{"beacons":{"Chicago":12.3,"LosAngeles":145.6,"Tokyo":233.2},"elo_rating":1337,"selected_game_mode":"quickplay","selected_map":["DustII","Airport","BankVault"],"backfill_group_size":["new","1"]}}
MM_TICKET_cusfn18msflc73beiil0={"id":"cusfn18msflc73beiil0","created_at":"2025-02-21T22:17:42.2548390Z","player_ip":"174.93.233.23","group_id":"015d4dc8-6c79-4b5c-bbc6-f309b9787c8f","team_id":"cusfn1gmsflc73beiim0","attributes":{"beacons":{"Chicago":87.3,"LosAngeles":32.4,"Tokyo":253.2},"elo_rating":1339,"selected_game_mode":"quickplay","selected_map":["Island","Airport"],"backfill_group_size":["new","1"]}}
MM_GROUPS={"b2080c27-19c9-4fb0-8fe7-4bf1e5d285d1":["cusfn10msflc73beiik0"],"015d4dc8-6c79-4b5c-bbc6-f309b9787c8f":["cusfn18msflc73beiil0"]}
MM_TEAMS={"cusfn1gmsflc73beiim0":["b2080c27-19c9-4fb0-8fe7-4bf1e5d285d1","015d4dc8-6c79-4b5c-bbc6-f309b9787c8f"]}
MM_MATCH_ID=advanced-example_initial-2025-02-21T22:17:43.3886970Z
MM_INTERSECTION={"selected_map":["Airport"],"backfill_group_size":["new","1"]}
MM_EQUALITY={"selected_game_mode":"quickplay"}
```

{% hint style="info" %}
Environment variables are **stored as stringified JSONs**, parse them using our SDK or a custom method.
{% endhint %}

{% hint style="success" %}
**Servers can map player connections to groups and attributes** after player sends their ticket ID to server.
{% endhint %}

## 🧵 Player Tracing

If your players experience any issues, tracing their path to server logs can be helpful. Each Matchmaker **deployment** **will be tagged with assigned player ticket IDs** so you can easily [#filter-deployments](https://docs.edgegap.com/orchestration/deployments#filter-deployments "mention") and find [#container-logs](https://docs.edgegap.com/orchestration/deployments#container-logs "mention") to help you troubleshoot.

{% hint style="success" %}
**Display ticket IDs and deployment IDs in client match history UI** to trace players when troubleshooting.
{% endhint %}

{% hint style="info" %}
See [#connection-quality](https://docs.edgegap.com/orchestration/deployments#connection-quality "mention") to learn about deployment troubleshooting.
{% endhint %}

## 👀 Analytics

Gain insights into your matchmaker load and performance, no code or configuration required.

🌟 [**Upgrade Matchmaker to Enterprise Tier**](https://app.edgegap.com/matchmaker-management-v2/matchmakers/list) **to unlock matchmaking metrics and insights:**

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fhb8NmB4ELuG7ayGLTeHs%2Fimage.png?alt=media&#x26;token=e3e0371e-c33f-48c8-bd15-5c11448dc633" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fl0zTNccJASUTkJfTYxss%2Fimage.png?alt=media&#x26;token=0d061320-9524-4b12-83e6-ea9112ef1409" alt=""><figcaption></figcaption></figure>

## ☁️ Hosting Cluster

Matchmaker is conveniently hosted and managed 24/7 around the clock by Edgegap.

Choose a hosting option best suited for your goal:

* **Free Cluster (shared)** to test all features and explore synergies with your design,
  * shuts down after 3 hours automatically, requiring restart to continue testing.
* **Private Cluster** **(dedicated)** to ensure a stable environment for your production needs,
  * pick your region and get 24/7 support for live games to release with confidence.

#### Private Cluster Tiers

We currently offer [3 private cluster tiers](https://edgegap.com/resources/pricing#managed-infrastructure) to cater to everybody’s needs:

<table><thead><tr><th width="160">Tier</th><th align="right">Hobbyist Tier</th><th align="right">Studio Tier</th><th align="right">Enterprise Tier</th></tr></thead><tbody><tr><td>Best Suited For</td><td align="right">enthusiasts,<br>solo developers</td><td align="right">commercial releases</td><td align="right">high-traffic launches</td></tr><tr><td>Resources</td><td align="right">1 vCPU + 2GB RAM</td><td align="right">6 vCPU + 12GB RAM</td><td align="right">18 vCPU + 48GB RAM</td></tr><tr><td>Redundancy</td><td align="right">1x virtual node</td><td align="right">3x virtual nodes</td><td align="right">3x virtual nodes</td></tr><tr><td>Rate Limit (req/s)</td><td align="right">200</td><td align="right">750</td><td align="right">2,000</td></tr><tr><td>Price, hourly</td><td align="right">$0.0312</td><td align="right"> $0.146</td><td align="right">$0.548</td></tr><tr><td><strong>Price, 30 days</strong><br>(nonstop usage)</td><td align="right"><strong>$22.464</strong></td><td align="right"><strong>$105.12</strong></td><td align="right"><strong>$394.56</strong></td></tr></tbody></table>

Upgrade to a private cluster with one click. Changing Private Cluster Tiers after launch, without any player downtime, is also possible with [#rolling-updates-and-ab-tests](#rolling-updates-and-ab-tests "mention"). Managed clusters provide high-availability service hosting maintained by Edgegap with 24/7 live support for publicly released games.

Resource requirements for your instance will depend on factors:

* **number of players** - more players result in more tickets and API requests,
* **number of requests per player** - faster retries increase service load and consume resources,
* **configuration complexity** - intersection rules and expansions are particularly demanding,
* **average match duration** - shorter sessions make players rejoin matchmaking more often,
* **expiration and removal periods** - stale tickets stockpile over time and consume resources,
* **client retry fallback logic** - retrying with jittered backoff helps spread traffic burst peaks.

{% hint style="warning" %}
**Prepare for success and optimize after launch, so you don’t block your players on release day.** Use [#matchmaking-sdk](https://docs.edgegap.com/unity/developer-tools#matchmaking-sdk "mention") or **implement exponential jittered backoff** to recover from high load.
{% endhint %}

{% hint style="info" %}
Our clusters use cloud machines featuring AMD/Intel CPUs with clock speed 2.4 - 3.2 GHz.
{% endhint %}

### Rate Limits

To protect your cluster from exceeding it's burst capacity and crashing, we limit the number of requests per second based on our internal load tests using [#advanced-example](https://docs.edgegap.com/learn/matchmaking/..#advanced-example "mention") configuration.

<table><thead><tr><th>API endpoint</th><th width="130">Free Tier</th><th width="130">Hobbyist Tier</th><th width="130">Studio Tier</th><th width="130">Enterprise Tier</th></tr></thead><tbody><tr><td><strong>Overall Limit</strong></td><td><strong>100</strong></td><td><strong>200</strong></td><td><strong>750</strong></td><td><strong>2,000</strong></td></tr><tr><td>Create Deployment</td><td>5</td><td>10</td><td>30</td><td>30</td></tr><tr><td>List Beacons</td><td>10</td><td>20</td><td>75</td><td>200</td></tr><tr><td>Create Group<br>+ Create Ticket<br>+ Create Group Ticket</td><td>10</td><td>20</td><td>75</td><td>200</td></tr><tr><td>Read Membership<br>+ Read Group<br>+ Read Ticket</td><td>10</td><td>120</td><td>450</td><td>1,300</td></tr><tr><td>Create Backfill</td><td>5</td><td>10</td><td>37</td><td>100</td></tr></tbody></table>

Rate limits are expressed in **combined requests per second to the specified set of API endpoints**.

If you game clients do not retry requests upon receiving response `429 Too Many Requests`  **your deployments may be missing players** who stop reading their assignments due to high load.

#### Load Testing

Matchmaking and assignments require CPU and memory usage, requiring a hosting cost with each private matchmaker. See resources and prices associated with each tier on [our pricing page](https://edgegap.com/resources/pricing#matchmaker).

{% hint style="success" %}
**Always use** [**private clusters**](#private-cluster-tiers) **for stress testing.** Free matchmakers are strictly limited for dev testing only.
{% endhint %}

When designing your load test, **please consider realistic player patterns**:

| Realistic Scenario                                                                            | Unrealistic Traffic Pattern                                                              |
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| ✅ Players join matchmaking gradually, increasing req/s over several hours.                    | ❌ All players coordinate and create their tickets in the exact same second.              |
| ✅ Players wait an increasing amount of time between their retries (e.g. 1s-5s-10s-10s).       | ❌ All players retry immediately upon receiving `429 Too Many Requests`  response.        |
| ✅ Most players will receive their assignments  within a short time (10-60s) and stop polling. | ❌ All players continue polling for a set amount of time even after receiving assignment. |
| ✅ Most players finish their game (taking time) before restarting matchmaking anew.            | ❌ All players restart matchmaking anew immediately after receiving their assignment.     |
| ✅ Peak traffic is sustained for 6-8 hours a day, after which some time zones drop off.        | ❌ Peak traffic is sustained 24 hours a day, with all players playing night and day.      |

If a matchmaker is experiencing high load:

* if CPU is throttling, matchmaking could be slowed down,
* if matchmaker runs out of memory, it will restart without losing ticket information, hoping that clients will implement exponential backoff and the burst is spread over longer period of time.

## ⏩ Rolling Updates <a href="#rolling-updates-and-ab-tests" id="rolling-updates-and-ab-tests"></a>

Keeping track of compatibility between server and client versions can get complicated. Follow our tips for reliable releases, updates, and preventing downtime or compatibility issues.

**Your Matchmaker URL and Auth token will always remain the same after restarting.**

{% hint style="danger" %}
**Create separate matchmakers for dev & production** environments to experiment safely.
{% endhint %}

#### ⚠️ **Before Going Live**

We recommend creating multiple copies of your matchmaker ahead of time: `green`, `blue` and `orange`. You can rotate which matchmaker is in use as you release updates ([blue/green strategy](https://en.wikipedia.org/wiki/Blue%E2%80%93green_deployment)).

**Choose different regions for each instance to prevent downtime** during localized outages.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Flzy4elHYDPBMpjSMG1HS%2Fimage.png?alt=media&#x26;token=7da795fc-ae6c-490f-b65c-5f8dda635503" alt=""><figcaption><p>Blue/Green DevOps Environment Example</p></figcaption></figure>

#### **🔃 Client + Server Update**

**Prerequisites:** This section assumes you’ve completed [#before-going-live](#before-going-live "mention").

In order to **release game client + server updates**, you may:

1. Prepare new server app version `v1.2.0-rc` on Edgegap:
   1. push a new image tag to your container registry `t1.2.0`,
   2. create new app version `v1.2.0-rc`,
2. Perform any dev tests by [deploying your new app version](https://app.edgegap.com/deployment-management/deployments/list) `v1.2.0-rc`:
   1. connect your game engine’s Editor to the provided URL + external port,
3. Update unused matchmaker `blue` to link to your new image tag `t1.2.0`,
   1. enable caching for new app version `v1.2.0-rc` , enabling cache for this version will ensure the image is also cached for version `v-blue`  since they reference the same tag,
   2. wait for caching indicator in version `v1.2.0-rc`  to reach :green\_circle: green,
4. Update your new game client `c2` to use the new version `v-blue` when creating tickets:
   1. update your base URL and Authorization token in game client,
5. Perform QA tests and final verifications of your new game client `c2`:
   1. if you find and resolve any issues, repeat process from the beginning,
   2. wait 3-7 days to propagate matchmaker DNS changes to ISPs globally, after the matchmaker has been stopped (quick restart doesn't require DNS updates or waiting period),
6. Release your new game client update `c2` on game distribution platforms,
7. Allow time for new game client `c2` to distribute to player devices (typically up to 3-7 days):
   1. monitor outdated game clients `c1`  using deployment [#analytics](https://docs.edgegap.com/orchestration/deployments#analytics "mention"),
8. Clean up unused resources in your Edgegap account:
   1. delete image tag `t1.0.0` to free up Container Registry capacity,
   2. delete image tag `t1.1.0` to free up Container Registry capacity,
   3. turn off your `green`  matchmaker to pause billing until your next update.

{% hint style="success" %}
**For your next update**, increase version numbers and swap `green` and `blue` keywords in the guide.
{% endhint %}

#### **⚡ Server Hotfix**

**Prerequisites:** This section assumes you’ve completed [#before-going-live](#before-going-live "mention").

To **release a server patch without requiring a game client update**, you may:

1. Prepare new server app version `v1.2.0-rc` on Edgegap:
   1. push a new image tag to your container registry `t1.2.0`,
   2. create new app version `v1.2.0-rc`,
2. Perform tests and verifications by [deploying your new app version](https://app.edgegap.com/deployment-management/deployments/list) `v1.2.0-rc`:
   1. connect your game engine’s Editor to the provided URL + external port,
   2. if you find and resolve any issues, repeat process from the beginning,
   3. enable caching for new app version `v1.2.0-rc` , enabling cache for this version will ensure the image is also cached for version `v-green`  later since they will reference the same tag,
   4. wait for caching indicator in version `v1.2.0-rc`  to reach :green\_circle: green,
3. Update version `v-green`  to link to your new image tag `t1.2.0`,
   1. new matches will automatically initiate assignment with the updated tag `t1.2.0`,
   2. monitor outdated game clients `c1`  using deployment [#analytics](https://docs.edgegap.com/orchestration/deployments#analytics "mention"),
4. Clean up of unused resources in your Edgegap account:
   1. delete image tag `t1.1.0` to free up Container Registry capacity.

## 📗 API <a href="#matchmaking-api" id="matchmaking-api"></a>

Clients and servers may call API directly or with game engine SDKs, see also [](https://docs.edgegap.com/learn/matchmaking "mention").

{% hint style="info" %}
Unity/Android - consider [using raw string interpolation](https://www.c-sharpcorner.com/article/convert-string-to-json-in-c-sharp/) to prevent code stripping of hardcoded JSONs.
{% endhint %}

{% hint style="success" %}
**Swagger Web UI**: deploying your service will generate an openAPI specification and a convenient web UI. Open the URL in your browser to view and test all API endpoints, and to review payload examples.
{% endhint %}

{% tabs fullWidth="false" %}
{% tab title="🍀 Simple Example" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FqofgtLfdhstOFoJr1BzV%2Fmm-conf-simple-example.json?alt=media&token=7dc25895-aef3-4174-9f40-e45af77a28d6>" %}
{% endtab %}

{% tab title="🏁 Advanced Example" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FxSkXEzgG9AUPrXbsFsTU%2Fmm-conf-advanced-example.json?alt=media&token=4bcc1c0b-5887-457f-8a50-d89c0b20714e>" %}
{% endtab %}

{% tab title="🎾 Custom Lobby" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fts2LDr07atVf70MREw7j%2Fmm-conf-custom-lobby.json?alt=media&token=0215a52a-3fc1-47e8-beb9-6e90a4c49f6d>" %}
{% endtab %}

{% tab title="🥛 Backfill Showcase" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FJEbYPVDhVI8UNCIcyFxQ%2Fmm-conf-backfill-example.json?alt=media&token=64733825-a3de-4d91-96fd-0f2f5e8be0cc>" %}
{% endtab %}

{% tab title="⚔️ Competitive Games" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FVUNHRCiqRhf7axG6KQAD%2Fmm-conf-competitive-example.json?alt=media&token=7e49eb87-f07e-4969-b141-dbb8edea37fe>" %}
{% endtab %}

{% tab title="🤝 Cooperative Games" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2F6pWfNV1QOvpWbYQtbxc6%2Fmm-conf-cooperative-example.json?alt=media&token=b5c9f44b-cdd2-4968-8445-3f74d56b61c8>" %}
{% endtab %}

{% tab title="🎈 Social Games" %}
{% file src="<https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FkVZMSRCd7JTXwOK2MqoW%2Fmm-conf-social-example.json?alt=media&token=ef26e619-5b31-4395-8e35-7607cb21bd35>" %}
{% endtab %}
{% endtabs %}

Import API specification to [Scalar API Web Client](https://client.scalar.com/workspace/default/request/default) or [Swagger Editor](https://editor.swagger.io/) to inspect details.

### Server to Server <a href="#server-to-server-api" id="server-to-server-api"></a>

Add enhanced or customized controls over matchmaking flow - implement a custom proxy using our [managed-clusters](https://docs.edgegap.com/learn/advanced-features/managed-clusters "mention") or any cloud FaaS[^13] compute platform, to achieve any of:

* attach sensitive player attributes - such as cheater flags, skill ratings, or similar,
* provide team and match context in-game - list my teammates and opponents during loading,
* restrict specific edge cases - e.g. allow only 1 group per player at any time,
* add caching or API rate limiting - reduce number of requests and load on matchmaker,
* customize lobby-group integration - create asymmetric/role-based lobbies before matchmaking.

{% hint style="success" %}
**Include parameter `player_ip`  with member's public IP address** to ensure lowest possible player latency and take advantage of [#id-1.-server-score-strategy-best-practice](https://docs.edgegap.com/orchestration/deployments#id-1.-server-score-strategy-best-practice "mention").
{% endhint %}

{% hint style="info" %}
Game clients may use [ipify.org](http://ipify.org/) free service to find their public IPs. VPNs may mask public IP address.
{% endhint %}

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FzLfA3fEBgwaziGtp2yMO%2Fimage.png?alt=media&#x26;token=3d551e74-4a60-47a6-9d79-a506f961663d" alt=""><figcaption><p>Server to Server Matchmaking Activity Diagram</p></figcaption></figure>

#### Cross-Origin Resource Sharing (CORS)

For webGL games hosted on third party distribution platforms (e.g. [itch.io](http://itch.io/)), sending any requests to Matchmaker from game client may result in [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy violations. Most modern web browsers send a [pre-flight request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) to verify that a backend service (the Matchmaker) understands and accepts communication from your game client.

Failing pre-flight check (default for security reasons) can result in [one of several possible CORS-related errors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSMissingAllowOrigin), most commonly `CORS header 'Access-Control-Allow-Origin' missing` .

To resolve this error, add **`allowed_cors_origin`** parameter to your configuration to either:

* whitelist your exact client hosting domains:

<details>

<summary>🍀 Simple Example (Specific Domains Example)</summary>

<pre class="language-json"><code class="lang-json">{
  "version": "3.2.0",
  "allowed_cors_origins": [
    "https://dev.my-game-server.com",
    "https://prod.my-game-server.com"
  ],
  "profiles": {
      <a data-footnote-ref href="#user-content-fn-14">...</a>
  }
}
</code></pre>

</details>

* or whitelist a wildcard domain (including all subdomains):

<details>

<summary>🍀 Simple Example (Wildcard Domain Example)</summary>

<pre class="language-json"><code class="lang-json">{
  "version": "3.2.0",
  "allowed_cors_origins": ["https://*.my-game-server.com"],
  "profiles": {
      <a data-footnote-ref href="#user-content-fn-14">...</a>
  }
}
</code></pre>

</details>

{% hint style="info" %}
**No credentials are required for Matchmaker pre-flight requests**, if domains are configured correctly.
{% endhint %}

## 🚨 Troubleshooting

**Your success is our priority.** If you'd like to send custom requests, ask for missing critical features, or express any thoughts, [please reach out in our Community Discord](https://discord.gg/MmJf8fWjnt).

<details>

<summary><code>The application configuration is not valid for profile XYZ.</code></summary>

* We couldn’t find your [application-and-versions](https://docs.edgegap.com/learn/orchestration/application-and-versions "mention"), please verify `application`  values.

</details>

<details>

<summary><code>Docker image for '2024.01.30-16.23.00-UTC' is not cached.</code></summary>

[**🌟 Upgrade to Pay as You Go tier**](https://app.edgegap.com/user-settings?tab=memberships) **to unlock** [**instant deployments with Caching**](https://docs.edgegap.com/orchestration/deployments#id-1.-start-a-deployment)**.**

* 4GB+ uncached images may take longer to deploy, resulting in [#id-4.-deployment-error](https://docs.edgegap.com/orchestration/deployments#id-4.-deployment-error "mention"). Consider optimizing your server image size ([Unreal Engine](https://docs.edgegap.com/unreal-engine#optimize-server-build-size) / [Unity](https://docs.edgegap.com/unity#optimize-server-build-size)).
* You may proceed anyway, though we recommend testing your deployment time.

</details>

<details>

<summary>Why am I getting errors when trying to create a new matchmaker?</summary>

* Please read the error, it’s possible you’ve misspelled an identifier, rule, or an operator. - Use [JSONLint](https://jsonlint.com/) to validate your JSON formatting, you may have missed a comma or a bracket. - Reach out over [our Community Discord](https://discord.gg/MmJf8fWjnt) for help, we’ll be happy to assist. 🙏

</details>

<details>

<summary>Why did my matchmaker turned off automatically after 3 hours?</summary>

* Matchmakers in Free Tier are intended for initial tests and are automatically turned off after 3 hours. To continue testing you may [restart your matchmaker](https://app.edgegap.com/matchmaker-management-v2/matchmakers/list).
* Consider upgrading to paid tier for unliminted runtime.

</details>

<details>

<summary>Why can’t I start a second deployment on my account?</summary>

* You may only run 1 concurrent deployment in Free Tier.
* Please consider upgrading to paid tier for unlimited deployments.

</details>

<details>

<summary>Why am I getting assignment/deployment at random times, disregarding <code>player_count</code>?</summary>

* You or another team member may have created tickets during a previous testing session which were not assigned. Please [restart your matchmaker](https://app.edgegap.com/matchmaker-management-v2/matchmakers/list).

</details>

<details>

<summary>My ticket is stuck in <code>SEARCHING</code> .</summary>

* Please verify you’ve created enough matching tickets adhering to your configuration.

</details>

<details>

<summary>My ticket is stuck switching between <code>MATCH_FOUND</code> and <code>TEAM_FOUND</code> repeatedly.</summary>

* Free Tier accounts are limited to 1 deployment at a time.
* Please consider upgrading or stop your current deployment to start a new one.

</details>

<details>

<summary>My ticket goes straight to <code>CANCELLED</code>.</summary>

* Your ticket reached it’s expiration. Create a new ticket or increase the expiration period in your configuration for testing purposes.

</details>

<details>

<summary>I receive <code>HTTP 404 Not Found</code> when checking on my ticket.</summary>

* Your ticket was removed either by a DELETE request, or by reaching it’s removal period (starts after ticket is expired, defined in your configuration). Recreate a new ticket or increase the expiration/removal periods in your configuration for testing purposes.

</details>

<details>

<summary>My matchmaker shows an error, what should I do?</summary>

* If this is a development or a testing instance, try restarting your matchmaker first. - Please report any issues through [our Community Discord](https://discord.gg/MmJf8fWjnt).
* In case this issue is impacting a live game, create an [urgent support request](https://edgegap.atlassian.net/servicedesk/customer/portal/3).

</details>

## 🔖 Changelog

#### Semantic Versioning

Our matchmaker uses official [Semantic Versioning](https://semver.org/) guidelines. Each configuration JSON is tied to a specific version of the service. Service version dictates which rules and settings are available in your configuration and API. **Once a version is released, it will never change**.

Your configuration file will be validated depending on matchmaker version used, make sure your rules are matching the matchmaker version’s capabilities.

{% hint style="info" %}
**The latest version of matchmaker is `3.2.2`**. All examples on this page are up to date. Keep an eye out for end of support notices for your matchmaker version. See also [#rolling-updates-and-ab-tests](#rolling-updates-and-ab-tests "mention").
{% endhint %}

#### 3.2.2 (Mar 18, 2025)

🩹 **Bugfixes:**

* Minor bugfix to disband teams if a matched player deletes their ticket before match found.

{% hint style="success" %}
**This is the latest matchmaker service version, recommended for production use.**
{% endhint %}

{% hint style="warning" %}
**To upgrade your matchmaker version - Stop, Edit, Restart.** Quick Restart won't apply version changes.
{% endhint %}

#### 3.2.1 (Nov 24, 2025)

🩹 **Bugfixes:**

* Minor deployment bugfixes, resolved several errors when starting your matchmaker.

#### 3.2.0 (Oct 31, 2025)

🩹 **Bugfixes:**

* Various smaller specification fixes and documentation consistency updates.
* Various stability and self-healing fixes across matchmaker infrastructure.

**✨ Improvements and new features:**

* **Introducing** [#group-up](#group-up "mention") **feature** - managing groups is now easy and doesn't require 3rd party!
  * No more sharing complex ticket attributes amongst group members, you only need Group ID.
  * Start matching as a group once all of your players mark themselves as ready.
  * Validate group member attributes against the group leader upon joining, preventing unmatchable groups (attributes of group players wouldn't match under profile's rules).
  * Validate group size and decline new memberships once max team size is reached.
  * Read our updated documentation with the new user flow, SDK updates coming soon!
* Tickets (memberships) now include your match ID - track players and add UI elements to share your team or opponent teams nicknames, skill rating, or other properties stored in third parties.
* Major [#rate-limits](#rate-limits "mention") overhaul based on internal stress testing, better handling of short bursts.
* Improved match fill rate due to delaying partial matches size until end of expansion.
  * If maximum team size reached, match immediately.
  * Otherwise match at the end of current expansion, if minimum team size reached.
* Set expiration and removal periods per profile and optimize for best player experience.

{% hint style="warning" %}
To update your Configuration, increase version, and copy expiration and removal fields to each profile.
{% endhint %}

#### 3.1.0 (June 10, 2025)

🩹 **Bugfixes:**

* Matchmakers now correctly validate tickets with multiple profiles including different rules.

**✨ Improvements and new features:**

* More optimizations to maximize match fill rate with [player\_count rule](#matchmaking-rules). Tickets will now wait until end of expansion (or expiration) if only partial match is possible (>min and \<max team size).
  * Full matches (max team size reached) are made immediately (no change).
* Upgrade to Enterprise [#hosting-cluster](#hosting-cluster "mention") to unlock matchmaking [#analytics](#analytics "mention")! Gain insights into matchmaker load and performance, no code or configuration required. Metrics at launch include:
  * total tickets, backfills, assignments, and deployments made over custom period of time,
  * per minute rates for the above metrics over custom period of time,
  * totals and time series insights into expired tickets, expanded matches, match fill rate,
  * API usage metrics, and more.
* Improved [#matchmaking-rules](#matchmaking-rules "mention") documentation with better examples and visuals.

#### 3.0.0 (May 20, 2025)

**⚠️ Breaking changes:**

* Use [min/max team size to fill teams efficiently](#matchmaking-rules) (replaces player count expansions):
  * in your configuration `player_count` rule, replace `team_size` with `min_team_size` and `max_team_size` to achieve "best effort" matching attempting to maximize match fill rate,
  * to require a specific number of players per team, set both min and max to the same value,
  * backfills bypass `player_count` rule and always match with 1 ticket (unchanged).
* Tickets, group tickets, and backfills with all latencies above the highest `max_latency` in a given profile will be immediately rejected with `400 Bad Request` response to ticket create request, instead of expiring:
  * only applies if [latency rule](#matchmaking-rules) is configured,
  * to bypass this behavior, create an expansion with `max_latency: 99999` (any value higher than your client latency measurement timeout).
* [Injected environment variables](#injected-environment-variables) containing ticket data now include field `id` (ticket ID) so they can be reused more easily when creating [#backfills](#backfills "mention").

🩹 **Bugfixes:**

* [#backfill](#backfill "mention") now uses configured deletion and expiration period (like tickets and group tickets).
* [#backfill](#backfill "mention") now correctly matches using configured [`intersection` rules](#matchmaking-rules).
* Fixed [openAPI specification](#matchmaking-api) for POST [#backfills](#backfills "mention") request (requires `public_ip`) and GET /tickets response (`team_id` is optional), including examples.

**✨ Improvements and new features:**

* Up to 3x more potential matches are considered now, producing more optimal groups and maximizing match fill rate.
* Up to 200% faster matching speed due to concurrency optimizations.
* Up to 40% increased match fill rate due to optimization of expansions algorithm.
* Improved service stability and increased speed of quick restarts.

{% hint style="info" %}
Benchmarks were produced with chaos-generated data using [Advanced Example configuration](#configuration).
{% endhint %}

<details>

<summary>Changelog Archive (v2.1.0 and before)</summary>

#### 2.1.0 (Feb 24, 2025)

**⚠️ Breaking changes:**

* Separated game profile and expansion stage information in the [#injected-environment-variables](#injected-environment-variables "mention"):
  * `MM_MATCH_PROFILE` will now only include the profile name as it appears in the configuration.
  * Introduced `MM_EXPANSION_STAGE` which will contain the expansion stage as a string (e.g. "initial", "15", "30").
* Ticket assignments now include the group ID when [#endpoint-tickets](#endpoint-tickets "mention"). Group ID is also included as an [#injected-environment-variables](#injected-environment-variables "mention"), as a mapping of group ID to a list of the group's player IDs.
* Ticket assignments now include the team ID when [#endpoint-tickets](#endpoint-tickets "mention"). The team ID is also included in every ticket data [#injected-environment-variables](#injected-environment-variables "mention").
* [#endpoint-tickets](#endpoint-tickets "mention") now returns `409 Conflict` HTTP code instead of `204 No Content` to indicate the ticket can't be deleted since the deployment is starting. To replace leavers, use a [#backfill](#backfill "mention") issued by the server after a pre-specified timeout period.
* [#endpoint-backfills](#endpoint-backfills "mention") request body parameter `attributes.deployment_request_id` has been moved to `attributes.assignment.request_id`.
* [#endpoint-backfills](#endpoint-backfills "mention") request body now requires full assignment details as part of `attributes` parameter in addition to the `request_id`.

🩹 **Bugfixes:**

* Resolved intersection rule values are now [#injected-environment-variables](#injected-environment-variables "mention") in the `MM_INTERSECTION` environment variable.
* Quick restart feature now reliably regenerates API endpoints and openAPI specification when configuration is changed.
* Fixed several bugs during matchmaker (re)start causing prolonged startup time or getting matchmaker stuck.

**✨ Improvements and new features:**

* Increased rate limits and scalability of all API endpoints, across all matchmaker tiers.
* When assigning a player to a [#backfill](#backfill "mention"), the new player's ticket ID will be added as a tag to the Backfill's [deployments](https://docs.edgegap.com/learn/orchestration/deployments "mention").
* Added swagger UI authentication feature to test API directly in web UI without needing postman.
* Improved openAPI examples to reflect realistic requests and responses more closely.
* Added new [#inspect-api](#inspect-api "mention") meant for development and debugging purposes.
  * Allows listing all current player tickets in a paginated list.
  * Allows listing all current matches in a paginated list.

#### 1.0.0 (Dec 9, 2024)

* [#backfill](#backfill "mention"): Upon (popular) request, we’re adding backfill with automated ticket assignment, which allows you to reuse server seats when players leave the session.
  * Ideal for filling empty player seats after a match has begun, or for replacing players that have left during a match.
* [#join-as-group](#join-as-group "mention"): We’re adding the ability to join as a group to the already available ability to fill multiple teams with players.
  * Ideal for joining up in a matchmaking queue with a group of friends or coming from a common lobby.
* [#matchmaking-sdk](https://docs.edgegap.com/unity/developer-tools#matchmaking-sdk "mention") and [#edgegap-integration-kit-by-betide-studio](https://docs.edgegap.com/unreal-engine/developer-tools#edgegap-integration-kit-by-betide-studio "mention") matchmaking SDKs:
  * To make integration easier, we’re now offering Software Development Kits for the most popular game engines.
* Fixed a bug where the [#latencies-attributes](#latencies-attributes "mention") was not applied correctly.
* Tickets will now be automatically canceled after a [#matchmaking-process](#matchmaking-process "mention") if they haven't been assigned to a deployment.
* You can now [#endpoint-tickets](#endpoint-tickets "mention") to enhance the flow of your matchmaking process.
* Deployments made by the matchmaker are now tagged with ticket IDs.
* You can now edit your configuration while the matchmaker is running. This triggers a quick reload of your configuration without requiring a full on/off cycle for your matchmaker. Note: This feature is not recommended for production environments, as it deletes all current tickets and temporarily makes the API unresponsive.
* Fixed [#injected-environment-variables](#injected-environment-variables "mention") to use the correct primitive types instead of arrays.
* Fixed [#injected-environment-variables](#injected-environment-variables "mention") JSON values, which previously contained escaped characters.

#### 0.2.3 (Oct 8, 2024)

{% hint style="info" %}
This version is no longer supported, please use a newer version for your matchmaker.
{% endhint %}

* Fixed a bug where certain headers were not accepted by the matchmaker when requests were made from a WebGL application (CORS policies).

#### 0.2.2 (Oct 3, 2024)

{% hint style="info" %}
This version is no longer supported, please use a newer version for your matchmaker.
{% endhint %}

* Fixed issue with TLS/SSL certificate validation preventing matchmaker from launching.

#### 0.2.1 (Sep 30, 2024)

{% hint style="info" %}
This version is no longer supported, please use a newer version for your matchmaker.
{% endhint %}

* Fixed a bug causing the beacons endpoint to return a 500 error.

#### 0.2.0 (Sep 25, 2024)

{% hint style="info" %}
This version is no longer supported, please use a newer version for your matchmaker.
{% endhint %}

* Basic authentication is now mandatory for all endpoints.
* Added the ability to configure the number of retries on server assignment failure.
* Team-based matchmaking is now the default for all matchmaking configurations.
* Both application and version are now required fields in all profiles.
* Introduced a new endpoint to monitor the matchmaker's status.
* Updated the format of the tickets environment variable in the deployment.
* Added a configuration option to allow hosts to communicate with the matchmaker.
* The debug API is now only available when explicitly enabled in the configuration (it is currently disabled for rework).
* The `game_profile` key in the GET ticket response has been replaced by `profile`.

</details>

[^1]: example value

[^2]: game clients join a Lobby to retrieve matchmaking Group ID and join the Group

[^3]: Fully Qualified Domain Name

[^4]: e.g. for 3 free slots = \["3", "2", "1"]

[^5]: replace "2" with the number of actual group members

[^6]: replace with your own application name

[^7]: replace with your own application version

[^8]: maximum difference exceeded

[^9]: maximum latency exceeded

[^10]: Internet Service Provider

[^11]: highest concurrent users on a given day

[^12]: concurrent users during off-periods

[^13]: [Function as a Service](https://www.ibm.com/think/topics/faas)

[^14]: see other examples
