Scaling LLM and ML Models to 300M Monthly Requests with Self-Hosting

storygraph · Media & Entertainment · 2024 · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.6 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: consumer recommendation and LLM infrastructure

StoryGraph needed to serve ML and LLM-powered book features to millions of users with a tiny team, while keeping cloud/API costs and user privacy under control.

Constraints:

Architecture

StoryGraph self-hosted its AI/ML stack on dedicated servers, including specialized recommendation, similarity, vision, and LLM models. The architecture evolved from naive file/watch-folder and load-balancer approaches into Redis queues for ML work, dedicated servers, distributed database infrastructure, HAProxy and PgBouncer on web servers, and self-hosted observability/logging systems.

Diagram confidence: reconstructed (reconstructed from prose).

flowchart LR
  A["StoryGraph application"] --> B["Redis ML job queue"]
  A --> C["Self-hosted LLM service"]
  A --> D["Self-hosted specialized ML models"]
  B --> D
  C --> E["Dedicated model servers"]
  D --> E
  A --> F["Local HAProxy layer"]
  F --> G["PgBouncer connection pool"]
  G --> H["Yugabyte database cluster"]
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
StoryGraph application Book tracking and recommendation product serving user requests. Redis ML job queue, Self-hosted LLM service, Self-hosted specialized ML models, Local HAProxy layer
Redis ML job queue Queue for coordinating recommendation and ML jobs across many workers. StoryGraph application, Self-hosted specialized ML models
Self-hosted LLM service Large language model used for preview and personalized reading features. StoryGraph application, Dedicated model servers
Self-hosted specialized ML models Recommendation, similar-user, mood, cover-image, and moderation models. StoryGraph application, Redis ML job queue, Dedicated model servers
Dedicated model servers Self-hosted compute capacity for AI/ML workloads. Self-hosted LLM service, Self-hosted specialized ML models
Local HAProxy layer Per-web-server load-balancing path to database infrastructure. StoryGraph application, PgBouncer connection pool
PgBouncer connection pool Connection pooling layer to reuse and control database connections. Local HAProxy layer, Yugabyte database cluster
Yugabyte database cluster Distributed database layer scaled to many database servers. PgBouncer connection pool

Data flow

User requests for recommendations, similar users, book previews, and content checks enter the application, queue or route to self-hosted ML/LLM services, run on dedicated servers, and return results without sending private user data to external LLM APIs. Database traffic is distributed through local HAProxy and PgBouncer layers to the Yugabyte/Postgres cluster.

3 supporting quotes
“every time someone loads a book page it sends a request to a large language model that's about 1 million requests on an average day”transcript, stated
“BR pop is smart enough to keep all of your servers in an orderly line you can have 100 servers all running that command”transcript, stated
“I just installed ha proxy on all the web servers and I told them all to connect to Local Host on the ha proxy port to get to the database”transcript, stated

Why this architecture

StoryGraph self-hosted because cost, privacy, and control outweighed managed-service convenience, especially for a freemium app making heavy LLM calls.

3 supporting quotes
“when I first brought Ai and ml to stereograph in 2019 I knew I wanted to host my own models for a few reasons the first was privacy”transcript, stated
“second is cost why pay someone else to do what I can do better”transcript, stated
“having many specialized custom models can take you a long way and are very easy to self-host”transcript, stated

Models used: self-hosted large language model · book mood classifier · recommendation models · similar-user models · book-cover vision model

Scale

Alternatives considered (roads not taken)

Use a watch folder for ML jobs

Rejected because: Multiple servers could pick up the same file and fight over work.

“what happens when two servers pick the same file to work on at the same time”transcript, stated

Route recommendation requests directly through load-balanced model servers

Rejected because: Long-running requests quickly exhausted available servers.

“these requests took about 30 to 60 seconds to run if more than a handful of people a minute requested a recommendation they were met with an error”transcript, stated

Use ChatGPT-4 APIs for LLM features

Rejected because: It would have compromised privacy and cost an estimated $770,000 per month.

