Skip to main content
Version: 5.0.x 🚧

MTE Relay Performance

MTE Relay is a drop-in encrypted HTTP gateway. It is the same engine behind both MTE Relay Server and MTE API Relay, so the performance characteristics on this page apply to either deployment. An application sends ordinary HTTP to an outbound relay; the relay MTE-encrypts the entire request; a peer relay in front of the origin decrypts it and forwards plaintext to the origin; and the response makes the same trip in reverse. Every byte of every request and response is encrypted in transit between the two relays, with no changes to the client or the origin service.

This page has two goals: to show that MTE Relay is highly performant, and to help you choose the right deployment size for your traffic.

Performance at a glance​

~7,700req/s
Encrypted 10 KB requests per standard 4 CPU / 4 GB relay pair
~665 M/ day
Encrypted API calls per day on one relay pair
~1ms
Added latency at light load across the full encrypted round trip
0.000%
Errors across 2.7 M requests, with 100% payload integrity

A pair of standard 4 CPU / 4 GB relay instances moves about 7,700 encrypted requests per second of 10 KB JSON β€” roughly 77 MB/s (β‰ˆ0.6 Gbit/s) of fully encrypted application payload, or about 665 million requests per day β€” with a measured error rate of zero. In the certification sweep for this configuration, 2,705,692 consecutive requests completed with 0.000% errors and 100% payload integrity (every response byte-identical to what was sent).

The price of the encryption is small and predictable:

  • At light load, the full encrypt β†’ transmit β†’ decrypt β†’ origin β†’ encrypt β†’ transmit β†’ decrypt round trip adds about 1 millisecond per request (1.2 ms through the relay pair vs 0.15 ms talking to the origin directly). For interactive APIs this is imperceptible.
  • Past its ceiling the gateway saturates gracefully, never destructively. When offered more work than it can encrypt, requests queue: latency rises linearly and predictably, throughput holds flat, and nothing fails. At 8Γ— saturating demand, 99% of requests still completed in under 43 ms.

The system is compute-bound on encryption, and therefore scales with CPU (about 50% of relay CPU time is spent inside the MTE cryptographic library) β€” measured at roughly 955 requests/second per relay core, linear across the 2- and 4-CPU shapes. Memory is not a throughput factor: it tracks payload size, not demand.

How MTE Relay works​

Client-side environmentOrigin-side environmentMTE-encrypted hopClient / Appplain HTTPMTE RelayencryptsMTE RelaydecryptsOriginplain HTTPplaintextciphertextpublic / untrusted networkplaintext
Each side runs its own relay inside its own environment. Applications speak ordinary HTTP to the nearby relay; every byte is MTE-encrypted for the hop across the public network and delivered as plaintext to the origin β€” no changes to the client or the origin service. Responses make the same trip in reverse.

Because the client and origin are untouched, MTE Relay drops into an existing architecture as an encrypted hop. The only resource it consumes is CPU to encrypt and decrypt; everything else on this page follows from that.

How we measured it​

Load was generated with k6, the open-source load-testing tool from Grafana Labs. k6 reports latency percentiles (p90, p95, p99) directly in its summary output, so every latency figure on this page is taken straight from the tool rather than computed by us.

The test uses maximum-pressure connections, not realistic users. Each connection is a k6 virtual user (VU) running closed-loop with zero think time: it fires its next request the instant the previous response arrives. A single such connection is itself a heavy load source β€” one connection alone extracts about 800 requests per second from the gateway. These connections are best thought of as units of offered pressure, each equivalent to hundreds of real API clients.

Test connectionsEach connection was sendingTotal demand delivered
8806 req/s6,445 req/s
16493 req/s7,892 req/s
32241 req/s7,711 req/s
64121 req/s7,775 req/s
12860 req/s7,617 req/s

The gateway reaches its ~7,700–7,900 req/s ceiling once total demand exceeds it (about 16 maximum-pressure connections); beyond that point the same total throughput is simply shared across more connections. The sweep then pushed to 128 simultaneous saturating connections β€” roughly 8Γ— the demand needed to saturate the gateway β€” to prove overload safety, and the error count stayed at zero throughout.

Measured throughput and latency​

Workload: a POST of ~10 KB JSON, sustained 60-second holds per load level, with a co-located k6 load generator over plain HTTP (no TLS) to isolate the cost of MTE encryption.

