Skip to content
HomeWorkBlogResume
Blog

Running a chess LMS in production

What I learned keeping live chess games running at ABC Chess, where serverless stops working and one bad line of JavaScript took the game server down.

October 11, 20263 min read
0

The first commit in the ABC Chess repo is "Initial commit from Create Next App", dated January 30. Today it is abcchess.org, India's chess platform from kids to masters, and coaches run real classes on it. Most of my commits since then start with "fix". This post covers the architecture, one difficult day in production, and what changed afterwards.

The platform

It is an all-in-one chess site: play, puzzles, lessons, and a side for coaches and schools. The website runs on Vercel and the data lives in DynamoDB, Amazon's database. Login started on Firebase and moved to Auth.js in September.

Live games need a long-running server

Almost everything on the site is request and response. You ask for a puzzle and the server answers. Vercel handles that well.

Live games are different. Two players share one board and a running clock, and something has to keep that clock going for the whole game. A Vercel function only lives for a few seconds. So live games run on one small always-on server, a 2 GB box on Amazon Lightsail, and every game's state is held in its memory.

That is simple, but it means I cannot just add a second server. Two servers would each hold half the games, and players would end up in rooms the other server cannot see. I wrote down how to scale it properly later. I haven't needed it yet, and I'm not sure I would get it right on the first try.

September 21 in production

Live games started failing with "Invalid auth token". The website signed Socket.IO tokens with AUTH_SECRET, and the live server on Lightsail was verifying them with CRON_SECRET. The day before, I had added a deploy fallback from one to the other, which made the mismatch look handled and fixed nothing. I synced the secrets, and now if auth fails, the player drops into watching the game instead of seeing an error.

The same day, the live server would not start. I had written this:

javascript
Number(rawTc.initial ?? process.env.WS_DEFAULT_INITIAL_MS || 600_000)

JavaScript does not allow mixing ?? and || without parentheses. It is a syntax error, so Node refuses to run the file at all. My deploy script also only waited two seconds before checking the server, so it did not catch the failure properly. I fixed both.

None of this broke locally. All of it broke in production.

Auth for schools

The newer work is for schools. The login page now has three entry points: individual, coach and school, and schools get class logins for their students. That changed how I think about auth. "Who is this" is easy. "Who is this, which class are they in, and who is allowed to see their games" is the hard part.

Takeaways

  • Keep stateful, long-running work (like a game clock) off short-lived serverless functions.
  • In-memory state limits you to one server until you design for more.
  • Shared secrets must match across every service that signs or verifies tokens.
  • Deploy checks need to wait long enough to catch a server that never starts.
  • I still don't know if one 2 GB box is a smart choice or just a cheap one. Right now it is both.
0
All posts

SreeCharan Desu

Engineer at Manuscripts · Open to remote

HomeWorkBlogResume
Chat

© 2026 SreeCharan Desu. All rights reserved.