Skip to content

The Edge of the Cyber World See the latest

Apps

Set Up Google Open Match 2 Matchmaking: 13 Steps [2026]

Matchmaking is the part of online game infrastructure that nobody notices when it works and everybody complains about when it doesn’t. Players expect a fair, fast match within seconds, but the backend logic behind “find me four other people my skill level, on my region, who want the same game mode” is a distributed systems problem on its own, separate from the servers that actually host the match. Google’s open source answer to that problem, Open Match, has quietly undergone its biggest architectural change since launch. Open Match 2 collapses what used to be three or four separate microservices into a single horizontally scalable core, and as of October 2026 it’s sitting in public preview with documentation updated as recently as late September.

This tutorial walks through standing up Open Match 2 on Google Kubernetes Engine from a blank GCP project to a working matchmaking function that pairs players into matches, end to end. You’ll deploy the om-core service, wire up Redis (or Memorystore), write a matchmaking function in Go, submit tickets, and pull back assignments. By the end you’ll have a cluster that can take “player wants to play ranked 5v5 in US-East” and turn it into “here are five compatible players and a server to send them to.”

None of this is a drop-in replacement for the game server itself. It’s the layer that decides who ends up on that server together, and it’s worth getting right before a launch, because a matchmaking bug doesn’t crash anything visibly, it just quietly makes queue times longer and match quality worse until players notice and leave. That makes it one of the easier pieces of game infrastructure to under-invest in during development and one of the more expensive pieces to retrofit after launch.

What Open Match 2 Actually Does (and Doesn’t)

Before touching a terminal, it’s worth being precise about scope, because this is the single most common point of confusion for teams evaluating it. Open Match 2 is not a game server. It does not spin up dedicated server instances, it does not manage fleets, and it does not handle the actual network connection between players once a match is formed. That’s the job of a tool like Agones, which Tech Insider has covered separately for dedicated game server allocation on Kubernetes. Open Match 2 sits one layer up: it answers the question of who should play with whom, not where should they play.

The official Open Match 2 project documentation describes it as “a data layer that helps your matchmaker run matching logic at scale,” more specifically, “a player data cache with a data-retrieving gRPC proxy.” That’s a deliberately narrow description. Open Match 2 doesn’t decide who should be matched with whom, that logic lives in your own Matchmaking Function (MMF), a service you write. What Open Match 2 provides is the plumbing: ticket storage, a pool-matching query layer, and a unified gRPC API so your MMF doesn’t have to reinvent ticket lifecycle management from scratch.

The architectural shift from Open Match 1.x is significant. OM1 split responsibilities across separate Frontend, Backend, and Query services, each independently deployed, each adding a network hop. OM2 consolidates all of that into a single binary commonly called om-core, deployed as one horizontally scalable container image. Fewer moving parts means fewer places for a matchmaking pipeline to silently fail at 2 a.m. on a Friday launch night.

Prerequisites and Versions

Get these installed and verified before Step 1. Version mismatches are the number one reason this kind of setup stalls out halfway through.

  • A Google Cloud Platform project with billing enabled (a fresh project avoids quota conflicts with existing workloads)
  • Google Cloud SDK (gcloud) installed and authenticated, version 480 or newer
  • kubectl installed and able to reach a cluster you control
  • helm version 3.x for templated Redis and Open Match deployments
  • go version 1.22 or newer if you plan to write a custom Matchmaking Function in Go (the reference examples in the Open Match 2 repo are Go-based)
  • Docker or a compatible OCI builder for packaging your MMF container
  • Basic familiarity with gRPC and Protocol Buffers, since the entire OM2 API surface is gRPC-first with an HTTP gateway layered on top
  • A terminal with at least 20 minutes of uninterrupted time for the GKE cluster to provision

One honest caveat up front: Open Match 2 is explicitly labeled a public preview by its maintainers in the googleforgames/open-match2 GitHub repository. Preview software means APIs can shift between releases. Don’t commit this to a production matchmaking pipeline for a live game without pinning a specific commit or tag and testing upgrades in staging first.

Step 1: Create the GKE Cluster

The Open Match installation guide uses a four-node n1-standard-4 cluster as its reference example, deployed in the us-west1-a zone. That’s a reasonable starting point for development and load testing, not a mandated minimum. Treat it as a known-good baseline you can scale down for a quick test or up for production load testing.