Offered load (vs capacity)Throughput (req/s)Avg latencyp90 latencyp99 latencyWorst caseErrors
Light (~80%)6,4451.2 ms1.5 ms2.0 ms7 ms0
Saturating (100%)7,8922.0 ms2.8 ms3.9 ms23 ms0
Overload (~2Γ—)7,7114.1 ms6.2 ms9.1 ms37 ms0
Overload (~4Γ—)7,7758.1 ms13.2 ms19.5 ms41 ms0
Overload (~8Γ—)7,61716.7 ms28.8 ms42.9 ms154 ms0

Across the full certification sweep: 2.71 million requests, 0 failures, 0 corrupted payloads.

Throughput holds flat at capacity no matter how hard the gateway is pushed:

Light (~80%)
6,445 req/s
Full (100%)
7,892 req/s← capacity
Overload (2Γ—)
7,711 req/s
Overload (4Γ—)
7,775 req/s
Overload (8Γ—)
7,617 req/s
Encrypted throughput delivered (req/s). Throughput holds at capacity even at 8Γ— saturating demand.

Under overload, the only thing that grows is queueing delay β€” and it grows linearly, with no cliffs and no error storms:

p90p99
Light (~80%)
1.5 ms
2 ms
Full (100%)
2.8 ms
3.9 ms
Overload (2Γ—)
6.2 ms
9.1 ms
Overload (4Γ—)
13.2 ms
19.5 ms
Overload (8Γ—)
28.8 ms
42.9 ms
p90 and p99 latency (ms) by offered demand. Both percentiles grow linearly as demand doubles β€” queueing delay only, with no cliffs and no error storms.

What this means in real terms​

7,700 encrypted requests/second is, equivalently:

Client profileRequest rate per clientClients supported by one relay pair
Mobile/web user (6 requests per minute)0.1 req/s~77,000 concurrent users
Active interactive session (20 requests per minute)0.33 req/s~23,000 concurrent sessions
Busy machine-to-machine integration10 req/s~770 concurrent integrations
Maximum-pressure benchmark connection30–800 req/s~16 (the saturation point above)
  • For an interactive API: the relay adds about one millisecond. A user cannot perceive it.
  • For a high-volume service: one standard relay pair carries ~665 million encrypted API calls per day at 10 KB each.
  • For reliability engineering: overload manifests as added queueing delay, not as errors. At 8Γ— saturating demand the gateway did not drop, corrupt, or reject a single request.

Real-world example: across the public internet​

Every figure above was measured with the load generator and both relays inside a single cluster. That isolates the cost of MTE encryption cleanly, but it says nothing about how the relay feels on a real network. To close that gap we ran the same A/B sweep across an actual WAN: the client and outbound relay on a workstation over a residential internet connection, and the inbound relay and origin on AWS EKS in us-east-1, each behind an Elastic Load Balancer.

Both arms cross the WAN exactly once β€” the control arm goes straight to the origin in plaintext, the test arm goes through the MTE relay pair β€” so the difference between them isolates the relay, not the network. Each side ran a single 2 CPU / 2 GB relay with in-memory state.

MetricResult
Median latency added per request+2.07 ms (mean of 9 unsaturated cells)
As a fraction of the WAN round trip+3.7% (against the 56 ms baseline RTT)
Throughput cost at equal offered loadβˆ’3.14%
Failed requests2 of 1,008,891 (0.0002%)
Response integrity100.00% byte-intact, both arms
Plaintext WAN round tripMTE Relay overhead
2 KB
62 ms
5 KB
60 ms
10 KB
63 ms
Median request latency over a real internet path to AWS us-east-1, by payload size (unsaturated load levels). The relay adds about 2 ms on top of a ~59 ms round trip β€” a few percent, for full end-to-end encryption.

The structural finding is the important one: the relay's cost is a small, roughly fixed amount per request β€” not a multiplier on network distance. The relay performs the same encode/decode work regardless of how far the packet travels; what changes between environments is the denominator. An in-cluster A/B on the same cloud infrastructure the same day measured +2.65 ms; across the WAN the same work measured +2.07 ms β€” the same magnitude in two very different environments. On a LAN, a couple of milliseconds can multiply a sub-millisecond round trip several times over. Across a real internet path it disappears into a few percent of a 56 ms one.

  • The further traffic travels, the cheaper encryption looks. Because the overhead is fixed, its relative cost falls as round-trip time rises β€” exactly the situation of any API served across regions, or to mobile clients on cellular networks.
  • Saturation came from the network, not the relay. In the cells that plateaued, the residential uplink (~40 Mbit/s each way) was the ceiling β€” and it capped the plaintext control arm just as hard. Both arms degraded into longer queues, never into errors.
  • Integrity held throughout. Every response in both arms was byte-intact, with no MTE protocol errors at any load level.
Reading this comparison

