Architecture
Big picture
Agones is two things: a set of CRDs (Custom Resource Definitions) that model game servers and their groupings, and a set of controllers that reconcile those resources into running Pods. The CRD types live under pkg/apis, and one controller package per resource lives under pkg/. A game server Pod carries an Agones sidecar (the SDK server) that the game binary talks to over gRPC, and the sidecar writes state changes back to the Kubernetes API. This keeps game code decoupled from Kubernetes: the binary only knows the local SDK.
Components
CRD types (pkg/apis)
The API types define the resources users and controllers exchange. agones/v1 holds GameServer (pkg/apis/agones/v1/gameserver.go:197), Fleet (pkg/apis/agones/v1/fleet.go:41), and GameServerSet (pkg/apis/agones/v1/gameserverset.go:40). allocation/v1 holds GameServerAllocation (pkg/apis/allocation/v1/gameserverallocation.go:52). autoscaling/v1 holds the FleetAutoscaler, and multicluster covers cross-cluster allocation.
GameServer controller (pkg/gameservers)
This owns the lifecycle of a single GameServer: allocating a port, creating the backing Pod, populating its address, and driving it to Ready. The per-resource reconcile entry point is syncGameServer (pkg/gameservers/controller.go:471).
Higher-level controllers (pkg/gameserversets, pkg/fleets, pkg/fleetautoscalers)
GameServerSet keeps a fixed number of identical game servers running (ReplicaSet analogue). Fleet manages rolling updates across GameServerSet generations (Deployment analogue). FleetAutoscaler scales a Fleet based on buffer or load policy.
Allocation (pkg/gameserverallocations)
A matchmaker claims one Ready game server and moves it to Allocated. This is a one-shot request resource (GameServerAllocation) rather than a long-lived object.
Port allocator (pkg/portallocator)
Agones assigns a Node HostPort to each game server itself instead of using a Kubernetes Service. The allocator tracks per-Node port usage (pkg/portallocator/portallocator.go:115).
SDK server (pkg/sdkserver)
The sidecar that runs in every game server Pod. It exposes the SDK gRPC service to the game binary and patches the GameServer resource on the binary's behalf (pkg/sdkserver/sdkserver.go:360).
How a request flows
Trace a GameServer from creation to Ready. The reconcile loop syncGameServer (pkg/gameservers/controller.go:471) calls each state-specific sync function in one pass; each function returns early unless the resource is in its state, so a single reconcile can advance several steps.
syncGameServerPortAllocationState(pkg/gameservers/controller.go:565) assigns a dynamic HostPort withc.portAllocator.Allocate(pkg/gameservers/controller.go:570) and advances the state toCreating(pkg/gameservers/controller.go:572). If the update fails it returns the port to the pool withDeAllocate(pkg/gameservers/controller.go:580).syncGameServerCreatingState(pkg/gameservers/controller.go:589) creates the backing Pod viacreateGameServerPod(pkg/gameservers/controller.go:683), then advances toStarting(pkg/gameservers/controller.go:631).syncGameServerStartingState(pkg/gameservers/controller.go:916) looks up the Node by the Pod'sNodeName(pkg/gameservers/controller.go:942), writes the external address and port (pkg/gameservers/controller.go:947), and setsScheduled(pkg/gameservers/controller.go:954).- The game binary calls
Ready()over the SDK. The server side isSDKServer.Ready(pkg/sdkserver/sdkserver.go:540), which enqueues aRequestReadytransition (pkg/sdkserver/sdkserver.go:543). The actual write happens inSDKServer.updateState(pkg/sdkserver/sdkserver.go:360), which setsgsCopy.Status.State = s.gsState(pkg/sdkserver/sdkserver.go:396). syncGameServerRequestReadyState(pkg/gameservers/controller.go:967) detectsRequestReady, fills in any missing address, and finalizes the state toReady(pkg/gameservers/controller.go:1014), recording anSDK.Ready() completeevent (pkg/gameservers/controller.go:1023).
The state constants this path walks are defined in order in pkg/apis/agones/v1/gameserver.go: PortAllocation (:49), Creating (:51), Scheduled (:57), RequestReady (:59), Ready (:62).
Key design decisions
The controller assigns HostPorts directly rather than relying on a Kubernetes Service or NodePort. Game traffic is mostly UDP (User Datagram Protocol) and clients connect straight to a Pod's host port to minimize latency. The allocator keeps a per-Node bitmap of used ports (pkg/portallocator/portallocator.go:119) and rebuilds it from informer state at startup.
The reconcile loop chains state-specific sync functions in one pass (pkg/gameservers/controller.go:471). One transition becomes the entry condition for the next function, so a work item can move through several states without re-queuing. Each sync guards on its own state at the top, for example syncGameServerPortAllocationState returns early unless the state is PortAllocation (pkg/gameservers/controller.go:566).
Game binaries never touch the Kubernetes API. They talk only to the local SDK sidecar, which patches the resource (pkg/sdkserver/sdkserver.go:419). This decouples game code from Kubernetes credentials and API shape.
Extension points
- CRDs:
GameServer,Fleet,GameServerSet,FleetAutoscaler,GameServerAllocationare the public API surface, consumed with standard Kubernetes tooling. - SDKs: client libraries for Go, C++, C#, Rust, and Node.js under
sdks/, used from game engines such as Unity and Unreal to callReady,Allocate,Health, andShutdown. - Allocation API: matchmakers integrate either through the
GameServerAllocationCRD or the Allocator gRPC service. - Multiple binaries:
cmd/shipscontroller,allocator,extensions,ping,processor, andsdk-server, with the controller entry point atcmd/controller/main.go:119.