Matchmaker Integration
Gameye + AWS FlexMatch
STANDALONE mode. Keep your matchmaking rules. Replace GameLift. Dedicated server sessions in under 0.5 seconds.
Updated July 1, 2026
AWS FlexMatch is a capable matchmaking rules engine — skill brackets, team balance, latency thresholds, custom logic. If you've built your matchmaking rules in FlexMatch, they work. There's no reason to rebuild them.
What FlexMatch doesn't give you well is the server side: GameLift's per-core economics, VM provisioning times, single-cloud lock-in, and the Server SDK in every build are what drive studios to look for alternatives. The Gameye + FlexMatch integration separates the two concerns cleanly: FlexMatch finds the match, Gameye starts the server.
- Players submit match tickets — FlexMatch runs your matchmaking rules
- Match found — FlexMatch publishes
MatchmakingSucceededto SNS/EventBridge - Lambda bridge receives the event and selects the optimal Gameye region from player latency data
- Lambda calls Gameye's Session API — server starts in the closest available datacenter (~0.5s)
- Lambda notifies players with the server IP and port
- Players connect directly to the game server — Gameye is not in the network path
What changes vs. GameLift-hosted FlexMatch
| FlexMatch + GameLift | FlexMatch + Gameye | |
|---|---|---|
| Matchmaking rules | FlexMatch | FlexMatch (unchanged) |
| Server allocation | GameLift Session Queue | Gameye Session API |
| Server location | AWS regions only | 21 providers, 200+ locations |
| Egress fees | ~$0.09/GB | None |
| Session start time | 1–5 min (VM provisioning) | ~0.5 seconds (pre-pulled containers) |
| Engine SDK required | GameLift Server SDK | None — plain Linux process |
| Infrastructure lock-in | AWS | Provider-agnostic |
The key change in your codebase: one Lambda function that calls POST /session instead of creating a GameLift game session. Everything upstream — matchmaking configuration, rule set, ticket submission — is identical.
What you need
Gameye uses container-based orchestration — the game server must compile to a Linux headless binary. Unreal Engine and Unity both support this target.
Package your Linux binary as a Docker image and push to any OCI-compatible registry (Docker Hub, GitLab Registry, ECR). Gameye pre-pulls images onto infrastructure — no cold-pull delay at session start.
Start a Developer Trial at trial.gameye.com. Self-serve signup. Your image is registered during onboarding.
One field change in your matchmaking config: set FlexMatchMode to STANDALONE. Your rule set and all other settings are untouched.
Integration
Update your FlexMatch configuration
Set FlexMatchMode to STANDALONE and point notifications to an SNS topic. In standalone mode, gameSessionInfo.ipAddress and gameSessionInfo.port will be empty in the event payload — your Lambda fills them in.
{
"Name": "your-matchmaking-config",
"FlexMatchMode": "STANDALONE",
"RequestTimeoutSeconds": 30,
"AcceptanceRequired": false,
"BackfillMode": "MANUAL",
"RuleSetName": "your-ruleset",
"NotificationTarget": "arn:aws:sns:us-east-1:YOUR_ACCOUNT:matchmaking-events"
} Collect Gameye region latency in your game client
Gameye exposes pingable IPs per region via the GET /available-location endpoint. Have clients measure latency to these before submitting a ticket and pass the results as playerAttributes. Your Lambda uses them to select the optimal server location for the match.
// Fetch available regions for your image
const { locations } = await fetch(
'https://api.gameye.io/available-location/your-image-name',
{ headers: { Authorization: `Bearer ${GAMEYE_TOKEN}` } }
).then(r => r.json());
// Measure latency to each region's pingable IP
const latencies: Record<string, number> = {};
for (const loc of locations) {
latencies[loc.location] = await measurePing(loc.latencyIp); // ms
}
// Submit ticket — include latency as playerAttributes
await gamelift.startMatchmaking({
ConfigurationName: 'your-matchmaking-config',
Players: [{
PlayerId: player.id,
PlayerAttributes: {
skill: { N: player.mmr },
gameye_europe_ms: { N: latencies['europe'] ?? 9999 },
gameye_useast_ms: { N: latencies['us-east'] ?? 9999 },
gameye_asia_ms: { N: latencies['asia-east'] ?? 9999 },
}
}]
}).promise(); Deploy the Lambda bridge
Create a Lambda triggered by EventBridge on MatchmakingSucceeded events. It selects the best Gameye region from the matched players' latency attributes, calls POST /session, and notifies players with the returned host and port.
export async function handler(event: EventBridgeEvent) {
const { matchId, tickets } = event.detail;
const players = tickets.flatMap(t => t.players);
// Select region with lowest worst-case latency across all players
const region = selectBestRegion(players);
// Start Gameye session
const res = await fetch('https://api.gameye.io/session', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GAMEYE_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
location: region,
image: 'your-image-name',
version: process.env.GAME_IMAGE_VERSION,
args: ['/Game/Maps/Gameplay', `-maxplayers=${players.length}`],
labels: { match_id: matchId },
ttl: 3600,
}),
});
const session = await res.json();
// session.host → IP address
// session.ports → [{ type: 'udp', container: 7777, host: 34521 }]
const port = session.ports.find(p => p.type === 'udp').host;
await saveMatchSession(matchId, session.host, port); // e.g. DynamoDB
await notifyPlayers(players, { host: session.host, port });
}
function selectBestRegion(players: FlexMatchPlayer[]): string {
const regions = {
'europe': 'gameye_europe_ms',
'us-east': 'gameye_useast_ms',
'asia-east': 'gameye_asia_ms',
};
return Object.entries(regions)
.map(([region, attr]) => ({
region,
worstMs: Math.max(...players.map(p => p.playerAttributes?.[attr]?.N ?? 9999))
}))
.sort((a, b) => a.worstMs - b.worstMs)[0].region;
} Port mapping. The external port in ports[].host is what players connect to — it is not always 7777. Always use the value from the session response, not a hardcoded port.
Idempotency. EventBridge delivers events at-least-once. Guard against duplicate Lambda invocations with a conditional write before creating a session — check whether one already exists for matchId.
Notify players
FlexMatch standalone does not push connection details to players — your backend handles this.
Polling
Clients poll a /match-status endpoint every 500ms. The Lambda writes session host and port to a data store; the endpoint returns it when ready.
WebSocket push
Persistent WebSocket per player via API Gateway. Lambda pushes connection details directly — players receive the server address within ~1 second of match found.
Track session lifecycle
Call Gameye's player join and leave endpoints from your game server backend when players connect and disconnect. This gives you accurate player counts and supports backfill:
# Player connected
POST https://api.gameye.io/session/player/join
{ "sessionId": "session-id", "playerId": "player-id" }
# Player disconnected
POST https://api.gameye.io/session/player/leave
{ "sessionId": "session-id", "playerId": "player-id" } When the match ends, your server exits cleanly. Gameye detects process exit and reclaims the container. The ttl on the session is a backstop for any server that fails to exit.
Session lifecycle
Backfill
If players drop mid-match, keep BackfillMode: MANUAL. When a player leaves:
- Your game server notifies the backend of the open slot.
- Backend calls
StartMatchBackfillwith the current session roster. - FlexMatch finds a replacement and fires
MatchmakingBackfillSucceeded. - Lambda receives the event — no new session — reads the existing session from your data store and notifies the new player with the current host and port.
- Backend calls
POST /session/player/joinfor the new player.
Migration from GameLift
If your game is live on FlexMatch + GameLift today, this migration can be done without player-facing downtime:
- 1 Register your Docker image with Gameye — onboarding, no code changes required.
- 2 Deploy the Lambda bridge in parallel — it doesn't affect live GameLift sessions.
- 3 Create a new FlexMatch config in
STANDALONEmode pointing to the Lambda. - 4 Route a small percentage of traffic to the new config via a feature flag in your matchmaking request logic.
- 5 Validate — session starts, latency, cost metrics.
- 6 Shift 100% of traffic and decommission your GameLift fleet.
The FlexMatch rule set and matchmaking logic are identical between configs — there is nothing to rewrite.
FlexMatch standalone mode: developer reference
The questions developers hit most when wiring FlexMatch to external hosting — answered against the standalone flow above.
What's in the FlexMatch MatchmakingSucceeded event?
When a match forms, FlexMatch publishes a MatchmakingSucceeded event. The payload carries the match result — who matched, their team assignments, and the per-player latency data you use to pick a region. In STANDALONE mode no game session is created, so gameSessionInfo.ipAddress and port come back empty: that's the slot your server-allocation call fills.
{
"type": "MatchmakingSucceeded",
"matchId": "1f9c8e7a-…",
"matchmakingConfigurationArn": "arn:aws:gamelift:us-east-1:…:matchmakingconfiguration/your-config",
"tickets": [
{
"ticketId": "ticket-1",
"players": [
{
"playerId": "player-9241",
"team": "red",
"attributes": {
"skill": { "valueAttribute": 50 },
"latencyInMs": { "valueAttribute": { "us-east-1": 28, "eu-west-1": 96 } }
}
}
]
}
],
"gameSessionInfo": {
"ipAddress": "",
"port": null,
"players": [ { "playerId": "player-9241", "team": "red" } ]
}
} Parse three things: matchId (to correlate the session), tickets[].players[] (rosters and teams), and each player's latencyInMs map (to choose the closest region). Then call Gameye's POST /session with that region and return the host and port to your players. The full schema — including MatchmakingTimedOut, MatchmakingCancelled, and MatchmakingFailed — is in the AWS FlexMatch docs; the fields above are the ones the bridge actually reads.
Does FlexMatch deliver match events over SNS or EventBridge?
Both. Set a NotificationTarget on the matchmaking configuration to publish events to an SNS topic, and FlexMatch matchmaking events are also emitted to Amazon EventBridge automatically. Subscribe your Lambda bridge to whichever you already operate — the MatchmakingSucceeded payload is identical either way.
FlexMatch standalone vs. integrated (managed hosting) mode
In integrated mode FlexMatch is attached to a GameLift queue: on a successful match it automatically creates a GameLift game session on a managed fleet, so matchmaking and hosting are coupled to GameLift. In standalone mode (FlexMatchMode: STANDALONE) FlexMatch only forms the match and emits the event — it places no server. That decoupling is exactly what lets you keep FlexMatch matchmaking while hosting elsewhere, like Gameye. Your rule sets and tickets are identical in both modes.
How do FlexMatch latency rules map to a Gameye region?
Latency-based matchmaking has each player submit a latencyInMs attribute per region in their ticket; your rule set's latency rules then only match players who share a low-latency region. Because that same per-region latency data rides along in the MatchmakingSucceeded event, your bridge reuses it to pick the launch region — take the region with the best aggregate latency across the matched players and pass it to POST /session. FlexMatch decides who plays together; the latency data decides where the Gameye server spins up.
What does the StartMatchmaking flow look like end to end?
- Your backend calls
StartMatchmakingwith a ticket — players plus their per-regionlatencyInMsattributes. - FlexMatch evaluates the ticket against your rule set and forms a match.
- FlexMatch emits
MatchmakingSucceededto SNS/EventBridge. - Your Lambda bridge reads the event, selects a region from the latency data, and calls Gameye
POST /session. - Gameye returns a host and port in ~0.5s; the bridge hands them to the matched players.
What does FlexMatch cost in standalone mode?
FlexMatch standalone is billed on matchmaking volume — AWS measures it in player packages (effectively the number of players matched), independent of any hosting. Check the current AWS GameLift pricing page for the per-package rate. The hosting math is where standalone pays off: you pay FlexMatch for matchmaking only — no GameLift fleet hours, no GameLift egress — and run the servers on Gameye at $0.07/vCPU/hr with zero egress fees.
Does FlexMatch with Gameye scale to 1M+ concurrent players?
Yes — the two layers scale independently. FlexMatch matchmaking is an AWS-managed service that scales with ticket volume, and because standalone mode places no servers, matchmaking throughput isn't bound by any fleet (account-level matchmaking limits are soft and adjustable via AWS support). Server allocation scales on Gameye: a single POST /session starts a container in ~0.5s, and the orchestrator bursts across 21 providers and 200+ datacenters — peak concurrency is limited by the capacity you reserve, not a fleet ceiling. Chivalry 2 ran a 250,000-CCU launch on Gameye with zero downtime; reaching 1M+ is adding capacity, not re-architecting.
What happens to matchmaking or hosting if a region or provider fails?
Failure isolation comes from the decoupling. FlexMatch runs in AWS's managed control plane, independent of where your servers run, so a hosting-provider issue doesn't stop matches forming. On the hosting side, Gameye runs multi-provider in every region: if a provider degrades, the orchestrator places new sessions on healthy capacity automatically — GameLift-managed hosting is AWS-only with no cross-provider failover. Your Lambda bridge can also re-drive a MatchmakingSucceeded event if a POST /session call fails, so a transient allocation error retries onto healthy capacity rather than dropping the match.
Frequently asked questions
Can I use AWS FlexMatch without GameLift?
Yes. AWS FlexMatch supports STANDALONE mode, where it handles matchmaking independently of GameLift fleet management. When a match is found, FlexMatch publishes a MatchmakingSucceeded event to SNS/EventBridge. A Lambda function receives that event and calls any server allocation API — including Gameye's Session API.
Do I need to rewrite my FlexMatch rule set?
No. Your rule set, matchmaking configuration, and ticket submission logic are unchanged. The only change is setting FlexMatchMode to STANDALONE and deploying a Lambda that calls Gameye's POST /session instead of creating a GameLift game session.
How does region selection work without GameLift queues?
Gameye exposes pingable IPs per region via the GET /available-location endpoint. Game clients measure latency to these IPs before matchmaking and submit the results as FlexMatch playerAttributes. The Lambda bridge aggregates those measurements across matched players and selects the region with the lowest worst-case latency — ensuring no one player has a significantly worse experience.
Does this work with FlexMatch backfill?
Yes, with BackfillMode: MANUAL. When a player drops and you call StartMatchBackfill, FlexMatch fires MatchmakingBackfillSucceeded. The Lambda checks whether a session already exists for the matchId — if it does, it's a backfill, so it notifies the new player with the existing session's host and port rather than starting a new server.
Do I need to remove the GameLift Server SDK from my game server?
Gameye doesn't require an engine-side SDK. Your server runs as a plain Linux process inside a container — no GameLift SDK, no Gameye SDK. Removing the GameLift SDK is a one-time cleanup during migration, but it's not required immediately; the server will run without it.
What is AWS FlexMatch STANDALONE mode?
STANDALONE mode runs AWS FlexMatch as a pure matchmaker, decoupled from GameLift's fleet and hosting layer. FlexMatch still groups players using your rule set, but instead of allocating a GameLift game session it publishes a MatchmakingSucceeded event to SNS/EventBridge — and you handle server allocation yourself. That's exactly what lets you keep FlexMatch while hosting on Gameye: a Lambda reads the event and calls Gameye's POST /session.
FlexMatch vs. custom matchmaking — which should I use with Gameye?
Both work with Gameye, because server allocation is a single REST call either way. Keep FlexMatch if you already have rule sets and ticket logic you don't want to rewrite — STANDALONE mode preserves all of it and only changes where the server comes from. Build custom matchmaking if you want full control over match formation, or you're not tied to AWS; your matchmaker just calls POST /session when a match is ready. Gameye has no SDK or matchmaker lock-in on the hosting side, so the choice is yours.
Get started
Start your free trial today.
Sign up, push your image, and start your first session — all before your next sprint ends. No sales call required.