The control arm is plaintext HTTP, so these figures are the relay's cost against a bare, unencrypted origin. Against a TLS-terminated baseline the gap would be smaller β€” TLS is not free either, and in this test MTE is carrying the encryption that TLS would otherwise be providing.

Resource footprint​

Two instance shapes are certified (zero errors through full overload sweeps):

Per relay instanceCPU at full loadMemory at full load (10 KB payloads)Saturation
4 CPU / 4 GB (standard)~4 cores (the limiter)1.9–2.4 GB~7,700 req/s
2 CPU / 2 GB (small)~2 cores (the limiter)~1.9 GB~3,820 req/s

Memory demand does not grow with offered load β€” it tracks payload size (response/body buffering churn), not request rate: at 10 KB payloads the working set is ~1.9 GB, at 2 KB payloads it is only ~0.2 GB. Under overload the gateway degrades in latency only; it does not balloon toward an out-of-memory failure. On the small shape, bound the runtime's memory slightly below the container limit so it stays within its allocation.

Sizing by hardware: cores β†’ throughput​

The gateway was measured at three instance sizes with the identical build and workload (10 KB payloads), giving a calibrated cores→throughput curve:

Per-relay CPUSustained ceiling (req/s)Per-core efficiency
2 (measured)~3,820~955 req/s per relay core
4 (measured)~7,700~960 req/s per relay core
8 (measured)~12,600~790 req/s per relay core

Scaling is essentially linear from 2 to 4 cores (Γ—2.02) at ~955 req/s per relay core. Only the 4β†’8 step measured sublinear (Γ—1.64); at that size the test deployment approaches the capacity of the single test node, so the roll-off is at least partly testbed contention rather than a software limit.

1 CPU
~1,900
2 CPU
3,820 βœ“
4 CPU
7,700 βœ“
6 CPU
~10,400
8 CPU
12,600 βœ“
12 CPU
~16,800
16 CPU
~20,600
Saturation at 10 KB by per-relay CPU. Solid bars are measured; dashed bars are projected from the ~955 req/s-per-core curve. Linear through 4 CPU; several small pairs beat one large instance.
Per-relay instance size100% saturation (10 KB)Confidence
1 CPU~1,900 req/sextrapolated (linear ~955/core), Β±20%
2 CPU3,820 req/smeasured
4 CPU7,700 req/smeasured
6 CPU~10,400 req/sinterpolated, Β±10%
8 CPU12,600 req/smeasured (conservative)
12 CPU~16,800 req/sextrapolated, Β±20%
16 CPU~20,600 req/sextrapolated, Β±20%+

Practical guidance: treat CPU as the only capacity lever. Per-core efficiency is flat at ~955 req/s through the 2- and 4-CPU shapes, so size by arithmetic and prefer adding more small instances (horizontal scaling, which also adds redundancy) over growing a single instance. Projections beyond the measured 2–8 core range should be confirmed by measurement before being relied on for production sizing.

On memory: it is not a throughput variable. 2 GB is certified for the 2-CPU shape (including 10 KB payloads); 4 GB is recommended for 4-CPU-and-larger shapes; 8–16 GB show no measurable throughput or latency benefit.

Payload-size sensitivity​

Three full overload sweeps at 2 KB, 5 KB, and 10 KB bodies β€” 5.0 million requests, 0.000% errors in every run β€” give a simple, accurate cost model:

PayloadSaturation (req/s)Payload movedLight-load avg latencyp90 latencyp99 latency
2 KB5,537~11 MB/s1.5 ms36.1 ms47.6 ms
5 KB4,762~23 MB/s1.7 ms43.1 ms59.7 ms
10 KB3,856~38 MB/s2.0 ms50.9 ms67.6 ms

The p90 and p99 columns are measured under heavy overload (128 max-pressure k6 connections); the average latency is at light load. The three points fit a simple linear cost model almost exactly:

Relay-pair capacity cost per request β‰ˆ 161 Β΅s fixed + 9.8 Β΅s per KB (predicts the 5 KB measurement to within 0.1 Β΅s)

Fixed per-request costPayload-dependent crypto
2 KB
181 Β΅s
5 KB
210 Β΅s
10 KB
259 Β΅s
Cost per request in Β΅s of relay-pair capacity (2-CPU shape). Even at 10 KB, most of the cost is payload-independent.

