← Back to blog
August 6, 2026Case Studies

How We Scaled Kamoto AI to 100,000+ Users on a Serverless Backend at 99.9% Uptime

#case-study#serverless#ai#scaling

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.

100,000+
active AI personas served
99.9%
uptime held through the growth curve
0
dedicated on-call engineers needed

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.

Persona message Stateless function (scales to zero)
Managed storage
Persona state & conversation history — never held in-process memory.
Model provider call
Timeout + fallback response — a slow provider never hangs the conversation.

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:

  1. 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.
  2. 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.
  3. 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.