mrkeyoor.com_
Tue 01 Sept 17:45 UTC
Tech6 min read

Cloudflare Cut 1.1.1.1 Cache Memory by 100 TB With Five Rust Fixes

Five changes cut each DNS cache entry from 953 to 420 bytes. At Cloudflare's scale, that released about 100 TB without slowing lookups.

A 504-point Hacker News surge put an unusually specific infrastructure number in front of developers: about 100 terabytes of memory recovered from the cache behind Cloudflare's 1.1.1.1 DNS resolver. The saving did not come from a new server generation or a smaller cache. Cloudflare changed how Rust laid out each entry in memory, cutting its benchmarked footprint from 953 bytes to 420 bytes. That matters because the same platform holds more than 250 billion entries at once, where one wasted byte becomes more than 250 GB across the fleet, according to Cloudflare's engineering account.

The result is a useful counterweight to infrastructure work built around larger machines. Cloudflare reports that five changes reduced per-entry memory by 56%, lifted insertion throughput from 625,000 to 893,000 entries per second, and lowered lookup latency from 828 to 670 nanoseconds in its benchmarks. Production memory fell by less than the per-entry test because the running process contains much more than the cache. Even so, the company measured aggregate working-set memory about 100 TB lower after the rollout settled.

Why a DNS record can carry so much baggage

Cloudflare's Big Pineapple platform powers 1.1.1.1 along with Gateway DNS, DNS Firewall, AS112, and other services. A cache entry has a query identity on one side and a response on the other. The response keeps the answer, authority and additional sections, plus details such as its creation time, hit count and time to live. Locations using EDNS Client Subnet may keep several answers to the same query because the authoritative response can vary with the client's network. Cloudflare says those locations have both more entries and larger entries.

The first problem was spare capacity that had no job after insertion. Rust's Vec<T> keeps a pointer, a length and a capacity, while String has the same broad arrangement for bytes. Capacity is useful when a collection may grow. A cached DNS response is immutable once stored, so that third field and any reserved heap space remain unused. The Rust standard library documents Vec<T> as a contiguous growable array with those three components.

Cloudflare replaced eight Vec and String fields in each entry with fixed-size Box<[T]> and Box<str> values. Each swap removed an eight-byte capacity field, saving 64 bytes before counting unused reserved space. Multiplied across the cache, this step alone accounted for more than 15 TB. The choice is narrow and practical: use a growable container while building data, then store the finished value in a representation that does not pay to grow.

One record list instead of three

DNS replies divide records into answer, authority and additional sections. Big Pineapple originally represented those sections as separate lists. Cloudflare combined them into one list and recorded where each section begins. Because the number of records in a section fits in a u16, each boundary needs two bytes. Two removed boxed lists had each consumed an eight-byte pointer and an eight-byte length, so replacing them with two offsets saved 28 bytes per entry.

Small field changes can alter more than their declared sizes. Rust inserts padding so fields meet alignment rules, then rounds a structure's total size to its alignment. Cloudflare also packed several Boolean fields into one bit field. The adjacent padding shrank with them, which meant the structure lost more bytes than the Booleans occupied by themselves. This is why estimates based only on adding field sizes can miss the actual heap cost.

The next saving came from record owners. Most records in a reply belong to the name that was queried. Storing that domain again on every record duplicates information already present in the cache lookup. Big Pineapple now represents the owner as optional. When it is absent, response construction reads the queried domain from the cache entry's identity. When a record has a different owner, as an address reached through a CNAME can, the cache still stores the full name.

That design borrows the same instinct used by DNS messages themselves, although the implementation differs. Section 4.1.4 of RFC 1035 defines a compression scheme in which repeated domain names can be replaced by two-byte pointers within a message. Cloudflare says following such pointers during cache lookups would make the hot path more expensive, so it infers only the common owner and keeps exceptional names explicit.

Rust enums made tiny records as large as rare ones

The largest single source of waste was subtler. Big Pineapple had represented DNS record data as a Rust enum with variants for types such as A, AAAA, TXT and NAPTR. A Rust enum must have room for its largest variant. Here, NAPTR needed 136 bytes, and the complete enum reached 144 bytes once its tag and padding were included.

An IPv4 address in an A record needs four bytes; an IPv6 address in an AAAA record needs 16. Those two types make up more than 80% of Cloudflare's traffic, yet every value occupied enough inline space for the much larger NAPTR case. The difference exceeded 120 bytes for most records. An entry can hold several records, so the padding multiplied inside the entry as well as across the fleet.

Cloudflare first moved larger variants into boxes. That reduced the enum itself to 24 bytes and saved 120 bytes for each common A or AAAA record. Rare large variants paid for a pointer and their own allocation. The trade was sensible for the observed traffic mix, but it created allocator overhead and scattered record data around the heap. Cloudflare uses jemalloc, whose size classes can also round a requested allocation upward. The post gives a 40-byte MX allocation as an example that occupies a 48-byte bin.

Those extra allocations also hurt locality. Inline enum values had occupied one continuous region; boxing sent their contents elsewhere in the heap. A lookup could then require another pointer traversal and CPU cache-line fetch. Cloudflare's intermediate design fixed the enum's worst padding, but it exposed why counting stored bytes is only part of memory performance. Placement and allocation frequency affect the lookup path too.

Wire bytes removed both allocation and serialization work

The fifth change stored record data in a single Box<[u8]>. Each record is encoded in DNS wire format behind a two-byte length prefix, while the rest of the cache entry remains structured. This removed the large enum and the separate allocation for each boxed variant. It also put the record bytes next to one another in memory.

Cloudflare stopped short of caching the entire finished DNS message. Clients may request different contents, including DNSSEC records when the DO flag is set, and domain-name compression can depend on the message being built. Keeping structured metadata preserves those choices. Within the record buffer, though, the system can copy A, AAAA, TXT and DNSSEC data directly into an outgoing response. Records containing domain names, including CNAME, NS, MX and SOA, still need parsing so the response can apply name compression.

A reusable scratch buffer handles insertion. The platform serializes a response's records into that buffer, allocates one exact boxed byte slice, then copies the finished data into it. Since earlier writes have usually expanded the scratch space enough, it rarely needs another allocation. Cloudflare attributes a 13% insertion-throughput increase to this change alone and says the packed representation cut lookup latency by 5% in its benchmark.

The final measurements separate the controlled test from the live fleet. Per-entry allocations fell from 1.1 KB to 461 bytes, a 58% reduction, while the net footprint fell 56%. In production, p99 resident memory declined from 9.3 GB to 5.3 GB and p90 fell from 6.5 GB to 3.8 GB. Cloudflare rolled the changes out from May 18 to July 6, 2026, and used the stable plateaus after cache refilling to estimate the fleet-wide saving.

Cloudflare plans to spend the released memory on more cache capacity, which should raise hit rates and reduce queries sent upstream. The next evidence to watch is whether that larger cache produces a measured hit-rate gain under real traffic, and whether further layout changes preserve the reported 670-nanosecond lookup latency. The 100 TB figure is striking, but the reusable lesson is smaller: at 250 billion entries, a capacity word, a pointer or an enum's padding becomes physical infrastructure.

We reviewed this

  1. gateway — our honest review
  2. query — our honest review

Sources

  1. How we saved 100 terabytes of memory by optimizing 1.1.1.1's DNS cache
  2. Rust std::vec::Vec documentation
  3. RFC 1035: Domain names, implementation and specification