What this means in practice:

  • Per-request overhead dominates. Even at 10 KB, 62% of the cost is payload-independent. Shrinking payloads 5Γ— raises request throughput only ~1.4Γ— β€” so for small-payload APIs, plan capacity by request rate, not by bandwidth.
  • Larger payloads are relatively cheaper to encrypt: moving the same data in 10 KB requests costs ~3.4Γ— less gateway capacity than moving it in 2 KB requests.
  • To estimate saturation for any payload size and shape: take 1 Γ· (161 Β΅s + 9.8 Β΅s Γ— KB) and scale by the CPU curve above.

High availability and horizontal scaling​

For production resilience the gateway runs multiple relay instances per side behind a load balancer, with each side's instances sharing their encryption state through a dedicated coordination store (Redis, one per side). Any instance can then serve any client's traffic β€” an instance can fail or be replaced without breaking sessions β€” and capacity grows by adding instances.

The load balancer uses sticky per-pair routing β€” an L7 hash on a routing header that keeps each encryption pair pinned to one instance β€” together with a local-first state cache on every relay. Encryption state is served from an in-process cache rather than read from the shared store on each request, so the store stays near-idle and relay CPU remains the only capacity lever. The payoff is clean horizontal scaling: each relay replica you add contributes throughput proportionally, at zero errors.

At the 2-CPU shape, moving from one instance to two to three lifts throughput about 1.3Γ— per added replica β€” the same shape at 1 KB and 10 KB, with 0.000% errors at every point:

1 KB10 KB
1 instance
7,644
5,808
2 instances
10,204
7,776
3 instances
13,239
10,001
Sustainable throughput by replica count (req/s, 2 CPU per instance, sticky per-pair routing). Adding replicas adds throughput proportionally β€” ~1.3Γ— per replica β€” at 0.000% errors.

Adding CPU to each instance lifts throughput the same way. The full multi-instance capacity grid (sustainable req/s at <1% error, sticky per-pair routing, every cell at 0.000% errors):

Deployment (per side)1 KB5 KB10 KB25 KB
2 instances Γ— 1 CPU5,6525,0024,3073,150
2 instances Γ— 2 CPU10,2048,9707,7765,406
2 instances Γ— 3 CPU12,05211,3079,7787,399
3 instances Γ— 1 CPU8,1117,1716,1594,542
3 instances Γ— 2 CPU13,23911,58910,0017,450
  • Scaling, not a tax. Earlier connection-level (L4) load balancing could pin uneven traffic onto one instance and forced a shared-store read on every request β€” so adding capacity didn't always help. Sticky per-pair routing removes both effects: each replica now adds throughput and a unit of high availability.
  • The shared store is not a bottleneck. With state served from the pod-local cache, under 0.1% of requests ever read from the store (a one-time cache warm-up); it sat near-idle while relay CPU set the ceiling.
  • Failover is graceful. If an instance is lost, its clients quietly reload state from the shared store β€” no re-pair storm β€” and errors stay bounded to the brief window before traffic re-lands on a healthy instance.
  • Relay memory stays low in this mode: encryption state lives in the shared store, so each relay holds only a small working set rather than a full self-contained pair pool.

Reading these numbers. As everywhere on this page, the co-located load generator makes absolute RPS rig-relative β€” the portable result is the shape: throughput rises with both replicas and cores, at 0.000% error. These multi-instance figures are from the current sticky-routing test build.

Deployment tiers: Small, Medium, Large, Enterprise​

The measured results reduce to four standard deployment tiers. Two planning rules are baked into the recommendations:

  1. Run at ≀60% of measured saturation. The gateway is provably safe far beyond that (zero errors at 8Γ— overload), but at ≀60% utilization p99 latency stays in the single-digit-to-low-tens of milliseconds.
  2. Capacities below are quoted for ~10 KB payloads β€” the most demanding case measured. Smaller payloads raise request capacity (Γ—1.23 at 5 KB, Γ—1.43 at 2 KB); adjust before picking a tier.
SmallMediumLargeEnterprise
Relay deployment (per side)1 Γ— 2 CPU / 2 GB, self-contained state2 Γ— 2 CPU / 2 GB + shared store (2 CPU / 2 GB)2 Γ— 4 CPU / 4 GB + shared store (2 CPU / 2 GB)N independent "cells" of the Large tier
Total hardware (both sides)4 CPU / 4 GB12 CPU / 12 GB20 CPU / 20 GB20 CPU / 20 GB per cell
Measured saturation3,856 req/s7,301 req/s11,706 req/s~11,700 req/s per cell
Recommended sustained load≀ 2,300 req/s≀ 4,400 req/s≀ 7,000 req/s≀ 7,000 req/s Γ— N
Requests per day≀ 200 M≀ 380 M≀ 600 M600 M Γ— N
Encrypted data per day (10 KB)≀ ~2 TB≀ ~3.8 TB≀ ~6 TB~6 TB Γ— N
Concurrent users (1 req/min)~140,000~260,000~420,000~420,000 Γ— N
High availabilityNo (single instance per side; sessions recover after a brief re-pair)Yes β€” instance loss is transparentYesYes, plus cell-level isolation
Typical fitDepartmental APIs, internal services, pilotsConsumer app or B2B platformHigh-volume consumer platformNational-scale / multi-tenant / multi-region
Small (4 CPU)
≀ 2,300 req/s
Medium (12 CPU, HA)
≀ 4,400 req/s
Large (20 CPU, HA)
≀ 7,000 req/s
Enterprise
≀ 7,000 req/sΓ— N cells
Recommended sustained encrypted traffic per tier (req/s at 10 KB). Every tier is certified at 0.000% errors through full overload; Enterprise grows linearly by adding independent cells.

