Proof

Benchmarks

How the Wäg gateway performs against LiteLLM under identical conditions. This page sets out the method in full and publishes the measurements it produced. We do not publish multipliers.

8 vCPU Docker, pinned coresWäg v1.0.0LiteLLM v1.104.23 runs/scenario

How this was measured

Everything ran in Docker on one machine, each component pinned to its own cores so nothing competed for CPU.

  • Both gateways sit in front of the same upstream with identical model configuration, so neither gets an easier path.
  • Each gateway runs as its own documentation recommends: LiteLLM per its production guide, with workers set to the number of vCPUs; Wäg with shipped defaults. Response caching is off on both.
  • Closed-loop tests measure maximum throughput. Open-loop tests fire on a fixed schedule and count latency from the intended send time, so a gateway that queues cannot hide the queueing.
  • Every response is validated: status, body shape, and for streams the expected chunk count and terminator. Anything else counts as an error, never as a fast response.
  • Each scenario runs three times and the tables show the median. With no gateway in the path the same load reaches about 90,000 req/s, far above either gateway, so the results are gateway-bound rather than harness-bound.

Footprint

What each gateway costs before it serves anything: memory at rest, time to ready, image size, and the latency it adds to a single request against an instant upstream.

Memory after start3.8 MBLiteLLM 2,302 MB
Time to ready0.32 sLiteLLM 11.2 s
Container image123 MBLiteLLM 1,669 MB
Added latency, one client (p50)0.11 msLiteLLM 2.1 ms

What it means: Memory decides how many replicas fit on a node, and image size decides how fast a rollout is. At 3.8 MB resident and a 123 MB image, Wäg fits in a sidecar and is ready before a health check times out; 2.3 GB and 11 seconds is a pod you plan around.

Added latency is the gateway's own p50 minus the p50 measured with no gateway in the path.

Under a realistic upstream

A real model answers in hundreds of milliseconds, so thousands of requests are in flight at once. Here the upstream takes 300 ms and requests arrive at a fixed rate whatever the gateway does. At 2,000 req/s roughly 600 requests are in flight at any moment.

WägLiteLLM
200 ms500 ms1.00 s2.00 s5.00 s10 s20 s50 s300 ms upstream floor1005001,0002,000requests per secondLiteLLMWäg
requests per secondWäg p50Wäg p99Wäg ErrorsWäg CPULiteLLM p50LiteLLM p99LiteLLM ErrorsLiteLLM CPU×
100303 ms305 ms0.00%10.2%305 ms308 ms0.00%38.7%1.0×
500301 ms303 ms0.00%15.6%304 ms321 ms0.00%140.5%1.1×
1,000301 ms302 ms0.00%20.2%317 ms605 ms0.00%263.7%2.0×
2,000301 ms302 ms0.00%31.7%3.26 s28 s7.99%391.9%92×

What it means: This is the shape of real traffic, where a model answers in hundreds of milliseconds and thousands of requests are in flight. A gateway that queues turns a 300 ms answer into a multi-second one, and nobody notices until peak. Wäg stays at the floor through 2,000 req/s on under a third of its CPU budget. LiteLLM is already queueing at 1,000 and fails 8% of requests at 2,000.

The upstream alone takes 300 ms, so 300 ms is the floor. Everything above it is time spent queueing inside the gateway. The × column always reads the same way: higher means Wäg is ahead. It is Wäg ÷ LiteLLM for throughput and LiteLLM ÷ Wäg for latency.

Throughput and latency

Closed loop against an instant upstream: pure gateway cost, with no model latency to hide behind. These are the numbers a capacity plan needs. They are not the reason to choose a control plane.

4 vCPU, no database

Concurrent clientsWäg req/sWäg p99Wäg CPULiteLLM req/sLiteLLM p99LiteLLM CPU×
16,2320.27 ms63.4%4633.2 ms98.1%13×
825,7110.70 ms311.5%1,23715 ms286.1%21×
3225,0932.6 ms321.8%1,62146 ms385.1%15×
12823,63911 ms328.3%1,669111 ms396.2%14×
51222,40745 ms336.9%1,569749 ms391.6%14×

1 vCPU, no database

Concurrent clientsWäg req/sWäg p99LiteLLM req/sLiteLLM p99×
16,5890.35 ms4593.2 ms14×
816,5941.3 ms48426 ms34×
3215,2263.8 ms50685 ms30×
12813,26415 ms505438 ms26×

What it means: This sets how many replicas a traffic level needs, and therefore what the gateway costs to run. The 1 vCPU table is the one to read for Kubernetes: that is the per-core efficiency a pod actually gets.

Streaming, payloads, embeddings, failover

4 vCPU, no database. Streams are 50 tokens 5 ms apart; the soak runs 200 streams per second of roughly 1.2-second answers for three minutes.

ScenarioWägWäg p99Wäg ErrorsLiteLLMLiteLLM p99LiteLLM Errors×
Streaming, 64 clients233286 ms0.00%179674 ms0.00%1.3×
Streaming, 256 clients911288 ms0.00%3071.09 s0.00%3.0×
64 KB conversation history, 32 clients3,58329 ms0.00%568106 ms0.00%6.3×
Embeddings (1,536 dims), 32 clients13,5584.3 ms0.00%74069 ms0.00%18×
Fallback after upstream 500, 16 clients19,8161.8 ms0.00%58462 ms0.00%34×
Soak: 200 streams/s for 3 minutes2001.27 s0.00%17620 s12.09%1.1×

What it means: Streaming, long prompts, embeddings and failover are where a production workload spends its time. Read the error columns before the throughput ones: a failed request is a failed user request, not a slow one.

The 64 KB payload figure sits at the harness ceiling: with no gateway in the path the same load reaches 3,711 req/s of 64 KB bodies on one core, so the Wäg number there is a lower bound rather than a maximum.

What this does not show

Every benchmark describes one setup. These are the limits of this one.

  • Absolute numbers depend on the host. Both gateways ran on the same machine with pinned cores, so the comparison between them holds, but expect different absolute figures on your own hardware.
  • The harness that drove these runs is not published, so you cannot repeat them command for command. The method above is described in enough detail to rebuild an equivalent.
  • LiteLLM ships most weeks. These figures were measured against v1.104.2, the current stable release. When they ship a release that changes the forwarding path, we measure it again and publish whatever we find.
  • LiteLLM was configured from its production guide, not by its maintainers. A better configuration may exist, and if you find one we will re-run and republish.
  • Both gateways ran on one machine with pinned cores. Nothing here measures behaviour across a network, under Kubernetes scheduling, or with noisy neighbours.