Skill-Based Matchmaking Architecture: Ratings, Pools & Relaxation

Design SBMM with ratings, party/region pools, queue-age relaxation, latency constraints, backfill and server assignment. Compare Open Match and FlexMatch.

Backend Guides

Skill-based matchmaking is a queue-management problem before it is a rating-algorithm problem. The hard part is deciding which constraints are non-negotiable, which can relax as a ticket ages, how parties remain atomic, where a playable server will come from, and how to measure the quality/wait-time tradeoff. Elo, Glicko, TrueSkill-style models, or another rating estimator only provide one input to that decision.

Start with liquidity: calculate the arrival rate into the eligible pool after region, mode, platform/crossplay, party size and other hard partitions. Total CCU can look healthy while a particular queue is starved. If a queue needs 10 players and receives 0.2 eligible tickets/second, the theoretical fill floor is already about 50 seconds before skill, latency, party and acceptance constraints reject anybody.

The Production Matchmaking Pipeline

StageResponsibilityWhat should stay separate
1. Rating / player featuresMaintain skill estimate, uncertainty, role/preferences and other trusted attributesRating updates do not have to run inside the queue service
2. Ticket ingestCreate one ticket per solo player or party, with region latency and mode constraintsAuthentication and party membership should be resolved before matching
3. Candidate poolsFilter tickets by hard compatibility: mode, build, crossplay, region availability, party policyDo not turn every preference into a permanent partition
4. Match generationGroup compatible tickets into candidate matches and balance teamsThe algorithm should be testable independently from server allocation
5. RelaxationWiden soft constraints as the oldest ticket waitsNever relax rules that are true correctness/safety requirements
6. AssignmentChoose or allocate the session/host and return a connection assignmentMatching players is not the same operation as hosting them
7. Backfill / recoveryFill eligible vacancies, handle declines/timeouts, return failed assignments to queueBackfill needs its own policy; it is not just “run matchmaking again”

Rating Is an Input, Not the Matchmaker

The rating model should answer something like “what do we currently believe about this player's/team's skill?” The matchmaker then combines that estimate with latency, party structure, roles, queue age and session constraints.

Model familyUseful propertyWatch for
Elo-styleSimple single-number rating; easy to explain and auditNo native uncertainty term; multiplayer/team updates need additional design
Glicko-styleAdds rating uncertainty/deviation, useful when activity variesYou still need a team/multi-player outcome model if the game is not 1v1
TrueSkill / Weng-Lin / OpenSkill-styleModels a distribution rather than one number and can support multi-team outcomesChoose/model/test for your actual match format; there is no universal best default

Do not pick a rating library because a blog calls it “the 2026 default.” Replay historical matches offline, measure calibration and convergence, and decide how inactivity, smurfs/new accounts, parties and mode-specific skill should work for your game.

The Core Tradeoff: Match Quality vs Queue Age

A practical matcher normally starts strict and expands soft constraints with ticket age. For example:

0-10s   same region, skill delta <= 75, exact party policy
10-25s  same region, skill delta <= 150
25-45s  common acceptable region, skill delta <= 250
45s+    game-specific fallback or ask player to widen search

The numbers above are examples, not recommendations. Derive them from your measured queue distribution. AWS FlexMatch formalizes this concept as expansions: rules can relax after configured wait times. It can base expansion timing on the newest or oldest ticket in a candidate match, which directly changes the quality-versus-wait bias.

Latency: Match on Common Playable Regions

Do not reduce networking to “players must be within X milliseconds of each other.” Each ticket should report latency to candidate regions (or another trusted estimate of region suitability). A match is feasible when the party/tickets share at least one acceptable hosting location. Then choose the location that satisfies your game's policy: cap worst-player latency, minimize average/max latency, prefer a home region, or trade latency against queue age.

{
  "ticket_id": "t_123",
  "party_id": "p_456",
  "skill": { "estimate": 1420, "uncertainty": 85 },
  "regions": { "eu-west": 31, "eu-central": 42, "us-east": 118 },
  "mode": "ranked_5v5",
  "queued_at": "2026-09-12T16:00:00Z"
}

FlexMatch accepts player latency data and can evaluate latency in rules or use latency-aware batching/placement. Open Match leaves this policy in your pools/matchmaking function rather than imposing one universal latency model.

Parties Are Atomic Tickets

A party should enter the queue as one logical unit. Do not split a four-player party into four independent tickets and hope team assembly puts them back together. Your policy then decides what party combinations are legal and how team skill is calculated.

  • Hard constraints: party members stay together, total team size fits, incompatible platform/build combinations are rejected.
  • Soft constraints: preferred role composition, party-vs-party symmetry, skill balance and region preference can relax if your game permits it.
  • Team rating: define how a party's skill distribution contributes to team balance; do not blindly average if one extreme player meaningfully changes expected outcome.