Choosing a tier from your numbers:

  1. Take your peak sustained request rate (not daily average β€” size for the busiest hour).
  2. Adjust for payload: capacity β‰ˆ tier rating Γ— (161 + 9.8Γ—10) Γ· (161 + 9.8Γ—KB).
  3. Pick the smallest tier whose recommended load covers it; step up one tier if you need high availability (Medium is the smallest highly-available tier).

Worked example: a consumer API peaking at 3,000 req/s with ~5 KB payloads. The 5 KB capacity factor is Γ—1.23, so Medium's effective rating is ~5,400 req/s β€” Medium fits with headroom, and provides HA.

Enterprise scaling notes:

  • Grow by adding cells (an independent Large-tier gateway pair with its own shared store), splitting traffic by client population, region, or DNS. Cells share nothing, so capacity grows linearly and a cell failure is contained.
  • Within a cell, the per-side coordination store has measured headroom to coordinate 3–4 relay instances instead of 2 (certify before relying on it).

Test conditions and scope​

  • Topology: client β†’ outbound relay β†’ encrypted hop β†’ inbound relay β†’ origin, with a pre-warmed pair pool and on-demand growth. Headline numbers are a single 4 CPU / 4 GB instance per side at 10 KB with in-process state; the sizing curve adds measured 2-CPU and 8-CPU points; the payload model was measured at the 2 CPU / 2 GB shape with 2/5/10 KB bodies; the multi-instance results use two and three instances per side behind an L7 hash load balancer (sticky per-pair routing) with a per-side shared store and a local-first state cache β€” each configuration a separate certified sweep.
  • Tooling: load was generated and measured with k6 (Grafana Labs); the load-balanced tier coordinates relay state through Redis; everything ran on Kubernetes. All latency percentiles (p90, p95, p99) are reported directly by k6.
  • Environment: single-node Kubernetes with the k6 load generator co-located in-cluster, so the absolute numbers are conservative. A direct-to-origin reference on the same rig reached ~56,000 req/s β€” the origin and generator were never the limiting factor.
  • Real-world WAN validation: a separate A/B sweep placed the client and outbound relay on a residential internet connection and the inbound relay and origin on AWS EKS in us-east-1 (one 2 CPU / 2 GB relay per side, in-memory state, one load balancer hop per arm). Both arms crossed the WAN exactly once, which isolates the relay's cost from the network's: 1,008,891 requests, 2 failures, 100% response integrity. Absolute throughput there is specific to that internet connection; the delta between arms is the portable result.
  • Isolation: all traffic was plain HTTP by design, so the figures isolate MTE encryption cost. TLS termination would add its usual, separate cost.
  • Where the time goes: per-stage instrumentation at saturation shows pair acquisition ~0.001 ms and MTE encode + decode under 0.5 ms combined per relay β€” the remainder of high-load latency is ordinary queueing for CPU.

Bottom line: at the standard 4 CPU / 4 GB shape, MTE Relay delivers fully-encrypted HTTP at ~7,700 requests/second per relay pair β€” enough for roughly 77,000 typical concurrent users β€” with about 1 ms of added latency at light load, perfect payload integrity, and zero-error behavior at up to 8Γ— overload. That overhead is fixed rather than proportional to network distance: across a real internet path to AWS us-east-1 it measured +2.07 ms, or 3.7% of the round trip. Capacity follows CPU on a measured curve, payload size on a measured cost model (~161 Β΅s + ~9.8 Β΅s/KB per request), and instance count on measured scale-out data. Pick Small (≀200 M req/day), Medium (≀380 M/day, HA), Large (≀600 M/day, HA), or Enterprise (linear growth by cells) from your peak traffic and payload size, then confirm with a one-hour certification sweep.