gcloud auth login
gcloud config set project YOUR_PROJECT_ID

gcloud container clusters create open-match-cluster 
  --zone us-west1-a 
  --machine-type n1-standard-4 
  --num-nodes 4

gcloud container clusters get-credentials open-match-cluster --zone us-west1-a

kubectl get nodes

You should see four nodes in Ready state. If the cluster creation command hangs for more than 10 minutes, check your project’s compute quota in the GCP console, this is the single most common silent failure at this step, particularly on fresh projects with default quota limits.

Step 2: Clone the Open Match 2 Repository

Unlike Open Match 1.x, which ships as a Helm chart in the main googleforgames/open-match repo, Open Match 2 lives in its own repository, googleforgames/open-match2. Keep these two repos mentally separate while you work, mixing documentation or Helm values between the two is a fast route to confusing errors.

git clone https://github.com/googleforgames/open-match2.git
cd open-match2
ls -la

You’ll find the core Go application source, Protocol Buffer definitions under pkg/api, a gRPC-Gateway reverse proxy that translates RESTful HTTP calls into gRPC for clients that can’t speak gRPC natively, and example deployment manifests. Spend five minutes reading the top-level README before moving on, the public preview status means the README is usually more current than any third-party tutorial, including this one.

Step 3: Deploy Redis or Memorystore

Open Match 2’s ticket pool and state live in Redis. The project documentation notes that Redis was previously deployed through a bundled Helm subchart but is now more commonly paired with either a self-managed YAML deployment inside the cluster or Google Cloud Memorystore for a managed, production-grade option. For this tutorial we’ll deploy Redis in-cluster to keep costs down during development, then note the Memorystore swap for production.

# In-cluster Redis for development
kubectl create namespace open-match
kubectl apply -n open-match -f install/yaml/redis.yaml

kubectl get pods -n open-match -l app=redis
kubectl logs -n open-match -l app=redis --tail=20

For a production deployment, provision a Memorystore instance instead and point om-core at its internal IP:

gcloud redis instances create om-matchmaking-cache 
  --size=5 
  --region=us-west1 
  --redis-version=redis_7_0 
  --tier=standard

gcloud redis instances describe om-matchmaking-cache 
  --region=us-west1 --format="value(host)"

The --tier=standard flag provisions a replicated Memorystore instance with automatic failover, which matters more than it sounds like it should. Open Match’s production best-practices guide specifically calls out Redis availability as a single point of failure if you skip replication, since every ticket, pool query, and assignment flows through it.

Step 4: Configure and Deploy om-core

With Redis reachable, configure the core service’s Helm values to point at it, then deploy.

# values-override.yaml
redis:
  enabled: false   # set false when using external Memorystore/self-managed Redis
  externalHost: "10.x.x.x"
  externalPort: 6379

omCore:
  replicas: 2
  image:
    repository: gcr.io/open-match-public-images/open-match2-core
    tag: latest
  resources:
    requests:
      cpu: 250m
      memory: 256Mi
    limits:
      cpu: 1000m
      memory: 512Mi

Then install with Helm:

helm install open-match-core ./install/helm/open-match2 
  -n open-match 
  -f values-override.yaml

kubectl get pods -n open-match
kubectl get svc -n open-match

Give it a minute, then confirm om-core pods report Running and the service has a ClusterIP assigned. If pods sit in CrashLoopBackOff, jump to the troubleshooting section below before continuing, it’s almost always a Redis connectivity issue at this stage.

Step 5: Understand Tickets, Pools, and the Matchmaking Function

Three concepts carry the entire system, and getting them straight now saves confusion later.

A ticket represents a player or a party requesting a match. It carries attributes, things like skill rating, region, game mode, and party size, that your matchmaking logic will use to decide compatibility. A pool is a filtered subset of currently active tickets that match a given set of constraints, for example “all tickets requesting US-East ranked 5v5 with MMR between 1200 and 1400.” A Matchmaking Function, or MMF, is the service you write that receives pools of candidate tickets and returns zero or more proposed matches. Open Match 2 handles ticket storage, pool filtering, and the gRPC plumbing between these pieces; the actual “is this a good match” decision is entirely yours to implement.

This separation is deliberate. The same OM2 core deployment can serve wildly different matchmaking logic, a battle royale lobby filler, a strict skill-based ranked queue, or a party-size-aware co-op matcher, because the matching intelligence lives entirely in your MMF, not in Open Match itself.

