minato Server Sharding Design¶
Status: Planning / Under Discussion¶
Problem Statement¶
As minato scales to support larger games (MMOs, massive SMP servers), a single GameServer may not be sufficient:
- Player limits: A single Minecraft server caps at ~100-200 players. An MMO might need 10,000+ concurrent players.
- Geographic distribution: Players in EU and US need low-latency servers. One server in EU means 150ms+ for US players.
- World size: A single persistent world can grow to terabytes. Splitting the world into shards reduces per-server storage.
- Failure domains: If one server crashes, all players are disconnected. Sharding limits the blast radius.
Question: How can minato support sharding while staying game-agnostic?
Core Principle¶
Sharding is a deployment pattern, not a game mechanic.
The operator provides infrastructure for sharding. The game/agent decides what a "shard" means:
- Minecraft: A shard could be a server in a BungeeCord/Velocity network
- MMO: A shard could be a geographic zone (EU-1, US-West-1)
- FPS: A shard could be a match instance (each match = one shard)
- SMP: A shard could be a dimension (overworld, nether, end as separate servers)
The operator never implements game-specific sharding logic (no "shard map", no "player migration", no "world splitting").
Use Cases¶
Use Case 1: Minecraft Server Network (BungeeCord/Velocity)¶
┌─────────────────────────────────────────┐
│ Proxy (BungeeCord) │
│ - Routes players to shards │
│ - Handles authentication │
│ - Manages cross-server chat │
└─────────────────┬───────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Shard-1 │ │Shard-2 │ │Shard-3 │
│Lobby │ │SMP-1 │ │SMP-2 │
│(hub) │ │(survival)│ │(creative)│
└────────┘ └────────┘ └────────┘
minato role: Deploy 3 GameServers + 1 Proxy. Inject shard topology so proxy knows where to route.
Use Case 2: MMO Zone Sharding¶
┌─────────────────────────────────────────┐
│ Game World (10k players) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ EU-West │ │ US-East │ │ APAC │ │
│ │ Zone-1 │ │ Zone-2 │ │ Zone-3 │ │
│ │ 3k plyr │ │ 4k plyr │ │ 3k plyr │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ Shared: Guilds, Auction House, Chat │
└─────────────────────────────────────────┘
minato role: Deploy 3 GameServers as shards. Each shard handles one zone. Shared state managed by external services (Redis, DB).
Use Case 3: Match-Based Games (CS2, Valorant-style)¶
┌─────────────────────────────────────────┐
│ Matchmaker Service │
│ - Creates match = 1 shard │
│ - Destroys shard when match ends │
└─────────────────┬───────────────────────┘
│
┌─────────────┼─────────────┬──────────┐
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│Match-1 │ │Match-2 │ │Match-3 │ │Match-4 │
│10 plyr │ │10 plyr │ │10 plyr │ │10 plyr │
│de_dust2│ │de_nuke │ │de_mirage│ │de_inferno│
└────────┘ └────────┘ └────────┘ └────────┘
minato role: Fleet with replicas=N, each replica = one match. Matchmaker creates/destroys GameServers via API.
Design Options¶
Option A: Fleet as Shard Controller (What we almost built)¶
Add shardMode to GameServerFleet. When enabled:
- Each GameServer gets
SHARD_ID,SHARD_COUNTenv vars - A ConfigMap is created with shard topology (addresses of all shards)
- The game/agent reads the ConfigMap to discover other shards
apiVersion: operator.minato.io/v1
kind: GameServerFleet
metadata:
name: minecraft-network
spec:
profile: minecraft-paper
replicas: 3
shardMode:
enabled: true
sharedConfig:
PROXY_ADDRESS: "minecraft-proxy.default.svc.cluster.local"
REDIS_HOST: "redis.default.svc.cluster.local"
Pros:
- Simple: One field enables sharding
- Game-agnostic: Just injects env vars and ConfigMap
- Works with any game that can read env vars
Cons:
- Limited: Only provides static topology. No dynamic shard creation.
- Doesn't handle proxy deployment (BungeeCord is a separate GameServer)
- Shard identity is tied to fleet index (shard-1, shard-2...), not game concepts ("EU-West", "Lobby")
Option B: Separate GameServerShard CRD¶
Introduce a new CRD that represents a logical shard:
apiVersion: operator.minato.io/v1
kind: GameServerShard
metadata:
name: minecraft-smp-1
labels:
minato.io/shard-group: minecraft-network
spec:
# References the fleet/parent
fleetRef:
name: minecraft-network
# Shard identity (game-defined)
shardId: "smp-1"
shardType: "survival" # lobby | survival | creative | ...
# Overrides from fleet template
env:
WORLD_NAME: "survival-world-1"
DIFFICULTY: "hard"
The GameServerShard controller creates the underlying GameServer with the shard identity.
Pros:
- Flexible: Shards can have different configs (one is lobby, one is survival)
- Game-defined identity: Shard ID is meaningful ("EU-West", "Lobby")
- Can be created/deleted independently (for match-based games)
Cons:
- More complex: New CRD, new controller
- User must manage shard lifecycle explicitly
- Overlap with GameServerFleet (both create GameServers)
Option C: Higher-Level GameServerNetwork CRD¶
A parent CRD that manages the entire network (shards + proxy):
apiVersion: operator.minato.io/v1
kind: GameServerNetwork
metadata:
name: minecraft-smp
spec:
# The proxy that routes to shards
proxy:
profile: minecraft-bungeecord
replicas: 2 # HA proxy
# Shard definitions
shards:
- name: lobby
profile: minecraft-paper
replicas: 1
env:
SERVER_TYPE: lobby
- name: survival
profile: minecraft-paper
replicas: 3
env:
SERVER_TYPE: survival
- name: creative
profile: minecraft-paper
replicas: 1
env:
SERVER_TYPE: creative
# Shared services (redis, db)
sharedServices:
- name: redis
type: Redis
- name: mysql
type: MySQL
Pros:
- Complete solution: Proxy + shards + shared services in one manifest
- Game-agnostic: Just defines topology, no game logic
- GitOps-friendly: One YAML = entire game network
Cons:
- Very complex: New CRD, lots of controllers
- Shared services (Redis, MySQL) are outside minato scope — how do we manage them?
- Over-engineered for simple use cases (one shard = one GameServerFleet)
Recommendation¶
Do NOT implement sharding in the operator yet. Here's why:
- Current Fleet is sufficient for 80% of cases: Most games need 1-3 servers, not 100 shards
- Sharding is game-specific: What works for Minecraft (BungeeCord) doesn't work for an MMO (zone-based)
- Agent/SDK can handle it: The agent gRPC API can expose shard topology to the game. The game decides what to do with it.
- premature optimization: We don't have users asking for 10,000-player worlds yet
Instead, enhance the existing Fleet with shard-friendly features:
Phase 1: Shard-Aware Fleet (Minimal Changes)¶
Add optional shard metadata to GameServerFleet (NO new CRDs):
apiVersion: operator.minato.io/v1
kind: GameServerFleet
metadata:
name: minecraft-network
spec:
profile: minecraft-paper
replicas: 3
# NEW: Optional shard metadata (just labels/env, no logic)
template:
metadata:
labels:
minato.io/shard-group: minecraft-network
spec:
env:
SHARD_COUNT: "3" # Agent can read this
The fleet controller already injects stable names (fleet-0, fleet-1). The agent can derive SHARD_ID from the pod name or hostname.
No code changes needed in operator. Just document the pattern.
Phase 2: Shard Topology API (Agent SDK)¶
Add an API to the agent SDK for shard discovery:
service Agent {
// Existing methods...
// NEW: Shard topology (implemented by game agent)
rpc GetShardTopology(ShardTopologyRequest) returns (ShardTopologyResponse);
}
message ShardTopologyRequest {}
message ShardTopologyResponse {
string shard_id = 1;
int32 shard_count = 2;
repeated ShardPeer peers = 3;
}
message ShardPeer {
string shard_id = 1;
string address = 2;
string status = 3;
}
The agent reads the fleet labels/env to discover its shard identity and peers. Game-specific agents (Minecraft, MMO) implement this differently.
Phase 3: GameServerNetwork CRD (Future)¶
When we have a real use case (e.g., a customer running a 5,000-player MMO), then design the GameServerNetwork CRD. Until then, document the pattern but don't implement.
What We Should Do Now¶
- Document the pattern: Add a doc showing how to use GameServerFleet for sharding (env vars, labels, DNS-based discovery)
- Enhance agent SDK: Add
GetShardTopologyto the agent gRPC API (optional, no-op default) - Profile examples: Add a Minecraft network example (fleet + proxy) to minato-games
- NO CRD changes: The existing GameServerFleet is sufficient
Open Questions¶
- Should the operator create proxy/load-balancer pods? Or is that external (e.g., Ingress, Istio)?
- How do shards discover each other? DNS (headless service), ConfigMap, or agent API?
- What about shared state? Redis, MySQL — should minato deploy these or assume they exist?
- Cross-shard player migration: Who handles this? Agent? Game server? Control plane?
- Auto-sharding: Should the fleet auto-create shards based on player load? Or is that game-specific?
Related Concepts¶
| Concept | minato Approach |
|---|---|
| Shard | One GameServer in a Fleet |
| Shard Group | One GameServerFleet |
| Shard Identity | Pod name (fleet-0, fleet-1) or env var |
| Shard Discovery | DNS (headless service) or agent API |
| Proxy/Router | Separate GameServer with its own profile |
| Shared State | External Redis/DB (not managed by minato) |
Status: Planning Date: 2026-05-28