One Relay Ballooned to 94 GiB — Beyond Tor's Accounted-Memory Limit

By 1AEO Team • Updated July 29, 2026 • Per-relay process memory from 1AEO's fleet telemetry, checked against tor 0.4.9.9 source • Telemetry through July 28, 2026; circuit-creation rejection counters remained elevated

Correction, October 20, 2026: This revision distinguishes resident memory from Tor’s accounted pools, and byte removal from handler invocations. The RSS and byte-counter figures were re-queried from retained historical metrics. The observation window remains June–July 2026.

Tor ships a last-line memory defense. MaxMemInQueues limits selected accounted memory pools, not total process RSS. In the checked Tor source, the automatic limit reaches 8 GiB on sufficiently large 64-bit hosts; smaller hosts use a different calculation. During June, we observed relay RSS far above that limit. In late July, Tor recorded substantial removal of accounted memory.

What the Defense Counts — and What It Doesn't #

The cap covers cell queues, half-streams, connection buffers, compression state, the onion-service descriptor cache, GeoIP and DNS caches, and conflux. It does not account all per-circuit structures, relay crypto state or allocator-retained memory. RSS can therefore grow without a matching increase in the pools used for this threshold.

1.3 GiB to 94 GiB of Resident Memory #

The re-queried trace rose from about 1.3 GiB to 93.8 GiB RSS. Its recorded PID and process-start timestamp remained unchanged during the decline. In ten-minute samples, RSS fell by 85.6 GiB within 30 minutes and 89.9 GiB within an hour. The peak was about 11.7 times the 8 GiB default accounted-pool ceiling; this is a comparison with a different memory quantity. Zero recorded removed bytes would not by itself prove that the handler was never invoked.

One relay RSS rose to93.8GiB in ten-minute historical samples, then declined with unchanged recorded PID and process-start time. The8GiB reference is the default accounted-pool ceiling, not an RSS cap.

Kernel OOM Kills and Tor’s Memory Accounting #

For June 24–July 13, the native Tor counters record about 23.15 GB of accounted-memory removal. By July 13, retained journal-derived counters recorded 31 Tor-process OOM kills. The journal’s anonymous-RSS-at-kill total and Tor’s removed-byte total are different accounting measures; their roughly 67x numerical ratio does not measure physical RAM reclaimed by competing defenses. A kernel OOM kill terminates the relay process and its circuits.

Late-July Memory-Handler Activity #

On July 23, the outbound directory-traffic surge coincided with Tor memory-handler activity. Re-querying the native byte counters gives about 221 GB removed July 23, 117 GB July 27 and 475 GB across July 23–28. The subsys="cell" counter includes several managed allocations; it does not isolate outbound queues or establish deliberate slow readers. The directory surge had subsided before the July 27 re-trigger, whose cause remains unknown. The selected host kernel OOM-kill counters did not increase in the July 18–28 follow-up.

Daily accounted-memory removal from native Tor byte counters, including221GB onJuly23 and117GB onJuly27;475GB acrossJuly23–28. These are managed allocation totals, not physical RSS released or handler invocation counts.

What It Costs When It Works #

Crossing the accounted-memory threshold triggers overload-general reporting in a relay’s server descriptor. The original July 29 snapshots recorded up to 92 reporters in an Onionoo snapshot, 94 in a daily descriptor set and the historical descriptor reanalysis confirms 135 distinct 1AEO reporters across July 23–28. These are different observation windows. Reports can persist for 72 hours after the latest trigger; they do not establish a current failure. Remaining in the consensus and avoiding kernel kills also do not prove uninterrupted user traffic. The planned July 28 restart cleared in-memory overload state.

Ballooning also recurred within the observed window. A single relay re-ratcheted to about 84 GiB on July 27 and returned toward its earlier RSS range within four hours. RSS changes alone do not identify the mechanism that released memory.

Compression State Also Counts #

The accounting cuts the other way too. A relay with DirCache enabled can run background consensus-diff compression, and Tor counts that compression state. Concurrent compression work can contribute to crossing a low MaxMemInQueues limit even without attack traffic, while closing circuits does not directly free compression state. Size the limit for the measured workload.

What We're Asking For #

The defense isn't broken. It sees one memory shape and not the other, and it cannot tell you which is happening. Two changes:

Both belong to the list in our defenses post.

How We Measured #

Per-relay RSS, PID and process-start gauges come from retained host metrics; the illustrated trace uses ten-minute samples. Tor removal totals use per-series increase(tor_relay_load_oom_bytes_total[1d]), then aggregation, on UTC-day boundaries. Journal-derived OOM counters identify killed processes with comm="tor"; the 31 total is visible by July 13. Host OOM counters cover all processes. Accounting semantics were checked against tagged Tor source. A July 28 restart can reset Tor counters and overload state; missing observations are not treated as zero.

Related reading: What It Actually Costs — Part 3's memory section, where the ballooning first appeared — and Defending Against Circuit-Building DoS — Part 5, the full list of metric and protocol asks this post's fix belongs to.

The figures are historical reanalysis, not new October telemetry. Host and relay identities are omitted from the plotted dataset. Plotted data and definitions.

Join the Mission