Step 6: Write a Basic Matchmaking Function in Go

Here’s a minimal MMF that groups tickets into matches of five, a common size for competitive shooters. This is intentionally simple, production MMFs typically add skill-band bucketing, region affinity, and backfill logic, but this gets a working pipeline end to end first.

package main

import (
	"context"
	"log"
	"net"

	pb "github.com/googleforgames/open-match2/pkg/pb"
	"google.golang.org/grpc"
)

const matchSize = 5

type mmfServer struct {
	pb.UnimplementedMatchFunctionServer
}

func (s *mmfServer) Run(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
	tickets := req.GetPool().GetTickets()

	for i := 0; i+matchSize <= len(tickets); i += matchSize {
		group := tickets[i : i+matchSize]
		match := &pb.Match{
			MatchId:      generateMatchId(),
			Tickets:      group,
			MatchProfile: req.GetProfile(),
		}
		if err := stream.Send(&pb.RunResponse{Match: match}); err != nil {
			return err
		}
	}
	return nil
}

func main() {
	lis, err := net.Listen("tcp", ":50502")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterMatchFunctionServer(s, &mmfServer{})
	log.Println("MMF listening on :50502")
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

Package this as a container and push it to your registry:

docker build -t gcr.io/YOUR_PROJECT_ID/simple-mmf:v1 .
docker push gcr.io/YOUR_PROJECT_ID/simple-mmf:v1

kubectl create deployment simple-mmf 
  --image=gcr.io/YOUR_PROJECT_ID/simple-mmf:v1 
  -n open-match
kubectl expose deployment simple-mmf 
  --port=50502 --target-port=50502 -n open-match

Step 7: Submit a Test Ticket

With om-core and your MMF both running, submit a ticket through the unified OpenMatchService gRPC API. This is a good checkpoint to use grpcurl if you don’t want to write a client yet.

grpcurl -plaintext -d '{
  "ticket": {
    "attributes": {
      "region": "us-east",
      "mode": "ranked-5v5",
      "mmr": 1300
    }
  }
}' localhost:50504 openmatch.OpenMatchService/CreateTicket

A successful response returns a ticket ID. Submit four or five more with similar attributes to give your MMF enough candidates to form a match on the next invocation cycle.

Step 8: Invoke the Matchmaking Function

Trigger an MMF invocation against a pool matching your submitted tickets’ attributes:

grpcurl -plaintext -d '{
  "profile": {
    "name": "ranked-5v5-us-east",
    "pool": {
      "string_equals_filters": [
        {"double_arg": "region", "value": "us-east"},
        {"double_arg": "mode", "value": "ranked-5v5"}
      ]
    }
  }
}' localhost:50504 openmatch.OpenMatchService/InvokeMatchmakingFunction

If five or more tickets matching those filters exist in the pool, you should get a Match object back containing the grouped ticket IDs and a generated match ID. That match ID is what you’d hand off to Agones or whatever server-allocation system assigns the actual game session.

Step 9: Assign Tickets and Clean Up

Once a match is formed and a server is allocated (through Agones or your own fleet manager), assign the match’s connection details back to each ticket so clients know where to connect:

grpcurl -plaintext -d '{
  "assignments": [
    {
      "ticket_ids": ["ticket-abc123", "ticket-def456"],
      "connection": "34.82.11.9:7777"
    }
  ]
}' localhost:50504 openmatch.OpenMatchService/AssignTickets

After assignment, delete the consumed tickets so they don’t get picked up by a future matchmaking cycle:

grpcurl -plaintext -d '{"ticket_id": "ticket-abc123"}' 
  localhost:50504 openmatch.OpenMatchService/DeleteTicket

Step 10: Scale the Core Service Horizontally

Because om-core is a stateless, horizontally scalable service (all shared state lives in Redis), scaling it under load is a straightforward replica bump rather than an architectural change:

kubectl scale deployment open-match-core 
  -n open-match --replicas=6

kubectl get hpa -n open-match

Consider wiring a HorizontalPodAutoscaler against CPU utilization or a custom metric tied to ticket volume for production traffic patterns, the same approach Tech Insider covered for autoscaling dedicated game servers with Kubernetes HPA and KEDA applies conceptually here, just targeting the matchmaking layer instead of the server-hosting layer.

Step 11: Add Observability

