The simplest multiplayer architecture: one player hosts the match and runs the game simulation locally. Other players connect via a lobby service. No dedicated server, no anti-cheat, no matchmaking. Demonstrates why client-hosted P2P fails for competitive play.
Designing a multiplayer game server is one of the most demanding system design interview questions because it combines real-time networking, state synchronization, anti-cheat concerns, and global infrastructure in a single problem. The naive client-hosted P2P approach is where every candidate should start — it establishes the baseline that makes the improvements in dedicated server architectures measurable and concrete.
The core challenge is enabling multiple players to interact in a shared game world in real-time. In the naive approach, one player (the host) runs the game simulation on their local machine. Other players connect directly to the host via UDP after finding each other through a simple lobby service. The lobby service is the only backend component — it stores lobby codes, player lists, and the host's IP address in a PostgreSQL database. Once the match starts, all game traffic is peer-to-peer and never touches the backend.
The P2P model's fatal flaw is the host advantage. The host player experiences zero network latency — their inputs are applied to the local simulation instantly. Other players experience full round-trip time (50-150ms) to the host, meaning they always react slower in close encounters. In competitive games, this asymmetry makes fair play impossible. A host with 0ms latency will win virtually every 50/50 engagement against a guest with 80ms latency.
The second critical flaw is the complete absence of anti-cheat. Since the host runs the game simulation, they can modify any game state — teleporting, invulnerability, aimbotting — without any server-side validation to detect or prevent it. Even non-host players can send forged inputs that the host may not validate. Production competitive games require server-authoritative simulation where no client is trusted, which is the approach taken by the Authoritative variant (v1).
The third flaw is host disconnection. If the host player crashes, rage-quits, or loses network connectivity, the match ends immediately for all players. There is no host migration in this naive approach — the game state exists only on the host's machine. Production P2P games implement host migration protocols, but these add significant complexity and still cause a 5-10 second interruption.
Despite these limitations, client-hosted P2P remains relevant for specific use cases: casual co-op games with trusted friends (e.g., Minecraft, Among Us early versions), LAN party play, and indie game prototypes where dedicated server infrastructure is not feasible. The architecture's simplicity — just 3 components — makes it the fastest path from zero to playable multiplayer.
This template makes the host advantage and anti-cheat gaps visible and quantifiable. Run the simulation and observe the latency asymmetry between host and guest players. Compare with the Authoritative variant to see how a neutral dedicated server eliminates the host advantage, and with the Global variant to see how regional servers and rollback netcode make competitive play fair across continents.
The naive multiplayer game system is a three-component architecture: PlayerClient, LobbyService, and LobbyDB (PostgreSQL). There is no dedicated game server, no cache, no event stream, no matchmaking algorithm, and no anti-cheat. The backend exists solely to coordinate lobby creation and joining — actual gameplay is entirely peer-to-peer.
All backend traffic flows through LobbyService, a stateless REST API running on 3 ECS Fargate pods with 50 threads each. It handles four operations: (1) lobby creation — generate a 6-character code, store host IP and game settings in LobbyDB, return the code for sharing; (2) lobby join — add a player to the lobby's participant list, return the host's IP address and current player list; (3) lobby polling — return the current lobby status (WAITING, IN_GAME, or EXPIRED) so joining players know when the host starts the match; (4) match start — transition the lobby status from WAITING to IN_GAME, triggered by the host player only.
LobbyDB is a single PostgreSQL instance storing lobby records. Each record contains the lobby code (primary key), host player ID, host IP address, game mode, max player count, current player list (JSONB), status, and creation timestamp. At 10K concurrent lobbies with approximately 200 bytes per record, the working set is under 2MB — trivially small. Lobbies expire after 300 seconds via a background cleanup job keyed on the created_at timestamp.
The gameplay data flow is entirely client-side. When the host starts the match, joining players detect the status change on their next poll (every 2 seconds) and initiate a direct UDP connection to the host's IP address. The host's game client runs the simulation at 30-60 Hz, receives inputs from connected players, applies game logic, and broadcasts state updates. All game state lives on the host's machine — the backend has zero visibility into what happens during a match.
The system has no redundancy at any layer. A single LobbyService deployment serves all lobbies. A single PostgreSQL primary handles all reads and writes. If either fails, new lobbies cannot be created and existing lobbies cannot be joined, but ongoing matches continue unaffected since they are P2P. This is one accidental benefit of the P2P model — backend outages do not interrupt active matches.
The concrete scaling ceiling is approximately 8K RPS at peak, limited by LobbyService capacity (3 pods x 50 threads x processing time). Since the backend only handles lobby coordination and not gameplay, the actual bottleneck is NAT traversal — many players behind symmetric NAT firewalls cannot accept incoming UDP connections, making them unable to host or connect. Production P2P games solve this with STUN/TURN servers, adding infrastructure complexity that this naive approach deliberately avoids.
Choice
One player runs the game simulation locally; other players connect directly via UDP
Rationale
Client-hosted P2P requires zero server infrastructure for game simulation. The host player's machine provides the compute, making it the cheapest possible multiplayer architecture. The trade-off is host advantage (0ms latency vs 50-150ms for guests), no anti-cheat (host controls the simulation), and host disconnect kills the match. The Authoritative variant eliminates all three problems with dedicated servers.
Choice
6-character lobby codes shared out-of-band instead of automated matchmaking
Rationale
Automated matchmaking requires skill rating, queue management, estimated wait times, and server allocation — significant backend complexity. A lobby code system is the simplest coordination mechanism: the host creates a room, shares a code via Discord or text, and friends join manually. No MMR, no queue, no algorithm. The downside is no skill-based matching and no way to find random opponents.
Choice
Clients poll GET /api/v1/lobbies/{code} every 2 seconds for status changes
Rationale
Polling on a simple GET endpoint is far simpler than managing persistent WebSocket connections with heartbeats, reconnection logic, and connection state tracking. At 10K active lobbies with 5 players each polling every 2 seconds, that is 25K RPS — well within a single service's capacity. The 2-second delay before detecting match start is acceptable for casual play.
Choice
One database instance for all lobby records with no Redis cache
Rationale
With a 2MB working set (10K lobbies x 200 bytes), the entire dataset fits in PostgreSQL's shared buffer cache. Adding Redis would add operational complexity for zero performance benefit at this scale. The lobby data is short-lived (300s TTL) and written infrequently compared to reads (60% polling). A single instance handles the full load comfortably.
Choice
No XP, no rankings, no match history, no player persistence
Rationale
A progression system requires durable player profiles, match result processing, MMR calculation, and leaderboard infrastructure. The naive approach stores nothing beyond the transient lobby state. When a match ends, no results are recorded. This makes the system stateless between matches — the simplest possible model. Progression is introduced in the Authoritative variant (v1).
Target RPS
~8K RPS (lobby operations only)
Latency (p99)
10ms lobby ops; 0ms host, 50-150ms guest game latency
Storage
~2MB (10K active lobbies, ephemeral)
Availability
~99% (single DB, but active matches survive backend outage)
| Operation | Time | Space | Notes |
|---|---|---|---|
| Create lobby (POST /api/v1/lobbies) | O(1) INSERT with random code generation | O(1) per lobby (~200 bytes) | Code collision check is O(1) via PK uniqueness constraint. Retry on collision (extremely rare at 36^6 keyspace). |
| Join lobby (POST /api/v1/lobbies/{code}/join) | O(1) SELECT + O(1) UPDATE (append to JSONB array) | O(P) where P = players in lobby (~50 bytes per player) | JSONB append is O(P) for rewrite but P is bounded at 16 max players. |
| Poll lobby status (GET /api/v1/lobbies/{code}) | O(1) SELECT by primary key | O(1) per response (~512 bytes) | Dominates traffic at 60% of requests. Single-row PK lookup, always in buffer cache. |
| Game simulation (host-side, not backend) | O(P) per tick where P = connected players | O(S) where S = game state size (~1-10MB depending on game) | Runs entirely on the host player's machine at 30-60 Hz. Not a backend operation. |
Stores transient lobby state for matchmaking coordination. Each row represents one active lobby with a unique 6-character code. Contains host player ID, host IP address, game mode, max player count, current player list (JSONB array), and status (WAITING/IN_GAME/EXPIRED). Read-heavy from status polling (60% of traffic). 300-second TTL via background cleanup. Single primary, no sharding, no replicas.
Indexes: idx_lobbies_status ON (status) WHERE status = 'WAITING', idx_lobbies_created_at ON (created_at) — for TTL cleanup
Working set ~2MB (10K lobbies x 200 bytes). Entire dataset fits in PostgreSQL shared buffer cache. Lobbies expire after 300 seconds.
| Variant | Tier | Latency | Throughput | Cost | Complexity | Reliability |
|---|---|---|---|---|---|---|
| Naive (Client-Hosted P2P) | T1 | 0ms host / 50-150ms guests | ~8K RPS (lobby ops only) | $100/month (1 DB, 3 pods) | Low — 3 components, no game servers | ~99% backend; match dies if host disconnects |
| Authoritative (Dedicated Servers) | T2 | 50-100ms all players (equal) | 60K RPS lobby + 100K matches | $15,000/month (dedicated game servers) | Medium — 10 components, Kafka, Redis | 99.9% (replicated, no host dependency) |
| Global Platform (Multi-Region + Rollback) | T3 | <30ms regional + client-side prediction | 100K RPS + 150K matches globally | $50,000/month (multi-region fleet) | High — 10+ components, rollback netcode, 5 regions | 99.9% (multi-region, replay, anti-cheat) |
This template is for educational and illustration purposes only. It may not represent the optimal production design for this problem. Real-world systems involve additional considerations (compliance, specific cloud provider constraints, organizational requirements) not captured here. Use this as a starting point for discussion, not as a production blueprint.
Multiplayer game servers combine real-time state synchronization (60 Hz tick rate), low-latency networking (sub-50ms requirement), anti-cheat (server-authoritative validation), global infrastructure (multi-region game servers), and scale (100K+ concurrent matches). It requires reasoning about UDP vs TCP, client-side prediction, rollback netcode, matchmaking algorithms, and the fundamental trade-off between P2P and dedicated server architectures. Companies like Riot Games, Epic Games, Valve, and Activision Blizzard ask this question because it maps directly to their production challenges.
Three reasons: (1) Host advantage — the host has 0ms latency while guests have 50-150ms RTT, making every close encounter unfair. (2) No anti-cheat — the host runs the simulation and can modify any game state (teleport, god mode, aimbot) without detection. (3) Host disconnect — if the host crashes or rage-quits, the match ends for everyone. Competitive games require a neutral, trusted authority — a dedicated server that all players connect to with equal latency, which validates every input server-side.
Dedicated server-authoritative game servers. Instead of one player hosting the simulation, a neutral server runs the game loop at 60 Hz. All players send inputs to the server, the server validates them (anti-cheat), simulates the game state, and broadcasts state snapshots. No player has a latency advantage. No player can cheat by modifying their local simulation. If a player disconnects, the match continues. This is the architecture used by Valorant, Fortnite, and every competitive multiplayer game.
Most home internet connections use NAT (Network Address Translation), which means the player's device has a private IP that is not directly reachable from the internet. For a player to host a match, incoming UDP connections must reach their machine, which requires either port forwarding (manual configuration), UPnP (unreliable), or STUN/TURN relay servers (additional infrastructure). Studies show 30-40% of players cannot host P2P connections without relay assistance, severely limiting the P2P model.
P2P is appropriate for: (1) Casual co-op games with trusted friends where cheating is not a concern (e.g., Minecraft with friends, Among Us). (2) LAN party play where all players are on the same network (0ms latency for everyone). (3) Indie game MVPs where dedicated server infrastructure is not feasible. (4) Turn-based or low-frequency multiplayer where the host advantage is negligible. The key factor is trust — if all players are friends, the anti-cheat and fairness concerns are less critical.
Sign in to join the discussion.
Ready to design your own Multiplayer Game Server?
Open the simulator, place components on the canvas, wire them up, and run a traffic simulation to see how your architecture performs under real load.
Open Simulator