In August 2026, Cloudflare published the engineering details behind their DNS cache optimization Big Pineapple, the system behind 1.1.1.1 and Gateway DNS, was caching over 250 billion entries at any given moment, each costing 953 bytes. After five targeted changes, that dropped to 420 bytes. Roughly 100TB freed, no new hardware.
Here’s exactly what they changed and why it worked.
What was wrong with the original design?
The problem wasn’t a bug. It was accumulated overhead from treating cached DNS entries like mutable objects allocating for growth and flexibility that the code never actually used. Once an entry is cached, it never changes. The data structures didn’t reflect that.
Five separate choices each contributed waste. Together, they almost doubled the memory footprint of what an entry actually needed to store.
The 5 fixes
Replacing Vec<T> with Box<[T]>
Rust’s Vec stores three fields: a pointer, a length, and a capacity. The capacity exists so the vector can grow without reallocating. Cached DNS entries never grow. Switching to Box<[T]> removes the capacity field and eliminates the over-allocated heap space.
Savings: 64 bytes per entry.
Merging three record lists into one
A DNS response has three sections: answer, authority, and additional. Each was stored as a separate list with its own 16-byte pointer/length pair. The fix stores one combined list, with two u16 offsets marking where each section starts.
Savings: 28 bytes per entry.
Conditional owner storage
Most cached records share the same owner — the queried domain. Storing it unconditionally means allocating a heap string for something already available in the cache key. The fix: store the owner only when it differs. At lookup time, if owner is None, reconstruct it from the key.
The post doesn’t isolate a byte count for this one, but the allocation reduction is real.
Boxing large enum variants
DNS record types range from 4 bytes (A record) to 144 bytes (NAPTR). In a Rust enum, every variant takes as much space as the largest one, so every A record was silently reserving 140 bytes of padding. A records make up 80% of traffic. Boxing the large variants moves them to the heap and shrinks the inline enum to something proportional.
Wire format instead of parsed structs
DNS records were previously stored as typed Rust structs after parsing. Now they’re stored as raw bytes: a single Box<[u8]> with a 2-byte length prefix per record. Less overhead per variant, better cache locality, fewer allocations overall.
Before and after
| Metric | Before | After | Change |
|---|---|---|---|
| Per-entry footprint | 953 bytes | 420 bytes | −56% |
| Per-entry allocations | 1.1 KB | 461 bytes | −58% |
| Insert throughput | 625,000/s | 893,000/s | +43% |
| Lookup latency | 828 ns | 670 ns | −19% |
| Fleet RAM freed | — | ~100 TB | 130 servers |
The counterintuitive result: this optimization didn’t trade speed for memory. Smaller structs mean better cache locality, which means faster access. Insert throughput went up 43%. Lookup latency dropped 19%.
Why this applies beyond DNS caching
DNS caching is a specific instance of a common backend pattern: a large, append-only, read-heavy in-memory cache where objects never mutate after write. Session stores, metadata indexes, configuration caches the same structural decisions apply.
The wire format change is worth particular attention. Storing raw bytes and parsing at lookup time trades a small amount of per-request work for a permanent reduction in memory footprint and allocation pressure. For hot paths with millions of objects, that tradeoff tends to win.
What Cloudflare does with 100TB of freed space?
They’re not scaling down the fleet. The freed capacity goes into expanding cache size, which improves hit rates and reduces upstream query volume to authoritative DNS servers. The memory savings convert directly into performance headroom.
Related reading:
How Cloudflare thinks about website performance
How to improve Core Web Vitals without rewriting your app