Matchmaking failures are invisible to players until queue times spike, which means you want metrics before launch day, not after. At minimum, instrument and export:

  • Ticket creation rate and current pool size per profile
  • MMF invocation latency and match yield (matches formed per invocation)
  • Average and p99 time-to-match per player
  • Redis memory usage and connection count
  • Assignment failures (tickets that matched but never got a valid server connection)

A Prometheus ServiceMonitor against the om-core metrics endpoint, paired with a Grafana dashboard tracking time-to-match as your primary SLO, is the standard pattern most teams running Open Match in staging environments converge on.

Step 12: Load Test Before You Trust It

Don’t take any matchmaking system’s word for its own throughput, test it. A simple load test script that creates hundreds of synthetic tickets per second across multiple regions and game modes will expose Redis connection limits, MMF invocation bottlenecks, and pool-filter query performance far faster than production traffic will teach you gently.

for i in $(seq 1 500); do
  grpcurl -plaintext -d "{"ticket":{"attributes":{"region":"us-east","mode":"ranked-5v5","mmr":$((1000 + RANDOM % 800))}}}" 
    localhost:50504 openmatch.OpenMatchService/CreateTicket &
done
wait

Watch Redis CPU and om-core pod memory during this burst. If either climbs steadily without plateauing, you’ve found your next scaling bottleneck before players did.

Step 13: Connect to a Server Allocator

Open Match 2 explicitly does not allocate dedicated game servers, the documentation is clear that additional services are needed for tasks like obtaining a server for a formed match. The standard pairing is Agones: your match-formation logic (triggered by a successful MMF invocation) calls Agones’ allocation API to reserve a ready game server, then feeds that server’s IP and port back into AssignTickets as shown in Step 9. If you haven’t set up Agones yet, Tech Insider’s Agones on Kubernetes setup guide covers that half of the pipeline in detail.

Securing the om-core gRPC Endpoint

Everything in this tutorial so far runs in plaintext inside a single namespace, which is fine for a laptop demo and dangerous for anything touching real player data. Before a matchmaking pipeline goes anywhere near production traffic, lock down three surfaces. First, the om-core gRPC endpoint itself should sit behind mutual TLS or, at minimum, a service mesh sidecar handling encryption in transit, since ticket attributes can include player identifiers and skill data that shouldn’t travel in the clear even inside a VPC. Second, restrict which workloads can call CreateTicket and AssignTickets with Kubernetes NetworkPolicies, there’s no legitimate reason for an arbitrary pod in the cluster to be able to forge ticket assignments. Third, if you’re exposing any part of the matchmaking API to client devices directly rather than routing through your own game backend, put an authenticated gateway in front of it. Open Match 2’s gRPC-Gateway handles the HTTP-to-gRPC translation, but it does not handle authentication on its own, that’s a layer you add.

A common production pattern is to never let game clients talk to om-core at all. Instead, the client calls your own backend API, which validates the player’s session token, then calls CreateTicket on the player’s behalf using a service account with scoped IAM permissions. This keeps the entire Open Match 2 deployment inside a private cluster network with no public ingress, which dramatically shrinks the attack surface compared to exposing matchmaking directly to the internet.

Running Matchmaking Across Multiple Regions

A single-region deployment like the one this tutorial builds works for testing, but most live games need matchmaking logic that’s aware of multiple GCP regions so European players aren’t queued against candidate pools sitting in us-west1. The cleanest pattern is running one independent Open Match 2 deployment per region, each with its own Redis or Memorystore instance, rather than trying to share a single global ticket pool across regions over a WAN link. Cross-region Redis calls add latency to every single pool query, and that latency compounds across every MMF invocation cycle.

Route players to the nearest regional deployment based on client IP geolocation or a latency probe at the start of the matchmaking flow, before a ticket is ever created. If your game needs cross-region matches for low-population modes during off-peak hours, build that as an explicit fallback in your own orchestration layer, after a configurable wait threshold, a backend job queries pools in adjacent regions and merges candidates manually, rather than baking cross-region queries into the default matchmaking path. This keeps the common case fast and only pays the cross-region latency cost on the rare matches that actually need it.

Open Match 2 vs Rolling Your Own Matchmaker

