How to Publish the Game You Just Built on Free Tiers — Lessons From 7,000 Players and One Dead Database

2026-11-15来源 dev.to WebDevEnglish

Your game is finished. It runs, it's fun, you could show it to someone today.

So — how do you publish it?

That question stopped me for longer than I'd like to admit. Not the game part — the publishing part. Where does it live? Does a leaderboard mean renting a server? What happens to the bill, and to the game, if people actually show up?

I ship browser games as a solo developer, run them entirely on free tiers, and measure everything. This article is the best-practice path I can draw from those measurements — including the day ~7,000 players came, and the day one of my databases died.

Publishing is only three parts

  1. Static hosting — the shelf that delivers the game itself (HTML, images, code). Free and effectively unlimited on the major platforms.
  2. The API (a Worker) — the doorway that does the processing: returning the leaderboard, receiving scores.
  3. The DB — the record book that stores scores and rankings.

A visitor receives the game from static hosting. Only when the game needs the leaderboard does the browser call the API, and the API reads or writes the DB. That's the whole map.

The day it held: 7,000 players, 89.7% of the cap, zero errors

One of my games had a ~7,000-player day on Cloudflare Workers' free tier:

  • 89,720 invocations of the 100,000/day free cap (89.7%)
  • Errors: 0
  • The database (TiDB Cloud) ended the month at ~22% of its free RU pool

The design rule that made it hold: only the APIs call the Worker — merely opening the game page never does. If showing the game screen hits your server every time, raw page views eat your allowance. Restrict the server to API calls and consumption becomes proportional to how much the online features are used.

That gives you the number that actually matters — the per-player unit cost:

89,720 calls ÷ 7,000 players ≈ 12.8 calls per player 

And with a unit cost, you can compute your ceiling before you launch:

100,000 (daily cap) ÷ 12.8 (calls per player) ≈ 7,800 players/day 

The day it died: the database counts what it scans

Another game used Cloudflare D1 as its database. What I had missed:

D1 does not count the rows it returns. It counts the rows it scanned to find them.

The culprit was the leaderboard query — a prefix-match lookup that returned a small board and, behind the scenes, scanned an average of ~792 rows per call:

  • 6,402 executions → 5,070,000 rows scanned
  • Total reads that day: ~6,000,000 — past the 5,000,000 rows/day free cap
  • Writes: 143,000 — past the 100,000 rows/day cap as well
  • Result: 2,490 API calls (10.4%) ended in exceptions

And here's the failure mode nobody warns you about: it didn't go down all at once. It failed probabilistically, a few calls at a time — the kind of breakage you notice late.

That night I migrated the game from D1 to TiDB — 51,954 rows, ~10.2 MB — and verified the move three ways: row count, data size, and real play (a score submitted and appearing on the leaderboard).

The correction: counting method × reset unit

Whether a free tier holds is not decided by your expected player count. It's decided by:

  1. What the limit counts — requests / scanned rows / workload units (RU)
  2. The reset unit — daily vs monthly
Service What it counts Reset Free cap (at time of writing)
Cloudflare Workers Invocations Daily 100,000/day
Cloudflare D1 (reads) Rows read, including scanned Daily 5,000,000/day
Cloudflare D1 (writes) Rows written Daily 100,000/day
Cloudflare KV (writes) Writes Daily only 1,000/day
TiDB Cloud RU (converted workload) Monthly 50,000,000 RU/month

On my biggest day, Workers hit 89.7% of a daily cap while TiDB used only ~3% of its monthly pool. Daily-reset limits are the walls; monthly pools absorb spikes. Same players, completely different risk.

Compute your ceiling before you launch

  1. List your APIs — only the calls that reach the server consume anything.
  2. Measure unit costs — play your own game, count calls per play session.
  3. Measure the scans — for each main query, how many rows does one execution scan? Mark any query "unverified" until you know. Nothing ships as unverified.
  4. Write down caps and reset units — from the official docs, on launch week, not from memory.
  5. Divide — cap ÷ unit cost = your ceiling in players (per day or per month, matching the reset unit).

Design for the fall

Free-tier insurance is boring and effective:

  • Keep results and progress in localStorage, so a dead backend doesn't erase the player's records.
  • Prepare fallback displays: if the leaderboard can't be fetched, show a notice plus the player's own local records — never an error screen.
  • Never make starting the game depend on a server response.

One honest line: automatic re-sending of failed submissions is not implemented in my setup. "Saved on the device" and "will sync later" are different promises — don't blur them in your UI copy.

The pre-launch checklist

  • [ ] Displaying the game never calls the server; only APIs do
  • [ ] Calls per player measured and written down
  • [ ] Scanned rows checked for the main queries (especially the vague "search" ones)
  • [ ] Headroom known for every daily-reset cap
  • [ ] Fallback display and local saves tested by deliberately breaking the connection

The full guide

This article is the condensed version. The full guide — every calculation step shown, a copy-ready conversion sheet (filled example + blank) for your own game, the complete D1 → TiDB migration pattern, and the fallback design in detail — is on Gumroad for $5:

How to Publish the Game You Just Built — Best Practices for Shipping an Online Browser Game on Free Tiers

It was originally published in Japanese; this is the English edition. If you run the backwards calculation for your own game, I'd genuinely like to hear what ceiling you got.

原文:https://www.a8q9.cn/en/article/how-to-publish-the-game-you-just-built-on-free-tiers-lessons-from-7-000-players--3d8dc7.html

相关文章