How We Scaled Kamoto AI to 100,000+ Users on a Serverless Backend at 99.9% Uptime
Kamoto AI lets users create and chat with AI personas. When I joined as the engineer architecting its backend, the product had validated demand but not the infrastructure to match it — the team needed to go from a handful of active personas to six figures without a platform rewrite six months later.
The starting constraints
Three things shaped every decision:
- No dedicated ops team. Whatever we built had to run itself — no one was going to be on call patching servers at 2 a.m.
- Unpredictable load. Persona conversations spike unevenly; a single viral persona could 10x traffic to one endpoint overnight.
- Runway mattered. This was a lean team pre-scale — infrastructure spend needed to track usage, not sit idle waiting for load that might not come.
A traditional always-on server fleet fails on all three at once:
| Always-on server fleet | Serverless | |
|---|---|---|
| Idle cost | Pays for provisioned capacity 24/7 | Scales to zero between requests |
| Traffic spikes | Needs a human to plan/add capacity | Scales out automatically, per request |
| Ops burden | Patching, capacity planning, on-call | No servers to patch or manage |
| Cost model | Fixed capacity, usage-agnostic | Pay per request/execution |
Why serverless was the right call, not just the trendy one
We architected the backend on a serverless model — compute that scales to zero between requests and scales out automatically under load. For this specific shape of traffic, it mapped almost exactly to the constraints:
- Per-request billing matched per-persona usage. Cost scaled with actual conversation volume instead of provisioned capacity.
- Auto-scaling absorbed spikes without a human deciding to add capacity in the moment.
- No servers to patch or manage freed the team to spend engineering time on the product, not the platform.
The trade-off we accepted going in: cold starts and execution-time limits constrain how you design long-running or stateful work. That pushed us toward keeping request handlers thin and stateless, and moving anything long-lived (persona state, conversation history) into managed storage rather than in-process memory.
Holding 99.9% uptime
Serverless removes a class of ops problems, but it doesn’t remove the need for reliability engineering — it just moves where that discipline has to live:
- Idempotent, stateless handlers. Every function could retry safely without side effects, which matters when the platform itself decides to retry a cold or failed invocation.
- Fallback paths on every external call. Model providers and third-party APIs fail; every call to one had a timeout and a degraded-but-functional fallback, so a slow provider never took down a whole persona conversation.
- Usage-based alerting, not just error-rate alerting. Because cost and load are coupled in serverless, a sudden spend spike is often the earliest signal of a runaway loop or abuse pattern — we alerted on both, not just 5xx rates.
Points 1 and 2 in practice, in a single handler:
async def handle_persona_message(event, context):
# Idempotency key lets the platform safely retry this invocation
if await already_processed(event.idempotency_key):
return await get_cached_response(event.idempotency_key)
try:
reply = await call_model_provider(event.prompt, timeout=3.0)
except (TimeoutError, ProviderError):
reply = fallback_reply(event.persona_id) # degraded, but the conversation keeps moving
await save_conversation_turn(event.persona_id, event.prompt, reply)
await mark_processed(event.idempotency_key, reply)
return reply
That combination is what held 99.9% uptime through the growth curve to 100,000+ active personas, without a dedicated on-call rotation.
The side effect: a new revenue stream
The same GenAI pipelines built to power persona conversations turned out to be reusable as a standalone capability — we packaged part of that pipeline into a new monetizable product surface rather than treating it as internal plumbing. The lesson generalizes: when you build GenAI infrastructure properly — modular, with clean boundaries around the model-calling logic — it stops being a cost center you tolerate and starts being an asset you can productize.
What I’d tell a founder facing this same decision
If your usage pattern is spiky, your team is lean, and you don’t have (or want) a dedicated ops function yet, serverless isn’t a compromise — it’s the correct default. The discipline you still need — idempotency, fallbacks, cost-aware alerting — is cheaper to build in from day one than to retrofit once you’re already at scale.