Skip to main content

Overview

Lichess handles real-time communication through a separate WebSocket server called lila-ws, which communicates with the main Scala application (lila) via Redis pub/sub. This architecture enables horizontal scaling and handles millions of concurrent WebSocket connections.

Architecture Diagram

Why Separate WebSocket Server?

Benefits

Horizontal scaling: Run multiple lila-ws instances behind load balancer Isolation: WebSocket connections don’t consume resources from main app Resilience: Main app restarts don’t drop WebSocket connections Performance: Optimized for connection handling vs. business logic Language flexibility: Could rewrite in different language if needed

Trade-offs

Complexity: Additional service to deploy and monitor Latency: Extra hop through Redis adds ~1-5ms Consistency: Must handle Redis connection failures gracefully

lila-ws Server

Technology Stack

  • Language: Scala 3
  • HTTP: Netty-based HTTP server
  • Concurrency: Akka actors for connection management
  • Redis: Lettuce client for pub/sub
GitHub: lichess-org/lila-ws

Core Responsibilities

Maintain WebSocket connections for:Game rooms: Players and spectators watching a game
  • /watch/{gameId} - Spectate game
  • /play/{gameId} - Play in game
Tournament rooms: Tournament participants and viewers
  • /tournament/{tournamentId}
Study rooms: Collaborative analysis boards
  • /study/{studyId}
User notifications: Personal event streams
  • /user/{userId}
Lobby: Players seeking games
  • /lobby
Site-wide: Global announcements
  • /site
lila-ws routes messages between clients and lila:Client → lila:
lila → Client:
lila-ws tracks online users:
Used for:
  • “Online” indicators on profiles
  • Friend online notifications
  • Activity statistics

Connection Lifecycle

Redis Communication

Redis Channels

Lichess uses Redis pub/sub with predictable channel patterns: Site input (clients → lila):
Game channels (lila → clients):

Message Protocol

Messages use JSON with type discriminator:

Example: Game Move

1. Client sends move:
2. lila-ws publishes to Redis:
3. lila validates and processes move 4. lila publishes result to Redis:
5. lila-ws broadcasts to all game watchers

lila Integration

Socket Module

The main lila application communicates with lila-ws through the socket module:

RemoteSocket API

Round Module Integration

The round module uses sockets for live game updates:

WebSocket Message Types

Game Room Messages

Client → Server:
  • move - Player makes a move
  • rematch - Player offers rematch
  • takeback - Request takeback
  • draw - Offer/accept draw
  • resign - Resign game
  • abort - Abort game
  • moretime - Give opponent more time
  • palantir - Premium user sees opponent’s time usage
Server → Client:
  • move - Move made (includes fen, clock, etc.)
  • end - Game ended (winner, reason)
  • crowd - Spectator count changed
  • clock - Clock sync
  • gone - Player disconnected
  • goneIn - Player will be considered gone soon
  • premove - Premove acknowledged

Tournament Room Messages

Server → Client:
  • reload - Tournament data changed (refetch)
  • redirect - Player should join game
  • standing - Leaderboard update

Lobby Messages

Client → Server:
  • join - Join lobby pool
  • cancel - Leave lobby pool
Server → Client:
  • redirect - Game found, join it
  • pools - Pool counts updated

Scalability

Horizontal Scaling

Multiple lila-ws instances can run simultaneously:
Sticky sessions: Nginx routes user to same lila-ws node (optional) Failover: If node dies, clients reconnect to another node

Connection Limits

Each lila-ws instance handles:
  • 100,000+ concurrent WebSocket connections
  • 10,000+ messages per second
Production deployment typically runs 3-5 lila-ws instances.

Redis Performance

Redis pub/sub can handle:
  • 100,000+ messages/second
  • Sub-millisecond latency
Redis Cluster used for high availability:
  • 3+ Redis nodes with replication
  • Automatic failover
  • Sentinel for monitoring

Client-Side WebSocket

TypeScript Socket Client

Frontend uses a custom WebSocket wrapper:

Socket Library

Reconnection Strategy

  • Automatic reconnect: Exponential backoff (2s, 4s, 8s, …)
  • State recovery: Server sends full state on reconnect
  • Buffering: Queue messages while disconnected

Monitoring

Metrics

Key metrics tracked:
  • Connection count: Active WebSocket connections per node
  • Message rate: Messages/second in/out
  • Redis lag: Time between publish and receive
  • Reconnection rate: Clients reconnecting (indicates issues)
  • Error rate: Failed message parsing, Redis timeouts

Health Checks

See Also