“if I had simply uploaded our user data to chat gbt breaking our user trust and compromising their privacy would have been easy but not cheap”transcript, stated

Patterns

privacy-preserving-processing (privacy)

StoryGraph self-hosted LLM and ML features to avoid sending sensitive user data to third-party LLM APIs.

“we're self-hosting this at a fraction of the cost and without compromising the privacy of our users”transcript, stated

cost-aware-routing (production-operations)

The team optimized architecture around infrastructure and API economics, choosing self-hosting where managed services were too expensive.

“costs were completely under control I was making jokes with Nadia saying our servers were so stable”transcript, stated

batch-offline-processing (workflow)

Redis queues coordinate long-running recommendation and ML jobs across many workers.

“clearly I needed a proper queuing system and surprisingly it wasn't as complicated as I thought it would be redis”transcript, stated

latency-optimization (production-operations)

The January 1 debugging work traced slow requests to thread/session distribution rather than database CPU.

“are we running out of rails threads are there so many users hitting the site that even though the web server CPU is in the single digits”transcript, stated

Tradeoffs

LLM hosting

Chose self-hosted LLM and ML infrastructure over third-party ChatGPT/API calls for heavy features.

Rationale: Self-hosting preserved privacy and avoided extremely high per-request API costs.

“we're self-hosting this at a fraction of the cost and without compromising the privacy of our users this is the power of self-hosting”transcript, stated

Job coordination

Chose Redis queue workers over watch folders or direct load balancing to long-running workers.

Rationale: A queue safely orders work across many servers and avoids duplicate processing or server exhaustion.

“as soon as an item is added it directly goes to the first server in the list and the rest of the servers just move up their place in line”transcript, stated

Operational style

Chose self-managed dedicated infrastructure over managed cloud services for every subsystem.

Rationale: The team accepted operational complexity because dedicated/self-hosted systems made the economics work.

“we can run those services on our own for a fraction of the cost”transcript, stated

Gotchas (what bit them)

Naive distributed file claiming broke down for multiple ML workers.

Root cause: Multiple servers needed a safe way to coordinate jobs without fighting over files.

Resolution: Use a proper Redis queue rather than watch folders and timestamp fights.

“all servers involved would go into fight mode they would all compare their timestamp with the others and whoever had the lowest number wins don't do that”transcript, stated

Direct load balancing to long-running ML servers failed under modest concurrency.

Root cause: Recommendation requests took 30-60 seconds, exhausting the small server pool.

Resolution: Move ML jobs behind a queue so workers process jobs in order.

“if more than a handful of people a minute requested a recommendation they were met with an error no servers are available”transcript, stated

Session stickiness overloaded only a few web servers.

Root cause: An HAProxy cookie pinned users to the same servers, creating uneven session distribution.

Resolution: Remove the cookie setting so users distribute across servers.

“it keeps you connected to the same server for your whole session and it clicked I opened up Firefox checked my cookies and there it was”transcript, stated

Bottlenecks appeared at each new traffic threshold.

Root cause: Capacity dashboards for one layer hid limits elsewhere in the stack.

Resolution: Iteratively inspect queues, proxies, connection pools, server threads, and session behavior.

“whenever we reached a new threshold of traffic New bottlenecks came up that were nowhere near being close on my radar”transcript, stated

Evaluation

StoryGraph evaluated by production traffic survival, cost reduction, privacy preservation, queue behavior, server/database load, request latency, and whether users could access ML-powered features during January traffic spikes.

Metrics: monthly requests · monthly users · daily LLM requests · requests per second · server sessions · database CPU · API cost estimate

“here's a live view of what that looks like each new line and what you just saw is a live request”transcript, stated
“at proxy showed 700 to 600 sessions on servers 1 and two and only 20 sessions for the rest”transcript, stated

Outcomes

The case is unusually rich in operational failure detail, including several discarded architectures before the working self-hosted design.

“if you want to get to figure out how to get to 300 million requests as a oneperson AI devops team”transcript, stated
“I deleted the cookie setting in ha proxy replied to the most recent person who emailed us and crossed my fingers”transcript, stated