Open Match: Do Not Copy an Old Architecture Diagram Blindly

Open Match 1.x uses the familiar Frontend/Backend/Query services, Match Functions, optional evaluator/synchronizer flow, and an external Director that fetches matches and writes assignments. Its documented deployment model is Kubernetes-oriented.

Open Match 2 is a separate public preview architecture. Its current documentation describes a single horizontally scalable om-core service backed by Redis, with no evaluator and a simplified core API. You still own the surrounding matchmaker: ticket-facing service, custom matchmaking function, assignment/server allocation, and player notification. If you are selecting Open Match today, decide deliberately whether you are adopting the established 1.x line or evaluating the 2.x public preview; do not mix their component diagrams.

FlexMatch: Managed Matching Does Not Require GameLift Hosting

Amazon GameLift Servers FlexMatch is a managed configurable matchmaker. Its rule sets define teams, player attributes, rules and time-based expansions; parties, player acceptance and backfill are supported. It can integrate with GameLift hosting, but AWS also documents standalone FlexMatch for peer-to-peer games or other compute providers. That makes “FlexMatch means GameLift fleet lock-in” an inaccurate decision rule.

Use FlexMatch when its rule language and managed lifecycle fit your game. Use a custom/Open Match path when the match-generation logic itself is a differentiator that needs arbitrary code, experiments or unusual constraints. Either way, keep rating calculation and game-server allocation as explicit boundaries.

Build Your Own: Size by Queue Shape, Not Total CCU

A small custom matcher can be entirely reasonable. The deciding variable is not “under 10K CCU.” It is whether the number of modes/regions/party shapes and the matching policy are simple enough to reason about, test and operate.

eligible_arrival_rate = tickets_per_second after hard partitions
minimum_fill_time     ≈ players_needed / eligible_arrival_rate
actual_fill_time      = minimum_fill_time + rejection/relaxation/assignment cost

A co-op game with three queues and simple constraints may need very little machinery at large CCU. A competitive game with many ranked modes, strict parties, crossplay policy, role constraints and regional fragmentation can need sophisticated matching at much smaller population.

Backfill Is a Separate Policy

Backfill asks a different question from new-match creation: “should this waiting ticket join an already-running session with an empty slot?” You need rules for round phase, score/state disadvantage, party integrity, expected remaining match duration and whether players can opt out. Prioritize or deprioritize backfill explicitly; AWS FlexMatch, for example, exposes backfill priority in its algorithm configuration.

Observability That Tells You Why Matchmaking Feels Bad

  • Queue time P50/P90/P95 by region, mode, party size and skill segment.
  • Expansion stage at match time: how often are players reaching each relaxation step?
  • Abandonment / timeout rate by queue age.
  • Skill quality: predicted win probability or rating spread between teams, not just raw MMR delta.
  • Latency quality: chosen region, max/median player latency and rejected-region causes.
  • Party composition: solo vs premade distributions and resulting team balance.
  • Assignment failures: match found but no usable server/host, plus time to recover.
  • Backfill outcomes: acceptance, completion and repeat-queue behavior for backfilled players.

Track these before experimenting with “AI matchmaking.” If you cannot explain which relaxation rule produced a poor match, adding a learned scoring model makes the system harder to debug, not smarter.

A Minimal Match-Generation Skeleton

for oldest_ticket in queue_by_age:
    policy = constraints_for_age(oldest_ticket.wait_time)
    candidates = pool.compatible_with(oldest_ticket, hard_constraints)
    candidates = candidates.where(common_region && policy.soft_rules)

    match = balance_parties_and_teams(oldest_ticket, candidates, policy)
    if match.complete:
        reserve_tickets_atomically(match)
        assignment = allocate_or_select_session(match)
        publish_assignment_or_release(match, assignment)

The difficult functions are constraints_for_age, party/team balancing, atomic reservation and failure recovery. Keep those testable with recorded queue snapshots so every rules change can be evaluated against the same historical population before it reaches production.

Crux boundary: Crux does not currently provide the SBMM system described above. Its current matcher uses exact game_mode + region buckets and forms groups of two; metadata is stored but does not drive matching, and there is no current MMR, party, latency-optimization or backfill engine. Crux can provide the surrounding player/backend and dedicated-server-registry surfaces while you use a specialized matcher or custom service for ranked matchmaking.

Current References

Related Guides