It’s a fair question for any team sizing this project: why adopt Open Match 2 at all instead of building ticket storage and pool queries directly on top of Redis without the extra service layer? The honest answer is that for a small, single-region game with simple matchmaking rules, a hand-rolled solution can absolutely be less work. Open Match 2 earns its complexity at scale, once you need horizontal scaling of the matching logic itself, multiple concurrent matchmaking profiles (ranked, unranked, co-op, each with different pool filters), and a standardized API that different teams (client, backend, live-ops) can all integrate against without re-deriving ticket semantics from scratch.

The other factor worth weighing is maintenance burden. A hand-rolled matchmaker is code your team owns forever, including every edge case around ticket expiry, partial match failures, and race conditions between concurrent MMF invocations. Open Match 2 pushes that operational surface onto an actively maintained open source project backed by Google’s gaming infrastructure team, with the tradeoff that you’re adopting software still in public preview rather than a decade-hardened internal system. For a new project with runway to absorb some API churn, that tradeoff usually favors adopting the framework. For a team shipping in six weeks with a simple 1v1 ranked queue, writing 200 lines of Redis-backed matching logic directly may genuinely be the faster, lower-risk path.

Open Match 1.x vs Open Match 2: What Changed

Teams already running Open Match 1.x in production are naturally asking whether the migration is worth it this early in OM2’s preview cycle. The table below summarizes the structural differences.

Aspect Open Match 1.x Open Match 2
Service architecture Separate Frontend, Backend, and Query microservices Single unified om-core binary
Latest tagged release v1.8.1 (December 13, 2023) Public preview, no formal tagged release identified as of October 2026
Ticket query model Separate Query Service, polled by MMFs Integrated into core via unified gRPC API
API style Multiple gRPC services, separate contracts Single consolidated OpenMatchService gRPC API with HTTP gateway
Redis deployment Bundled Helm subchart Memorystore or standalone YAML, documented as more flexible
Project status Mature, stable, minimal recent updates Active public preview, docs updated through September 2026

The practical takeaway: OM1 is the safer choice today if you need a battle-tested, versioned release for a live game shipping in the next few months. OM2 is the better long-term bet if you’re starting a new matchmaking pipeline from scratch and can tolerate API shifts during the preview period, since it’s clearly where Google’s gaming infrastructure team is directing active development.

Open Match 2 vs Agones: Two Different Jobs

This comparison trips up more teams than the OM1-vs-OM2 question, because both tools run on Kubernetes and both are part of the “connected games” stack Google Cloud promotes, but they solve entirely different problems.

Layer Open Match 2 Agones
Primary role Matchmaking data layer and MMF orchestration Dedicated game server hosting, allocation, and lifecycle management
Main input Tickets representing players or parties Game server fleet specs and allocation requests
Game-specific logic Implemented in your Matchmaking Function Implemented through GameServer and Fleet configuration
Output Matches and ticket-to-connection assignments A running, allocated game server instance
Typical pairing Feeds match IDs to a server allocator Receives allocation requests from a matchmaker

In a complete pipeline, a player’s client creates a ticket in Open Match 2, the MMF groups it with others into a match, your allocation logic calls Agones to reserve a server, and the resulting IP and port get written back to the ticket as an assignment. Neither tool replaces the other, they’re adjacent layers in the same stack. Teams comparing dedicated-server backends more broadly may also want to look at how Nakama’s game server setup and SpacetimeDB vs PlayFab vs Nakama handle session management, since those platforms bundle matchmaking-adjacent features that Open Match 2 deliberately leaves out.

Common Pitfalls

Five mistakes account for most of the frustration teams report when standing up Open Match 2 for the first time.

  • Mixing OM1 and OM2 documentation. Because both projects share a name and a maintaining organization, it’s easy to apply an OM1 Helm value to an OM2 deployment and get a confusing failure. Always check which repository a doc page or GitHub issue references before copying a command.
  • Treating the reference GKE cluster size as a hard requirement. The four-node n1-standard-4 example in the installation guide is just that, an example. Running it on a single small node for local experimentation works fine; running production traffic on four nodes without load testing first does not.
  • Skipping Redis replication. A single non-replicated Redis instance is a silent single point of failure. If it restarts mid-match-formation, every in-flight ticket and pool state disappears with it.
  • Writing an MMF that never terminates its stream. The Run method in the Match Function gRPC service is a server-streaming call. Forgetting to close the stream after sending all matches leaves connections hanging and eventually exhausts gRPC connection pools under load.
  • Assuming Open Match allocates servers. This is the most common conceptual error. Open Match 2 will happily form a “match” of five tickets and never connect them to anything if you haven’t wired up a separate allocation step with Agones or an equivalent system.

