When I type into Claude Code, my laptop runs the agent and the model runs on a GPU somewhere far away. We have an agent loop that reads files, runs tests, edits files, and uses the CPU to complete this work. The model is a GPU fleet shared by thousands of other people at the same moment, and coding agents use about a thousand times the tokens of a chat, which is mostly input. Most people think the AI is one thing, but it’s really two systems talking over HTTP. I’m trying to think about which one is actually hard to scale.
If I wanted to host my own models, say a few of them, for a lot of users, what would it actually take? Is it just Kubernetes plus GPUs? My intuition would be pods behind a load balancer and autoscaling on CPU, but that may not be the case. I’m going to dive in.
First, how’s an LLM API different from a normal one? A normal API request is short and stateless, while an LLM request is long, heavy, and holds onto memory the whole time it runs. A typical REST call finishes in tens of milliseconds, while an LLM response streams for seconds. A max-effort reasoning model can take minutes before the first answer token, and every in-flight request holds a KV cache on the GPU. That’s about 330 KB per token for a 70-billion-parameter Llama model with its KV cache at FP16. A 128K-token context is about 43 GB, a bit over half of an 80 GB H100.
In a normal API any pod can take any request, but I’m thinking that’s not true in the case of an LLM API because of this cache mechanism.
KV cache stands for key-value cache and is the model’s working memory of everything it has already read in a conversation. It lives in GPU memory on one replica, which is one machine or a group of GPUs that the model is split across. If a subsequent turn in a conversation with an LLM is sent to a different pod, then that pod has none of the key-value cache, which causes a reread of the whole conversation from scratch, which we call a re-prefill. For a 100k-token Claude Code session, that’s the difference between a fast reply and a long pause, and a cache read price versus a full input price.
What’s Actually on the GPU
So what’s actually on the GPU? Generating each new token means reading the whole model out of GPU memory, so for a regular dense model, generation speed is set by memory bandwidth and not raw compute. (Mixture-of-experts models like DeepSeek only read the experts each token uses, but the idea’s the same.)
I’ve learned there are two phases:
- Prefill, which reads your whole prompt at once and is limited by compute.
- Decode, which produces one token at a time and is limited by memory bandwidth.
The rough ceiling for one user is bandwidth divided by model size. An H100, which moves about 3.35 TB/s, with a 70-billion-parameter model at FP8, gives about 48 tokens per second. An H200 has 141 GB at 4.8 TB/s, and a B200 has about 180 GB at 8 TB/s. These are expensive.
So that’s why VRAM is the whole hardware conversation. More VRAM means a bigger model fits, with room left over for everyone’s KV cache. Faster VRAM means more bytes per second come off the memory, and since every token needs a full read of the weights, more bytes per second is more tokens per second. So why does quantizing to FP8 or FP4 make the model faster and not just smaller?
What are FP8 and FP4, and why does smaller mean faster?
- A model is billions of numbers (weights). FP16, FP8, and FP4 say how many bits store each one: 16 bits (2 bytes), 8 bits (1 byte), 4 bits (half a byte). “FP” is floating point, like scientific notation in binary: a sign, an exponent, and a few digits of precision.
- A 70B model is 140 GB at FP16, 70 GB at FP8, about 35 GB at FP4.
- Faster reason #1: decode reads every weight once per token. Half the bytes means half the reading, so roughly double the tokens per second. At one H100’s 3.35 TB/s, every 70 GB of weights costs about 21 ms per token. A 70B model at FP16 is 140 GB, which doesn’t even fit on one H100, so you split it across two and pay in GPUs instead.
- Faster reason #2: the GPU’s tensor cores do more low-precision math per cycle. H100 does about 2x the FP8 math of FP16. Blackwell (B200) adds native FP4. This one mostly speeds up prefill, since that’s the compute-bound phase. Decode is still waiting on memory.
- Faster reason #3: smaller weights leave more memory for KV cache, so more users fit in a batch (more on batching below).
- The cost: less precision. Four bits only hold 16 distinct values. FP4 formats keep quality by sharing a scale factor across small blocks of weights (NVFP4 uses blocks of 16). FP8 is usually close to lossless. FP4 needs more care.
B200 pricing: about $4.89 (DeepInfra) to $14 (AWS) per GPU-hour rented, and a DGX B200 box (8 GPUs) is about $400k.
Quantizing is like compressing the model, so there’s less to haul out of memory on every single token. Is a slightly less intelligent model at twice the speed a good deal? I guess it depends on your use case.
Before you can scale anything, you have to fit the model on one machine. Weights = parameters × bytes. A 70-billion-parameter model is 140 GB at FP16 (two or more H100s) or 70 GB at FP8 (one H200 with room left over).
DeepSeek V3/R1 is 671 billion parameters and won’t fit on 8 H100s, which is 640 GB. It needs 8 H200s. Wow, that is massive when you think about it relative to a normal personal computer, even a beefy one. GPT-OSS-120B fits on one 80 GB GPU.
Splitting a model across GPUs can run over PCIe (about 128 GB/s counting both directions), but it’s slow, because the GPUs swap results constantly. NVLink (900 GB/s to 1.8 TB/s, also counting both directions) is what makes it fast. That’s why the unit is an 8-GPU box or a 72-GPU NVL72 rack.
Nerd corner: GPU boxes and racks
- The 8-GPU box (NVIDIA HGX/DGX): eight GPUs on one board, all wired to each other through NVSwitch chips. Any GPU can talk to any other at full NVLink speed (900 GB/s per GPU on H100, counting both directions). A DGX B200 draws about 14 kW, roughly a dozen homes’ worth.
- Tensor parallelism (inside a box): split every layer’s math across the 8 GPUs. Each GPU holds 1/8 of each layer. After every layer, they swap partial results. That happens dozens of times per token, so in practice you keep it on NVLink. It’ll run over PCIe, just a lot slower.
- Pipeline parallelism (across boxes): box A holds layers 1–40, box B holds 41–80. Data crosses the slower network (InfiniBand, about 50 GB/s per GPU each way, roughly 9x slower than NVLink’s 450 GB/s each way on H100) only once per hop.
- Expert parallelism (for MoE models like DeepSeek): the model is many “expert” sub-networks and each token only uses a few. Different GPUs hold different experts, and tokens get shipped to the ones they need.
- The NVL72 rack: 72 Blackwell GPUs in one NVLink domain (130 TB/s), so it behaves like one giant GPU with about 13–20 TB of memory. About 120–140 kW, has to be liquid-cooled, about $3–4M.
The more of the model you can keep inside one fast wired box, the faster it runs, while processing over the network between boxes is the slow path. It’s interesting to think about a laptop with one GPU versus a frontier model that runs on a rack that weighs more than a car. The infrastructure to run these things is quite crazy.
So now let’s think about one user versus a thousand and what it takes to make this work. One user wastes almost the entire GPU. The economics only work when many people share each pass through the weights.
- At batch size 1, an H100 uses only about 27% of its memory bandwidth in one study.
- Continuous batching: Orca slots new requests in at every step, which is about 37 times the throughput at the same latency.
- PagedAttention: vLLM cut wasted KV cache memory from 60–80% to under 4%.
- LMSYS halved its GPU count after switching.
This is a huge difference from my own laptop use. A single Claude Code session is a terrible GPU workload on its own. Serving a million people is cheaper per token than serving one, which makes sense because one user leaves most of the memory bandwidth unused.
Nerd corner: continuous batching, for people who've never touched a GPU
- Who LMSYS is: a research group led by UC Berkeley, with UCSD and CMU. They made Vicuna, ran Chatbot Arena (spun out in 2025 as LMArena), and built SGLang. vLLM came out of the same Berkeley lab.
- The bus analogy. Every decode step is a bus trip: the GPU hauls the entire model out of memory once and produces one token for every passenger aboard. The trip costs about the same whether 1 person rides or 64.
- Static batching (old way): the bus waits until it’s full, then nobody gets off until the longest rider is done. Short answers sit waiting on long ones.
- Continuous batching (Orca, vLLM): riders hop on and off at every stop. One finishes, a new request takes the seat on the very next step.
- PagedAttention: old engines reserved one huge seat per rider, sized for the longest possible answer, and 60–80% of that sat empty. vLLM hands out memory in small pages as the conversation grows, like an OS does with RAM, so more riders fit.
Why only 27%? The 48 tok/s from earlier is a theoretical ceiling that assumes the GPU reads memory at full speed. At batch 1, much of each step goes to launching small kernels and multiplying tiny matrices that can’t keep the memory bus busy, so real single-user decode falls well short of it. Batching is what lets the hardware get near its ceiling.
Worked example (idealized arithmetic, 70B FP8 on one H200, 141 GB at 4.8 TB/s, each user holding a 3k-token conversation):
1 user 48 users Bytes read per step 70 GB weights + 1 GB KV 70 GB weights + 47 GB KV Time per step ~15 ms ~24 ms Tokens per step 1 48 Each user sees ~68 tok/s ~41 tok/s GPU total ~68 tok/s ~1,970 tok/s Cost/M tokens at $3.49/hr ~$14 ~$0.49 $3.49/hr is DeepInfra’s dedicated H200 rate, Nov 2026. Same GPU, same hour, about 29x cheaper per token. Each user gets about 60% of the speed. Why 48 and not more? vLLM grabs 90% of the H200 by default, about 127 GB. The weights take 70 GB and the KV cache 47 GB, which leaves about 10 GB for the runtime itself. Memory is the real limit on batch size.
The simplest way to say it: the weights are the expensive part to move, and batching moves them once for everyone.
The trade-off curve that nobody seems to talk about is that you can make the GPU produce more tokens in total or make each user’s tokens come faster, not both.
At first I didn’t get why total tokens matter. I can see why you’d want each user’s tokens to come faster. But why care how many tokens the GPU makes in total? Turns out it has nothing to do with answer quality. It sets the cost. A GPU-hour costs the same no matter what, so the more tokens the GPU produces in that hour, the cheaper each one is and the more people one GPU can serve.
Here’s a real example. A B200 serving MiniMax does about 9,600 tokens per second per GPU when each user gets 43 tokens per second. Push each user up to 102 tokens per second and the GPU total drops to about 2,100. Cost per million tokens goes from 5 cents to 23 cents (InferenceX, November 2026).
Every provider is picking a point on this curve. Fast modes and premium tiers are just a different point. What point would you pick for your own product is the question at large.
Which brings me back to batching. I had a few questions. How do thousands of independent prompts get batched together, run on a GPU at the same time, and come back out as the right answer for each person? Why batch at all? Is the time lost sending the prompt over the wire to the GPU, or is it caching, or the GPU starting up? The deep dive below walks through each one.
How does batching actually work?
How independent prompts share one pass without mixing:
- The model’s math is mostly giant matrix multiplies. One user’s current token is a row of numbers. Batching just stacks 64 rows into one matrix and multiplies it against the weights once. Row 12 never touches row 13. They share the weights, not each other’s data.
- The one step that needs a user’s history (attention) runs per-sequence against that user’s own KV cache pages.
- Each output row is that user’s next token, and the server streams it back to their connection. The engine tracks which row belongs to which request.
Where the time actually goes: not the network (the prompt is a few hundred KB, gone in milliseconds) and not GPU startup (the model is already loaded and warm). It’s that fixed toll every step: hauling about 70 GB of weights out of memory, about 15–20 ms on an H200. That toll is the same for 1 rider or 64. Batching is splitting the toll.
Why you can’t have both: more riders on the bus means more KV cache to read and more math every step, so each step takes longer. Everyone still gets one token per step, just slower steps. Fewer riders means faster steps but you’re paying for empty seats. The batching table above shows both directions.
You don’t batch because requests are slow to arrive. You batch because every token costs a full read of the model. Might as well make that read count for everyone.
Between the API and the GPU
So what sits between the API and the GPU? It’s not a load balancer per se in front of a GPU. It’s a stack:
- Gateway: auth and rate limits per org: requests, input tokens, and output tokens per minute.
- Router: picks the replica that already has your prompt prefix cached (llm-d, the Kubernetes Gateway API Inference Extension, NVIDIA Dynamo). It routes on KV cache usage and queue depth, not round robin.
- Engine: vLLM, SGLang, and TensorRT-LLM do batching and memory paging on the GPU.
Big shops split prefill and decode onto separate GPU pools. DistServe reports up to 7.4x more requests within the same latency targets.
flowchart TD C["Client<br/>agent on a laptop, or a cloud sandbox"] G["API gateway<br/>auth · per-org rate limits (RPM / ITPM / OTPM)"] R["Inference router<br/>picks the replica with your prefix cached<br/>routes on KV-cache use + queue depth"] S["Engine scheduler (vLLM / SGLang)<br/>continuous batching · KV-cache pages"] P["Prefill GPUs<br/>read the whole prompt at once<br/>compute-bound → sets TTFT"] D["Decode GPUs<br/>one token per step for the whole batch<br/>bandwidth-bound → sets tokens/sec"] X["GPU fault (Xid 74/79, ECC)<br/>KV cache lost → re-prefill elsewhere<br/>node tainted and drained"] C -->|"HTTPS request"| G G -->|"429 if your limit is hit"| C G --> R R -->|"529 if the whole fleet is overloaded"| C R --> S S --> P P -->|"KV cache handoff (NVLink / RDMA)"| D D -->|"tokens streamed back over SSE"| C D -.->|"mid-stream failure"| X X -.->|"retry, or fall back to another model/provider"| R
Separate prefill and decode pools are the big-provider layout (Dynamo, llm-d). On a single vLLM box, both phases run on the same GPUs.
In a normal API, routing is dumb on purpose. Here routing is performance. Why does stateful routing scare you as an engineer? Sticky routing is basically just having to route back to the same pod as before. Why is that scary? Turns out there are a few reasons.
Why is sticky routing scary?
- Hot spots. Round robin spreads load evenly by design. Cache-aware routing deliberately piles requests onto whichever pod holds the cache. A popular system prompt, or one heavy user, can melt one pod while its neighbors idle.
- Blast radius. If a sticky pod is broken, the same users keep landing on it. That’s exactly what happened to Anthropic in 2025 (more on that below): a misrouting bug, and sticky routing sent follow-ups back to the bad servers, so some users got hit over and over.
- Losing a pod loses real work. In a stateless API a dead pod costs one retry. Here it wipes every cached conversation on it. Those users all re-prefill at once, which spikes load on the survivors right when you’re short a pod.
- Scaling out doesn’t help right away. A new pod has an empty cache. Existing conversations stay put, so the busy pods stay busy.
- Deploys and drains get harder. Rolling a pod means evicting warm caches or waiting for long sessions to finish.
- Two goals pulling against each other. Cache hits want “same pod,” load balancing wants “least busy pod.” The router has to trade them off constantly, and getting it wrong shows up as latency or cost, not as an error.
Sticky routing makes each server matter again. Cloud ops folks have an old way of putting this: servers are either pets or cattle. Pets have names, and when one gets sick you nurse it back. Cattle are numbered, and when one gets sick you replace it. Stateless APIs turned servers into cattle. Sticky routing turns them partway back into pets. The conversation itself isn’t lost, since the client re-sends it every turn. But each server holds warm caches that are expensive to rebuild, and losing one means a pile of re-prefills.
So, Kubernetes? Yes, but almost none of the default autoscaling behavior applies. Interesting. Google’s GKE guidance says don’t scale on CPU. Scale on queue depth or batch size. GPU memory looks full all the time because vLLM pre-allocates it.
Cold start is a killer: about 11 minutes for an 8-billion-parameter model in one BentoML test (image pull, unpack, load weights), and about 25 times faster by streaming weights instead.
OpenAI has written about running Kubernetes at 7,500 nodes (research and training clusters, 2021). Separately, in December 2024 a telemetry rollout overloaded the control plane, broke DNS, and took everything down for about four hours. In a normal API, scaling adds a pod in seconds. Here it’s minutes, and GPUs you might not be able to get. Do you scale at all, or just overprovision?
What is vLLM?
vLLM is an open-source inference engine from UC Berkeley (2023). You point it at model weights and it gives you an OpenAI-compatible HTTP server. It owns continuous batching and PagedAttention. On startup it grabs about 90% of GPU memory by default (
gpu_memory_utilization=0.9), loads the weights, and keeps the rest as a pool of KV cache pages. That’s why GPU memory looks “full” even when it’s idle, and why memory is useless as a scaling signal. Its rivals are SGLang (also from the LMSYS circle) and NVIDIA’s TensorRT-LLM.In short, vLLM is to the GPU what a web server is to a CPU: the thing that takes requests and keeps the hardware busy.
Cold start vs. streaming weights, in plain terms
- Cold start, the slow way: a new pod has to (1) pull a container image that often has CUDA libraries and the weights baked in, tens of GB, (2) unpack every layer to disk, (3) read the weights from disk into CPU memory, (4) copy them onto the GPU, (5) warm up. In BentoML’s test that was 5–6 min pulling, 3–4 min unpacking, about 2 min starting.
- Streaming weights: keep the weights out of the container image. The pod starts small, then reads the weight files straight from object storage or local disk onto the GPU in parallel chunks, loading while it’s still downloading. BentoML: about 25x faster. Google’s Run:ai Model Streamer: up to 6x.
- The analogy: downloading the whole movie before you press play, vs. streaming it.
- Readiness: a pod that’s still loading should report “not ready,” not get killed. Restarting it starts the 10-minute load over.
The short version: the model is a 70 GB file that has to be in GPU memory before the pod is useful, and cold start is all about how fast you can get it there.
Scaling vs. overprovisioning
- Overprovision: keep enough GPUs running for peak, all day. Simple and instant, but you pay for idle GPUs at 3am. Traffic follows the day, so average utilization can land somewhere around 20–40% (a rough estimate).
- Autoscale: add replicas when queue depth climbs. Cheaper on paper, but a new replica takes minutes to warm up, and in a GPU shortage the cloud may simply say no.
- What people actually do:
- A reserved baseline sized for normal load.
- Warm spares with weights already loaded.
- Burst capacity on on-demand or spot GPUs.
- Spill overflow to a hosted API when everything’s full.
- Shed or queue low-priority work, like batch jobs, before you turn users away.
- Scale-to-zero is fine for a dev endpoint and painful for anything users wait on.
In a normal API, autoscaling is how you save money. With GPUs, overprovisioning is how you buy reliability, and autoscaling is the backup plan.
If you want to watch this play out, I turned it into a short animated story on Kubedrills: The Model That Took Eleven Minutes to Wake Up. It follows a third replica from Pending to serving during a traffic spike, then replays the same morning with an always-on spare and with streamed weights.
Nobody serving at scale is running one model on one cluster. OpenRouter doesn’t own GPUs. It routes each request to one of many upstream providers based on price, throughput, time to first token, and 429 rate. It falls back on 5xx and rate-limit errors and keeps you on the same provider while your cache is warm.
Its rankings page shows the top model alone at about 56 trillion tokens in a week, and press reports put the total around 200 trillion a week this fall. Wow, that’s a ton.
Turns out picking a provider is harder than sorting by price. The same open model isn’t the same everywhere. Providers serve different quantizations, so one host might run FP8 and another FP4, and OpenRouter’s own docs warn that quantized models can do worse on some prompts. That’s the Anthropic misrouting story further down, at marketplace scale: same model name, servers configured differently, and a 200 every time. So OpenRouter’s default routing doesn’t rank on list price. It compares what a good response would actually cost at each provider, and it weighs throughput, time to first token, rate limits, reliability, and whether tool calls come back well formed.
It’s also the throughput-versus-speed curve from earlier, and you can see it on a model’s provider list. Every provider already picked its point. Specialty hardware hosts sit at fast and pricey, and the cheap hosts sit at slower streams. A router lets you pick your point per request instead of per vendor: sort by throughput when a person is waiting, sort by price when it’s a batch job.
Nerd corner: cache-aware routing, one layer up
- Same tension, bigger units. OpenRouter keeps a conversation on the same provider while its prompt cache is warm. That’s sticky routing again, just with providers instead of pods. Price routing wants whoever’s cheapest right now. Cache affinity wants whoever you used last.
- List price isn’t the price for agents. Caching support and cache-read pricing differ by provider. With a 92% cache hit rate, a provider with a higher list price and cheap cache reads can beat a cheaper one with no caching.
- A router is a retry layer. OpenRouter falls back on 5xx and 429s, walks your fallback list once, and doesn’t bill you when the whole chain fails. That’s great, but it counts as one of your layers (more on stacking retries below).
Google reported about 3.2 quadrillion tokens a month at I/O ‘26 on its own TPUs. Anthropic serves Claude on AWS Trainium, NVIDIA GPUs, and Google TPUs. AWS Bedrock routes across regions with cross-region inference profiles, or you buy provisioned throughput.
Three hardware platforms for one model is a choice. What does that buy you, and what does it cost?
What's a TPU, and why run one model on three kinds of chips?
What a TPU is: a Tensor Processing Unit, Google’s own chip built just for the matrix math neural networks run on. A GPU started life drawing graphics and got repurposed. A TPU was designed for this from day one. Google has used them internally since about 2015. Mostly you rent them through Google Cloud. At I/O 2026 Google split them into a training chip (TPU 8t) and an inference chip (TPU 8i). Amazon’s equivalent is Trainium. Anthropic signed for up to 1M Google TPUs in October 2025.
What three platforms buys you:
- Capacity. You’re not stuck in line for one vendor’s chips during a shortage.
- Price leverage with every supplier.
- Distribution. You can sell through the clouds customers already pay for (Bedrock, Vertex), next to their data.
- Resilience. One platform’s bad day isn’t your outage.
What it costs:
- Three different compiler and kernel stacks: CUDA for NVIDIA, XLA for TPU, Neuron for Trainium.
- The same model has to produce the same quality on all three, and tiny numerical differences turn into bugs. Anthropic’s 2025 top-k bug only existed on the TPU stack (more on that below).
- Every optimization gets built three times.
Running one model on three chip families is like shipping one app on iOS, Android, and web: more reach, three times the ways to break. Sounds like if you can handle the complexity, you get benefits, but those benefits come with a cost.
When the routing is wrong, the scariest failures aren’t errors. They’re wrong answers served with a 200. I first assumed that hitting the wrong pod, or the wrong cache, would give you a wrong answer. Turns out a cache miss mostly just costs speed, because the new pod re-reads your prompt. Wrong answers come from landing on servers that are configured differently, which is what happened to Anthropic.
In Anthropic’s 2025 postmortem, some Sonnet 4 requests were misrouted to servers configured for 1-million-token context. That peaked at 16% of requests in one hour. About 30% of Claude Code users hit it at least once. Sticky routing made it worse because follow-ups went back to the same bad server. A separate TPU compiler bug corrupted token selection.
Sticky routing is good for caching and bad for blast radius. How do you even detect that quality went down in monitoring?
Does a routing mistake give you a wrong answer?
Wrong pod vs. wrong server: a routing mistake usually costs speed, not correctness. Land on a pod without your cache and it just re-reads your prompt, and the answer is still right. Anthropic’s case was worse because the wrong pool was configured differently (long-context servers), and separately a misconfigured TPU server and a compiler bug changed which tokens got picked. Users saw Thai or Chinese characters in English replies, and broken code. The server returned 200 every time.
Why monitoring missed it: Anthropic wrote that its evaluations “simply didn’t capture the degradation users were reporting” and that it “relied too heavily on noisy evaluations.” Privacy controls also kept engineers from reading the bad conversations to reproduce them.
How you’d detect it:
- Canary prompts: known questions with known answers, sent continuously to every server pool and hardware type, scored automatically. Anthropic’s fix: run evals “continuously on true production systems.”
- Compare pools against each other: if TPU replies differ from GPU replies on the same prompts, something’s off.
- Cheap output signals per replica: wrong-language characters, response length, refusal rate, token-probability statistics.
- User signals: thumbs-down, regenerate rate, people abandoning sessions. Slow, but it’s what finally caught this one.
A 500 error pages someone. A worse answer with a 200 pages nobody, so it can go unnoticed for weeks while things go downhill for everyone.
Latency and Failures
For LLMs you measure two latencies: how long until the first token, and how fast tokens keep coming. Those are the important ones: time to first token (TTFT) and time per output token (TPOT).
Low-effort frontier models take about 3 to 9 seconds to the first answer token. Max-effort reasoning takes minutes (Artificial Analysis, early November 2026). The tail comes from queuing behind long prompts, prefill stalling everyone’s decode, and preemption: when KV memory runs out, vLLM evicts a request and later reruns its entire prefill. Chunked prefill gets 2.6–5.6x more capacity within tail-latency limits.
In a normal API, p99 is a slow database query. Here it’s someone else’s 100k-token prompt landing in your batch. That’s a strange thing to explain in a planning meeting, so the callout below has a grocery-store version, plus what you can actually do about big prompts.
Tail latency, in plain terms
p50/p95/p99, quickly: line up every request from fastest to slowest. p50 is the one in the middle. p99 is the one slower than 99 out of 100. At a million requests a day, “1 in 100” is 10,000 requests a day.
The grocery-store version for a PM: you have two items. The GPU is one checkout lane that serves everyone a little at a time. A 100k-token prompt is someone pulling up with three full carts. Before chunked prefill, the cashier rang up all three carts before scanning anyone else’s next item. Everyone in line, mid-conversation, froze. Your p99 isn’t about your request. It’s about who you happened to share a GPU with.
What a 100k prompt costs (rough arithmetic): the weights part of prefill is about 2 × parameters × tokens. For a 70B model, 2 × 70B × 100k ≈ 14 quadrillion operations. Attention adds about as much again at this length, because it grows with the square of the prompt (2 × 80 layers × 100k² × 8,192 ≈ 13 quadrillion). So call it about 27 quadrillion. Eight H100s peak around 8 quadrillion per second at BF16, so that’s about 3 seconds at perfect use and more like 6–7 in practice. Compare decode, where everyone else is getting a token every 20–40 ms.
What you do about it:
- Chunked prefill: slice the big prompt into pieces and mix them in between everyone’s decode steps, so nobody freezes. Sarathi-Serve reports 2.6–5.6x more capacity under tail-latency limits.
- Separate prefill and decode pools (DistServe): the big carts go to their own lane.
- Prefix caching: in an agent loop, about 95% of that 100k prompt is the same as last turn. Only the new part gets prefilled. This is why Claude Code works at all.
- Route by size: long-context requests go to a dedicated pool. (Anthropic’s misrouting bug above was exactly this pool split going wrong.)
- Price it: providers charge more for long prompts. Gemini Pro charges more above 200k tokens (input doubles) and Claude Haiku 5.5 costs more above 100k. Input-token rate limits (ITPM) cap how many big carts one customer can bring.
You can’t make a 100k-token prompt cheap, but you can stop it from making everyone else wait.
So how do you keep latency down?
- The biggest lever isn’t hardware: it’s not recomputing things you already computed.
- Prompt caching: cache reads cost 0.1x or less on Anthropic and OpenAI, and they skip prefill (the read-your-whole-prompt phase from earlier).
- Claude Code: about 92% cache hit rate in third-party data. Anthropic reportedly treats a drop in hit rate as an incident. That makes sense. Cache reads don’t count towards input rate limits, though cache writes still do.
- Speculative decoding: 2 to 3x faster decode, and the gain shrinks at large batch.
- Other levers: streaming (feels faster but it isn’t), smaller models or lower reasoning effort, FP8/FP4, region proximity, and specialty hardware (Cerebras around 1,000 tokens per second on a 1T model).
Caching is the same lesson as every other system I’ve built. It ties back to what I wrote in Where LRU Caching Actually Matters.
Each latency lever, defined
- Prompt caching: the provider keeps the KV cache from your last request for a few minutes. If your next request starts with the exact same text, it skips re-reading that part. One changed character early on (a timestamp in the system prompt) breaks everything after it, which is why Claude Code keeps its prompt prefix stable.
- Speculative decoding: a small, fast “draft” model guesses the next 4–5 tokens. The big model checks all of the guesses in one pass. Checking is like prefill, many tokens at once, so it costs about the same as producing one token. Keep the guesses that match, throw away the rest. The output has the same quality: mathematically the same distribution as the big model alone, and identical text when decoding greedily. It’s like an assistant drafting a sentence and the expert just nodding through the parts they agree with. Why the gain shrinks at large batch: speculative decoding spends spare compute, and a full batch has no spare compute left.
- Streaming: send each token as it’s made instead of waiting for the full reply. The answer takes the same total time, but you start reading at TTFT instead of at the end.
- Smaller model / lower effort: fewer bytes to read per token, and fewer hidden “thinking” tokens before the answer. Opus 5.5 at max effort took about 7 minutes to its first answer token, and at low effort about 7 seconds (Artificial Analysis).
- FP8/FP4: see the FP8 and FP4 deep dive above.
- Region proximity: a US coast-to-coast round trip is roughly 60–70 ms. That’s noise next to seconds of generation for one chat. But an agent making 50 sequential calls pays it 50 times.
- Specialty hardware: Cerebras and Groq keep the model in on-chip SRAM instead of HBM. SRAM is far faster but tiny per chip, so they spread a model across many chips. Decode is bandwidth-bound, so faster memory means faster tokens.
Every lever comes down to one of three moves: read fewer bytes, skip work you’ve already done, or start showing the answer sooner.
GPUs fail constantly at scale, and an LLM request can’t just be retried like a GET. Meta saw an unexpected interruption about every 3 hours on 16K H100s over 54 days. About half traced to the GPUs or their HBM memory. The common hardware errors are:
- Xid 48: ECC
- Xid 74: NVLink
- Xid 79: fell off the bus
Operators taint and drain the node, which means marking the machine so Kubernetes stops sending it new work, then moving whatever’s running somewhere else before it gets repaired. When a GPU dies, the request’s KV cache dies with it. It has to be re-prefilled somewhere else. Public APIs don’t promise to pick up a half-streamed answer where it stopped, so in practice the client starts over (more on why in the callout below).
- 429: you hit your limit
- 529: the whole service is overloaded
- 500: retry with backoff
Midstream errors can arrive after a 200.
Retries with jitter. Retry at only one layer: 5 layers that each try 3 times is 3⁵ = 243x load. Fall back to another provider or model.
What does a good failure look like to a user? This stuff is hard to grok, so here it is in plainer terms.
GPU failures and error codes, in plain terms
Why GPUs fail so much: each one is a hot, dense chip running near its limits around the clock. At 16,000 of them, “rare” happens every few hours. Meta still kept training productive more than 90% of the time, because the system expects failure.
The error codes, translated. Xid is NVIDIA’s version of a car’s check-engine code.
- Xid 48: the GPU’s memory flipped bits it couldn’t fix. Data may be corrupt, so stop trusting this GPU.
- Xid 74: the high-speed cable between GPUs in the box failed. The 8 GPUs splitting one model can’t talk.
- Xid 79: the GPU vanished from the computer entirely.
What happens to your request, as a phone call: you’re mid-conversation and the call drops. The person you were talking to has forgotten the whole conversation (the KV cache was on that GPU). A new person picks up and has to re-read the full transcript before saying a word (re-prefill). Worse, the model picks words with some randomness, so the new person might start the same sentence differently. That’s why you can’t splice the retry onto the half-answer you already saw. Most APIs just fail the stream and your client starts over. (Technically a server could resume by re-reading the prompt plus the tokens already sent. That’s a design choice, and public APIs don’t promise it. That’s reasoning, not something I found a source for.)
The status codes, as a restaurant:
- 429: you’ve used up your reservations for this minute. Wait the time in
retry-after.- 529: the kitchen is slammed for everyone. Back off and try again.
- 500: something broke in the back. One more try is fine.
- The spend-limit 429: your tab is maxed. Don’t retry.
- Error after a 200: the waiter said “coming right up,” then the dish hit the floor halfway to your table.
The retry rules, plainly:
- Backoff with jitter: wait a bit longer after each failure, plus a random extra so a thousand clients don’t all retry in the same second.
- Retry at one layer only: if your app, your gateway, the SDK, the router, and the engine each try 3 times, one failure becomes 3⁵ = 243 attempts piling onto a system that’s already struggling.
- The Anthropic SDKs retry twice automatically, so don’t stack your own loop on top without thinking about it.
- A router counts as a layer too. If requests go through a router that falls back across providers, that fallback is already your retry layer. Put the SDK’s retries and your own loop on top and you’re multiplying again.
- Fallback only works cleanly before the first token. Once tokens have streamed, switching providers means starting the answer over, same as the dropped phone call above. (That’s reasoning from how streaming works. The router docs I read don’t spell it out.)
What a good failure looks like to the user:
- It’s fast and clear: “overloaded, retrying in 8s.”
- It keeps what was already written instead of wiping the screen.
- It retries once quietly.
- It falls back to another model or provider and says so.
- It never shows a half-answer as if it were complete.
In a normal API, a failed request is a blip. Here, it throws away work the GPU already did, so the whole design is about wasting as little of that work as possible and telling the user plainly what happened when some of it gets lost.
Agents in the Cloud
So, the other half: agents in the cloud. Run the agent in the cloud and now you need two fleets: computers for the agent and GPUs for the model.
- Anthropic Managed Agents: a stateless “brain” harness and a disposable “hands” sandbox. Decoupling them cut p50 time to first token by about 60% and p95 by over 90%.
- Devin: same split, brain in their cloud, a VM per session.
- Codex cloud: the sandbox goes offline with secrets stripped.
- Copilot agent runs on GitHub Actions runners.
- Sandbox platforms (E2B, Modal, Vercel, Cloudflare, AWS AgentCore): microVMs or gVisor, starting in about 100 ms to 1 second, roughly 5 to 14 cents per vCPU-hour, sessions up to 8 to 24 hours.
The agent box is CPU-bound, stateful, and idle most of the time waiting on the model. The GPU fleet is shared and request-scoped. They scale on completely different signals.
Here’s how one Claude Code task touches both.
Walkthrough: one Claude Code task, "fix the failing test" (illustrative numbers)
- You type the prompt. The harness on your laptop (or in a cloud sandbox) builds a request: system prompt, tool definitions, CLAUDE.md, your message. Say 30k tokens. It sends it over HTTPS.
- GPU side. The gateway checks your org’s rate limits. The router finds a replica holding your prefix in cache. 28k tokens are a cache hit, and only 2k get prefilled. Decode streams back “Let me run the tests” plus a tool call:
npm test.- The GPU lets go. The response ends with
stop_reason: tool_use. That replica moves on to other people’s requests. Your KV cache stays warm for about 5 minutes.- Agent side. Your laptop runs
npm testfor 20 seconds. Pure CPU. The GPU fleet has no idea.- Back to the GPU. The harness appends the test output (5k tokens) and sends the whole conversation again. Cache hit on the first 30k, and prefill only the new part: the reply and tool call from step 2, plus the 5k of test output.
- Repeat. Read a file, edit, rerun tests: 20–44 steps for a typical task. Every turn re-sends everything so far. That’s why agent traffic is about 100 input tokens per output token and why caching is everything.
In the cloud version, step 4 runs in a microVM instead of your laptop. The harness might live in a separate service (Managed Agents). The sandbox sits idle during steps 2–3, which is why Vercel doesn’t bill for time spent waiting on I/O and Managed Agents bills per active session-hour.
The two fleets scale on different things:
- Sandboxes: how many sessions are open, and CPU/RAM/disk per session (up to 28 GB of memory per session in one study).
- GPUs: tokens per second, queue depth, and KV-cache space across everyone’s sessions.
- Where the time goes: in 5 of 10 agent apps studied, non-LLM work dominated latency. It depends on the job.
The agent is a long, mostly idle conversation. The GPU only sees it in short, expensive bursts.
What It Costs
Self-hosting is only cheaper if your GPUs stay busy, and even then, maybe not. The formula: GPU dollars per hour divided by tokens per hour actually produced (SemiAnalysis’s definition).
Llama 3.3 70B on a rented H100: $2.90 per hour, about 1,500 tokens per second. That’s 54 cents per million tokens at 100% busy, $1.08 at 50%, $5.38 at 10%. ($2.90 is DeepInfra’s dedicated-GPU rate, Nov 2026. At Lambda’s $3.99 on-demand it’s $0.74, $1.48, and $7.41.)
The same model on a hosted API (DeepInfra) is about 12 cents per million (blending input and output 8:1, like the benchmark), four times cheaper than your perfectly busy GPU (about six times at on-demand rates).
DeepSeek, API discounts, hidden costs, and one person at home
DeepSeek R1 on 8×B200 (rented at DeepInfra’s $4.89/GPU-hr, the cheapest dedicated rate found, Nov 2026. That’s $39/hr and about $28.5k a month whether you use it or not, at 4,610 tok/s per GPU):
- $0.29 per million at 100% busy, $0.59 at 50%, $2.95 at 10%.
- Hosted DeepSeek R1 (DeepInfra, R1-0528 at $0.50 in / $2.15 out) is about $0.68/M blended 8:1, so self-hosting beats it above roughly 43% busy.
- Against Claude Sonnet 5.5 ($2 in / $10 out, about $2.89/M blended) you win above roughly 10–15% utilization. But it’s not the same model, so the real question is whether the open model is good enough. And that $2.89 is chat-shaped with no caching. For agent traffic Sonnet’s closer to $0.39/M (see the agent math after the company table). That pushes break-even to at least 75% busy, and likely higher, because those self-host numbers come from short prompts and long agent contexts fit fewer users per GPU.
API discounts you’d have to rebuild yourself:
- Cached input 90–95% off.
- Batch API 50% off.
- Providers run at high utilization on the newest chips with FP4, so their cost per token is lower than yours.
Hidden costs of self-hosting:
- GPUs idle at night and on weekends.
- Headroom for peak, plus one spare node (N+1).
- About 700 GB of weights per DeepSeek replica to store and move.
- Months of tuning the serving stack.
- People: one infra engineer at about $250k fully loaded is about $29/hr, the price of roughly ten rented H100s. (That last one is an assumption.)
One person, one machine (illustrative):
Setup Effective $/M Assumes Mac Studio running a big open model ~$13 ~$9.5k box over 3 yrs, 20M tokens/month RTX 5090 box ~$5 ~$3.5k box over 3 yrs, 20M tokens/month gpt-oss-120b on a hosted API $0.15 in / $0.60 out Together One person keeps a GPU busy maybe 1–5% of the time. Local wins on privacy, offline use, and tinkering, not price.
API prices for the same capability drop about 10 times a year, so buying a GPU on a 4-year depreciation is a bet against the curve.
When does self-hosting actually make sense? Privacy, control, a fine-tuned model, steady 24/7 load. This is interesting when thinking about big companies who are thinking about self-hosting models to get off API prices. And Bedrock doesn’t get you out of API pricing. On-demand bills per token like any API, and provisioned throughput is reserved capacity by the hour, so it’s not self-hosting.
What if we have a company with, say, 1,000 engineers who work 9 to 5, Monday through Friday? It’s not really a 24/7 load. So I did the napkin math for that case.
Napkin math: 1,000 engineers, 9-to-5, Monday–Friday
Every input here is an assumption.
Option A: frontier API (Claude Code as-is).
- Anthropic’s docs put enterprise Claude Code at about $150–250 per developer per month (about $13 per active day, with 90% of users under $30/day).
- 1,000 engineers ≈ $150k–250k a month, about $1.8–3M a year. Zero ops.
Option B: self-host an open model (DeepSeek-class) on rented B200s.
- Concurrency: assume about 30% of engineers have an agent mid-turn at the busiest hour, so about 300 live sessions at 50–100k tokens of context each.
- Memory per session: DeepSeek’s compressed KV cache is about 70 KB per token, so a 100k-token session is about 7 GB. 300 sessions is about 2 TB of KV cache.
- Fit: one 8×B200 node has about 1.4 TB of memory, and about 0.7 TB of that goes to weights. But vLLM only grabs about 90% of memory, and the runtime needs some of that, so it’s more like 0.55 TB of KV cache per node. So roughly 4 nodes at peak, plus a spare. Call it 5–6 nodes with headroom for speed.
- Kept on 24/7: 5–6 × about $28.5k = $143k–171k a month.
- Shape of the week: 9-to-5 weekdays is 40 of 168 hours, about 24%. Kept on 24/7, the GPUs sit idle three-quarters of the week and you pay for them anyway.
- Scaled down nights and weekends: keep one node on around the clock as the baseline. It can’t be a smaller node, since the weights alone are about 0.7 TB, so that’s a full 8×B200 at DeepInfra’s dedicated rate, about $28.5k. Rent the other 4–5 nodes only for the workday: about 173 hours a month on-demand at about $6.69/GPU-hr (Lambda’s B200 rate per neurohive, Nov 2026) is about $37k–46k. That’s about $66k–75k a month, mixing a dedicated baseline with on-demand burst. It’s only true if the on-demand GPUs are actually there at 9am every Monday, which in a shortage they may not be. Lambda’s posted B200 rate goes to $7.99 on November 16, which adds about $7k–9k.
- Add people: 2–3 infra engineers on call ≈ $40–60k a month.
- Total: about $105k–135k a month (scale-down) or $185k–230k (always-on), for a model your engineers may like less than Claude.
Option C: mix them. Nothing says a company has to pick one model. A router can send easy turns, like reading files or summarizing test output, to a cheap open model and keep the frontier model for the hard reasoning. I haven’t tried this split myself, so I’m not putting a number on it. But it’s the option I’d look at before signing for GPUs.
One note on Option A: that $150–250 per developer is real spend, so caching’s already baked in. So don’t line it up against the list-price company table below. That table uses a different model, traffic shape, and scale.
Verdict: on paper, scaling down nights comes in under the API, about $105k–135k against $150k–250k, and always-on, about $185k–230k, lands inside the API’s range. But the cheaper number is optimistic. It assumes on-demand B200s show up every Monday, and it sizes the nodes by memory only. Nobody checked that 300 long sessions decode fast enough on those nodes, and if they don’t, you need more of them. So the real gap is probably smaller than it looks. Self-hosting only clearly wins if (a) the open model is good enough for your engineers, (b) you can reliably scale down nights and weekends, or (c) you have a non-cost reason: data can’t leave your network, you need a fine-tuned model, or you need guaranteed capacity. Workday-only traffic is close to the worst shape for owning GPUs. The strongest case to bring to leadership is privacy.
Finally, cloud or a machine in a closet. Renting GPUs is flexible. Buying them only wins if you keep them busy. H100 on-demand runs from about $4 per hour (Lambda, RunPod) to $11 to $12 an hour (GCP, Azure) (Nov 2026 prices). B200 runs about $5 to $14 an hour. A DGX B200 costs about $400K and draws about 14 kW. For how long?
An NVL72 rack is about $3 to $4 million and 120 to 140 kW, liquid cooled. Rough break-even for owning versus renting on-demand: about 25% to 35% sustained utilization, before staff and spares. At home: Mac Studio, DGX Spark, or RTX 5090 with Ollama for one person.
14 kW for how long?
kW is a rate, like miles per hour, so the 14 kW draw lasts as long as the box is busy.
- A year at full load: 14.3 kW × 8,760 hours ≈ 125 MWh. Add cooling overhead (PUE about 1.3) and it’s about 163 MWh, roughly what 15 US homes use.
- Power bill: about $16k a year at $0.10/kWh.
- Colocation: usually priced per kW per month ($150–450/kW), which works out to about $26k–77k a year for one box including power and space.
- Lifespan: these break-even numbers depreciate the box over 4 years. Hyperscalers book servers over 5–6 years, but GPU generations turn over every 1–2, and API prices fall about 10x a year. So 4 years is generous to ownership, not conservative.
Where does a small company land? Where would I land for my own applications? So I ran the numbers for three kinds of companies: an early-stage startup, a small company, and a large company.
Three companies, same math
Illustrative, using an 8:1 input:output blend and the prices above. Self-host rows assume Llama 3.3 70B on B200 at 6,127 tok/s per GPU (InferenceX, 50 tok/s per user) and on H100 at 1,496 tok/s. Those throughput numbers count input and output tokens together (InferenceX prices them per million total tokens on an 8k-in, 1k-out workload), the same way the API prices are blended. The owned row uses InferenceX’s B200 ownership cost of $1.73/GPU-hr, which assumes hyperscaler-volume pricing, so a normal company would pay more.
Early-stage startup Small company Large company Usage 1k users × 10k tokens/day = 10M/day 100k users = 1B/day 10M users = 100B/day Frontier API (Sonnet 5.5, ~$2.89/M, before caching) ~$870/mo ~$87k/mo ~$8.7M/mo Hosted open 70B (~$0.12/M) ~$36/mo ~$3.6k/mo ~$360k/mo Self-host, rented 1 H100 24/7: ~$2.1k/mo at ~8% busy 1 × 8×B200 node: ~$28.5k/mo at ~24% busy ~48 nodes at 50%: ~$1.4M/mo Self-host, owned (~$1.73/GPU-hr, hyperscaler TCO) n/a ~$10k/mo + people ~$485k/mo + people Where you land API, easily Hosted open model, or frontier API if quality matters Hosted open model is still cheapest. Owning runs about 35% above it, and about 18x under frontier list (chat-shaped, uncached) The pattern:
- You’re never competing with frontier API prices. You’re competing with neoclouds running B200s at high utilization.
- On price alone, the hosted open model wins every column, even against hyperscaler-volume ownership. Self-hosting makes sense for privacy, a fine-tuned model, or guaranteed capacity, and those reasons matter at any size.
For my side projects, and for most early-stage startups, it’s the left column.
That table has one problem for agents. It blends input and output 8:1 with no caching, which is a chat shape. Agent traffic runs about 100:1 with a cache hit rate around 92%, both from earlier in this post. Run Sonnet 5.5 through that shape and the price per million drops from about $2.89 to about $0.39, roughly 7x cheaper. Self-hosting moves the other way. Those throughput numbers come from 1k–8k-token prompts, and agent sessions sit at 50–100k tokens of context, so fewer users fit per GPU. So for agent workloads, the frontier column is a lot closer than the table makes it look. The large company’s owned GPUs come to about $0.16 per million ($485k a month over 3 trillion tokens). Against $0.39, that 18x gap shrinks to about 2.4x at most, before the long-context hit to self-host throughput.
Agent-shaped math for Sonnet 5.5
Prices from Anthropic’s pricing page: $2 input, $2.50 for a 5-minute cache write, $0.10 for a cache hit, $10 output, per million tokens.
- Input: 92% of it is a cache hit at $0.10, and the new 8% gets written to cache at $2.50. That’s 0.92 × $0.10 + 0.08 × $2.50 ≈ $0.29/M.
- Blend 100:1: (100 × $0.29 + $10) ÷ 101 ≈ $0.39/M.
- Same 100:1 shape, no caching: (100 × $2 + $10) ÷ 101 ≈ $2.08/M.
- The table’s 8:1, no caching: (8 × $2 + $10) ÷ 9 ≈ $2.89/M.
Open-model hosts get cheaper with this shape too, but caching support varies by provider, so I didn’t put one number on it.
To close, I’ll say that scaling LLMs isn’t Kubernetes plus GPUs. It’s memory management, stateful routing, and caching wrapped in a Kubernetes shell.
And hosting your own models brings all of that with it. You’ve got to run and tune the serving stack, build the prefix caching and cache-aware routing, handle cold starts and GPU failures, and keep people on call to maintain it. With an API, most of that comes out of the box. You turn on prompt caching, set your retries, and you’re serving. So when the prices come out about the same, the API is a pretty good deal, because that price already covers all the work you’d otherwise be doing yourself.
What should someone thinking about self-hosting do Monday? Start on an API. If you’re using open models, put a router in front so you’re not tied to one provider’s point on the speed and price curve. Measure your cache hit rate and utilization, and only self-host when the math says so. I’ve really enjoyed this learning session. Thinking about where I’ll go next, it seems like the routing is a big piece of this. I think I will continue to advance my knowledge around the details of the implementation and dive deeper into the things that make this work. I wanted to see Kubernetes as a wrapper around LLM inference in action, so I built a companion piece on my Kubedrills site: The Model That Took Eleven Minutes to Wake Up. Claude and I built it together, and its numbers come from the same sources as this post.
Numbers at a glance
Fact Value Source H100 / H200 / B200 memory 80 GB @ 3.35 TB/s / 141 GB @ 4.8 TB/s / ~180 GB @ ~8 TB/s NVIDIA, SemiAnalysis KV cache, Llama 3 70B FP16 ~320 KiB (≈330 KB)/token; 128k ctx ≈ 40 GiB (≈43 GB) arithmetic Batch-1 decode ceiling, 70B FP8 on H100 ~48 tok/s arithmetic Continuous batching (Orca) 36.9x throughput at same latency OSDI ‘22 PagedAttention KV waste 60–80% → <4% vLLM B200 throughput vs per-user speed ~9,600 → ~2,100 tok/s/GPU at 43 → 102 tok/s/user; $0.05 → $0.23 / M (Nov 2026) InferenceX Llama 3.3 70B per B200 ~10k tok/s at 50 tok/s/user with 1k-token prompts; 6,127 with 8k-token prompts NVIDIA / InferenceMAX; InferenceX run DistServe 7.4x requests or 12.6x tighter SLO arXiv 2401.09670 Sarathi-Serve 2.6–5.6x capacity under tail SLO arXiv 2403.02310 Cold start 8B on k8s ~11 min → ~25x faster with weight streaming BentoML OpenAI k8s outage 2024-12-11, 3:16–7:38pm PST OpenAI status Anthropic misrouting peak 16% of Sonnet 4 req/hour; ~30% Claude Code users Anthropic postmortem Claude Code cache hit rate ~91.9% Requesty (3rd party) Meta Llama 3 failures 419 unexpected / 54 days / 16,384 H100; ~half GPU+HBM Llama 3 paper 1M req/day 11.6 RPS avg (arithmetic); ~35 peak assumes 3x peak-to-average; ~350 concurrent streams assumes ~10 s per response arithmetic + stated assumptions Managed Agents decoupling p50 TTFT −60%, p95 −90%+ Anthropic eng Agent token multiplier ~1000x chat, mostly input; ~100:1 in/out (Manus) arXiv 2604.22750, Manus H100 $/hr $2.90 (DeepInfra) / $3.99 (Lambda) – $12.29 (Azure) neurohive (Nov 2026) DGX B200 ~$400k, 14.3 kW resellers Own vs rent break-even ~25–35% utilization vs on-demand arithmetic Batching example, 70B FP8 on one H200 1 user: ~68 tok/s, ~$14/M. 48 users: ~1,970 tok/s total, ~41 each, ~$0.49/M idealized arithmetic Claude Code enterprise spend ~$150–250 per developer per month Claude Code docs 1,000 engineers, 9-to-5 API ~$150–250k/mo vs self-host ~$105–135k (scale down nights) or ~$185–230k (24/7) napkin math Self-host Llama 70B, H100 @ $2.90 $0.54 / $1.08 / $5.38 per M at 100 / 50 / 10% util InferenceX + DeepInfra, arithmetic Hosted Llama 70B API ~$0.12/M blended (8:1 in:out) DeepInfra Self-host DeepSeek R1, B200 @ $4.89 $0.29 / $0.59 / $2.95 per M at 100 / 50 / 10% InferenceX, arithmetic Claude Sonnet 5.5 / Opus 5.5 $2/$10, $4/$20 per M in/out; cache read 0.05x Anthropic pricing Sonnet 5.5, agent-shaped ~$0.39/M (100:1 in:out, 92% cache hits, 5-min writes) vs ~$2.89/M (8:1, no cache) Anthropic pricing, arithmetic Owned GPU cost, hyperscaler volume H100 ~$1.17/GPU-hr, B200 ~$1.73/GPU-hr InferenceX API price decline ~10x/yr same capability a16z LLMflation, Epoch AI
Sources
Serving stack and providers
- OpenRouter, Provider routing, Model fallbacks, Reliability & failover, Rankings
- OpenAI, Scaling Kubernetes to 7,500 nodes (2021), Dec 11 2024 incident write-up
- Anthropic, A postmortem of three recent issues
- Google, Sundar Pichai I/O 2026
- AWS, Bedrock cross-region inference
- vLLM / PagedAttention, vLLM launch blog, SGLang, Orca
- NVIDIA Dynamo architecture, Gateway API Inference Extension, llm-d (Thoughtworks Radar), KServe 0.17
- GKE inference autoscaling best practices, BentoML cold starts
Hardware and hosting
- NVIDIA H100, H200; SemiAnalysis B200
- Cloud GPU pricing Nov 2026, AWS P5 price cut
- NVL72 cost, 120 kW liquid-cooled rack
- Llama 3 paper, GCP GPU Xid troubleshooting
- DeepSeek open-infra-index
Latency, errors, caching
- Artificial Analysis methodology, providers leaderboard
- InferenceX B200 vs H200, NVIDIA InferenceMAX, Lambda MLPerf 5.1
- DistServe, Splitwise, Mooncake, Sarathi-Serve, Speculative decoding
- Anthropic errors, rate limits, prompt caching; Claude Code prompt caching; OpenAI prompt caching
- Requesty coding-agent cache hit rates
- vLLM p99 preemption, AWS Builders’ Library: retries & jitter
Cost
- Anthropic pricing, OpenAI pricing, Gemini pricing
- DeepInfra, Together, Fireworks, OpenRouter Llama 3.3 70B, Lambda
- InferenceX: cost-per-million-tokens definition, Llama 70B H100, Llama 70B B200, DeepSeek R1 B200
- a16z LLMflation, Epoch AI price trends, The Price of Progress
- RTX 5090 benchmarks
Claude Code, Anthropic, and AWS
- Claude Code: manage costs (enterprise $/dev figures)
- Anthropic TPU deal (Maginative)
- Bedrock provisioned throughput
- EAGLE-3 on Vertex (LMSYS), Cerebras Kimi K2.6, Cache misses as incidents (Implicator)
- SWE agent steps/tokens (arXiv 2604.11641)
Agents in the cloud
- Anthropic, Managed Agents engineering post, Managed Agents docs
- Devin VPC, Codex cloud environments, Cursor cloud agent security, Copilot agent environment
- AWS AgentCore Runtime, E2B, Vercel Sandbox pricing, Cloudflare Containers limits, kubernetes-sigs/agent-sandbox
- From LLM Inference to Agentic Workloads, Sutradhara, Manus context engineering