Advanced Tips for Production Matchmaking

Once the basic pipeline works, a few refinements separate a demo from a system that can survive a real launch.

Widen skill bands over time. A strict MMR filter that only matches players within 50 points produces excellent match quality but terrible queue times during off-peak hours. Have your MMF progressively widen the acceptable MMR range the longer a ticket has waited in the pool, a pattern sometimes called “elastic matchmaking.”

Shard pools by region first, mode second. Filtering a global ticket pool by mode before region means every MMF invocation scans far more tickets than necessary. Structure your pool filters so the highest-cardinality, most player-differentiating attribute (usually region) narrows the candidate set first.

Build backfill support early. Players disconnecting mid-match is not an edge case, it’s Tuesday. Design your assignment flow so a server can report “I need one more player” back into a new ticket pool rather than treating every match as a one-shot, immutable group.

Pin a specific OM2 commit in CI. Since there’s no stable tagged release as of this writing, pin your deployment manifests to a specific git commit hash rather than tracking the default branch, and test upgrades in a staging cluster before touching production.

Log every MMF decision, not just the final match. When a player complains their queue took four minutes, you want to be able to reconstruct exactly which pool filters ran against their ticket and why they weren’t grouped sooner. Emit a structured log line on every invocation cycle that includes pool size, filter criteria, and whether a match was formed, even when the answer is “no match this cycle.” Those negative results are what let you debug queue-time complaints after the fact instead of guessing.

Example Output: A Successful Match Cycle

Here’s what a healthy end-to-end cycle looks like in the logs, from ticket creation through assignment:

[om-core] INFO: ticket created id=t-8841 region=us-east mode=ranked-5v5 mmr=1310
[om-core] INFO: ticket created id=t-8842 region=us-east mode=ranked-5v5 mmr=1295
[om-core] INFO: ticket created id=t-8843 region=us-east mode=ranked-5v5 mmr=1340
[om-core] INFO: ticket created id=t-8844 region=us-east mode=ranked-5v5 mmr=1280
[om-core] INFO: ticket created id=t-8845 region=us-east mode=ranked-5v5 mmr=1315
[simple-mmf] INFO: pool size=5, forming match
[simple-mmf] INFO: match formed id=m-2291 tickets=[t-8841,t-8842,t-8843,t-8844,t-8845]
[om-core] INFO: assignment written match=m-2291 connection=34.82.11.9:7777
[om-core] INFO: tickets deleted count=5 match=m-2291
[metrics] time_to_match_ms=1840

A time-to-match under two seconds for a well-populated pool is a healthy sign. If you’re consistently seeing times climb past 10-15 seconds with similar pool sizes, work through the troubleshooting steps below before assuming it’s a capacity problem.

Troubleshooting

Eight issues account for the overwhelming majority of setup problems reported around Open Match deployments.

  1. om-core pods stuck in CrashLoopBackOff. Check kubectl logs for a Redis connection refused error first. Confirm the Redis service name and port in your values file match what’s actually running with kubectl get svc -n open-match.
  2. Tickets create successfully but never match. Verify your pool filter attributes exactly match the attribute keys used at ticket creation time, this is case-sensitive and a single typo (Region vs region) silently returns zero matches with no error.
  3. MMF invocation times out. Confirm the MMF service is reachable from om-core’s network namespace with kubectl exec into an om-core pod and a raw grpcurl call to the MMF’s ClusterIP.
  4. Helm install fails with a CRD error. Run helm upgrade --install instead of a fresh install if you’ve previously attempted and partially failed a deployment, leftover CRDs commonly block clean reinstalls.
  5. Redis memory grows unbounded. Confirm your matchmaking cycle actually calls DeleteTicket after successful assignment. Orphaned, never-deleted tickets are the most common cause of slow, creeping Redis memory growth.
  6. grpcurl commands hang indefinitely. Confirm you’re using the -plaintext flag during development; om-core doesn’t enable TLS by default in a local test deployment, and grpcurl will otherwise wait for a TLS handshake that never completes.
  7. GKE cluster creation fails with a quota error. New GCP projects often start with restrictive CPU quotas in a given region. Check Compute Engine quotas in the console and request an increase, or switch to a region with more available headroom.
  8. Match quality degrades under load testing. If synthetic load tests show your MMF forming low-quality matches (wide MMR spreads) under volume, your pool filter is likely too permissive for the traffic rate. Tighten the filter and rely on elastic widening instead of a permanently loose default.

Complete Working Project Structure

For reference, here’s how the pieces from this tutorial fit together as a complete, deployable project layout:

matchmaking-pipeline/
├── open-match2/              # cloned OM2 repo, pinned to a specific commit
├── values-override.yaml      # Helm values pointing om-core at Redis/Memorystore
├── mmf/
│   ├── main.go                # the Matchmaking Function from Step 6
│   ├── Dockerfile
│   └── go.mod
├── scripts/
│   ├── create-cluster.sh      # Step 1 automation
│   ├── deploy-redis.sh        # Step 3 automation
│   ├── deploy-core.sh         # Step 4 automation
│   └── load-test.sh           # Step 12 load generator
├── monitoring/
│   └── servicemonitor.yaml    # Prometheus scrape config from Step 11
└── README.md

Running scripts/create-cluster.sh, scripts/deploy-redis.sh, and scripts/deploy-core.sh in sequence, then deploying the MMF container from the mmf/ directory, reproduces the entire pipeline from this tutorial in about 30 minutes end to end, most of which is GKE cluster provisioning time rather than active work.

Cost Considerations for FinOps Teams

Matchmaking infrastructure is cheap relative to game server hosting, but it isn’t free, and it scales with ticket volume rather than concurrent player count, which catches some FinOps teams off guard during capacity planning.

Component Development setup Production baseline
GKE cluster (4x n1-standard-4) ~$0.60/hr while running Scales with node pool size and autoscaler policy
Redis In-cluster pod, no extra cost Memorystore standard tier, billed per GB provisioned
om-core replicas 1-2 pods Scales horizontally with ticket throughput, not player count
MMF compute 1 pod, minimal CPU Scales with invocation frequency and pool size per cycle

Shut the development cluster down with gcloud container clusters delete open-match-cluster --zone us-west1-a when you’re not actively testing, GKE bills for node uptime regardless of whether any matchmaking traffic is flowing through it. If you haven’t provisioned the underlying cluster yet, Tech Insider’s Google Kubernetes Engine setup guide covers GKE project configuration and node pool sizing in more depth than this tutorial’s quick-start commands.

Frequently Asked Questions

Is Open Match 2 production-ready in October 2026?
It’s explicitly labeled a public preview by its maintainers, with no stable tagged release identified as of this writing. Teams running it in production should pin a specific commit, run their own upgrade testing, and budget time for API changes between preview iterations.

Do I need Agones to use Open Match 2?
Not strictly, you could pair Open Match 2 with any server allocation system, including a custom one, or even a pool of always-on servers. But Agones is the most common and most documented pairing within the Google Cloud gaming ecosystem, and most tutorials and examples assume it.

What’s the difference between Open Match 1.x and Open Match 2?
OM1 splits matchmaking responsibilities across separate Frontend, Backend, and Query microservices. OM2 consolidates all of that into a single horizontally scalable om-core binary with a unified gRPC API, reducing deployment complexity and network hops.

Can Open Match 2 run outside Google Kubernetes Engine?
Yes. It’s a standard containerized application that runs on any Kubernetes cluster, GKE just happens to be the environment Google’s own documentation and examples target first, and it integrates most smoothly with Memorystore when staying inside Google Cloud.

What language does the Matchmaking Function need to be written in?
Any language with gRPC support works, since the MMF just needs to implement the Match Function service contract. The reference examples in the Open Match 2 repository are written in Go, which is why this tutorial follows that convention.

How does Open Match 2 handle player disconnects mid-queue?
It doesn’t automatically, your client or matchmaking orchestration layer is responsible for calling DeleteTicket when a player cancels or disconnects before a match forms, otherwise their stale ticket keeps occupying pool space.

Is Redis a hard dependency, or can I swap in another data store?
As of the current public preview, Redis (self-managed or via Memorystore) is the supported backing store for ticket state. There’s no documented, supported alternative backend at this time.

What happens if my Matchmaking Function crashes mid-invocation?
Tickets that were part of an in-progress, uncompleted invocation remain in the pool and become eligible for the next invocation cycle. No tickets are lost, but a crashing MMF under load will increase average time-to-match as cycles get wasted on failed invocations.

Related Coverage

Source: Tech Insider