In-app reader
Blog 1.1.1.1Deep DiveDNS** +4 Show 4 more tags
** 7 Tags Show 7 tags
_**
Selected Tags
1.1.1.1Deep DiveDNSEngineeringOptimizationPerformanceRust
All tags
Matching tags
No tags found
1.1.1.1
2FA
Abuse
Access
Access Control Lists (ACLs)
Accessibility
Account Takeover
Acquisitions
Addressing
Advanced Certificate Manager
Advanced DDoS
Advertising
Aegis
AEO
Africa
Afroflare
Agent Cloud
Agent Development Lifecycle
Agent Readiness
Agents
Agents Week
Agents Week 2026
AI
AI Bots
AI Gateway
AI Search
AI-SPM
AI WAF
AI Week
Alertmanager
Always Online
AMD
AMP
Analytics
Anonymous
Anti Malware
Anycast
API
API Gateway
API Security
API Shield
APJC
Apple
Application Security
Application Services
Area 1 Security
Argo Smart Routing
ASCII
Asia
Athenian Project
Atlassian
Attacks
Audit Logs
Austin
Australia
Authentication
Authy
Automatic HTTPS
Automatic Platform Optimization
Automation
AutoMinify
Auto Rag
Awards
AWS
Baidu
Bandwidth Alliance
Bandwidth Costs
Best Practices
Beta
Better Internet
BGP
Billing
Birthday Week
Blackbird
Black Friday
Bot Fight Mode
Bot Management
Botnet
Bots
BPF
Brand
Brand Protection
Brazil
Browser Insights
Browser Rendering
Browser Run
Bug Bounty
Bugs
BYOIP
Cache
Cache Purge
Cache Reserve
Cache Rules
California
Canada
Cap'n Proto
CAPTCHA
Careers
CASB
Categories
CDN
CDNJS
Certificate Authority
Certificate Pinning
Certificate Transparency
Certification
CFSSL
Challenge Page
ChatGPT
China
China Network
Christmas
Chrome
CIO Week
CISA
Claire
CLI
ClickHouse
Clientless
Clientless Web Isolation
Cloud Connector
Cloud Email Security
Cloudflare Access
Cloudflare Apps
Cloudflare Area 1
Cloudflare Calls
Cloudflare Email Service
Cloudflare for Campaigns
Cloudflare for SaaS
Cloudflare for Startups
Cloudflare Gateway
Cloudflare History
Cloudflare Images
Cloudflare Media Platform
Cloudflare Meetups
Cloudflare Network
Cloudflare One
Cloudflare One Client
Cloudflare One User Risk Score
Cloudflare One Week
Cloudflare OS
Cloudflare Pages
Cloudflare Polish
Cloudflare Queues
Cloudflare Realtime
Cloudflare Stream
Cloudflare Tunnel
Cloudflare TV
Cloudflare Workers
Cloudflare Workers KV
Cloudflare Workers KV (ES)
Cloudflare Workers (PT)
Cloudflare Zero Trust
Cloudforce One
Cloudy
Code Orange
Coinbase
Colombia
Community
Compliance
Compression
Config Rules
Configuration Management
Congestion Control
Connectivity
Connectivity Cloud
Consumer Services
Containers
Content Independence Day
Content Scanning
Context
Core
COVID-19
Crawler Hints
CrowdStrike
Cryptography
Crypto Week
CSAM Reporting
Customers
Customer Success
Customer Zero
CVE
CVE-2023-50387
Cyber Readiness
Cybersecurity
D1
Dashboard
Data
Database
Data Catalog
Data Center
Data Localization
Data Localization Suite
Data Loss
Data Loss Prevention
Data Platform
Data Privacy Day
Data Protection
Data Sovereignty
Data Transfer Bucket
DDoS
DDoS Alerts
DDoS Reports
Debugging
Deep Dive
Descaler
Design
Deskope
Developer Documentation
Developer Platform
Developers
Developer Spotlight
Developers Storage
Developer Week
Device Security
DevOps
DEX
Digital Experience Monitoring
Digital Forensics
Disrupt
Distributed
Distributed Systems
Distributed Web
Diversity
DLP
DMARC
DNS
DNS Filtering
DNS Flood
DNSSEC
DNS Security
Dogfooding
DoH
Domain Rankings
Domain Scoped Roles
dosd
Drupal
Due Process
Durable Execution
Durable Objects
Early Hints
Earth Day
eBPF
EC2
eCommerce
Edge
Edge Computing
Edge Database
Edge Rules
Education
Egress
Elastic
Elections
Election Security
Elliptic Curves
Email Routing
Email Security
Email Workers
EmDash
Emissions
Employee Resource Groups
Encrypted SNI
Encryption
Engineering
Enterprise
Entropy
EPYC
Ethereum
Europe
European Union
Events
Exploit
Fancy Bear
Fast Fonts
FCC
Feature Flags
FedRAMP
FedRAMP High
FedRAMP Moderate
Firefox
Firewall
Firmware
Florida
Football
Formal Methods
Forrester
Fortran
Foundation DNS
Founders' Letter
France
Fraud
Free
Freedom of Speech
Front End
Full Stack
Full Stack Week
Fun
Gartner
Gatebot
GA Week
GDPR
General Availability
Generative AI
Gen X
Geo Key Manager
Germany
GitHub
Go
Google Analytics
Google Cloud
Google Workspace
Government Innovation
Grace Hopper
Grafana
GraphQL
Green
Grinch
Growth
gRPC
Guest Post
Hackathon
Halloween
Hardware
HashiCorp
Hertzbleed
Heuristics
History
Holidays
Holocaust
Hong Kong
Hosting Con
Hostnames
HTTP2
HTTP3
HTTPS
Human Rights
Hurricane
Hybrid Cloud
Hyperdrive
IBM
ICANN
iCloud Private Relay
Identity
IETF
IETF Standards
IL4
Image Optimization
Image Recognition
Image Resizing
Image Storage
Impact
Impact Week
I'm Under Attack Mode
Incident Report
Incident Response
India
Indicators of Compromise
Indonesian
Inference
Infrastructure
Infrastructure as Code
Insights
Intel
Interconnection
Internal DNS
Internet Performance
Internet Quality
Internet Regulation
Internet Shutdown
Internet Summit
Internet Traffic
Internet Trends
Internship Experience
Intrusion Detection
Investors
IoCs
iOS
IoT
IPFS
IPsec
IPv4
IPv6
IRAP
Israel
Italy
IWD
JAMstack
Japan
JavaScript
Jengo
Jengo Policy
Joomla
Judeoflare
Kafka
Kernel
Keyless SSL
KeyTrap
Key Value
Killnet
Korea
Kubernetes
LangChain
Latency
Latin America
Latinflare
LavaRand
Lazarus group
Leaked Credential Checks
Legal
Legal Patents Sable
LGBTQIA+
Life at Cloudflare
Linux
Lisbon
Live Streaming
Llama
LLM
Load Balancing
Localization
Log4J
Log4Shell
Logging
Log Push
Logs
LUA
Machine Learning
Magecart
Magic Firewall
Magic Network Monitoring
Magic Transit
Magic WAN
Magic WAN Connector
Malicious JavaScript
Malware
Managed Components
Managed Rules
March of Cloudflare
MASQUE
MCP
Meerkat
MeetUp
Meris
Message Protocol
Mexico
Micro-frontends
Microsoft
Microsoft 365
Microsoft Azure
Middle East
Migration Hub
Milestones
Miniflare
Mirage
Mirai
Mitel
Mitigation
Mixed Content Errors
MLops
Mobile
Mobile SDK
Model Context Protocol
Moldova
Monitoring
Multi-Cloud
Multi-User
MySQL
NaaS
Net Neutrality
Network
Networking
Network Interconnect
Network Performance Update
Network Protection
Network Services
New Year
NGINX
Ninjas
NIST
Node.js
North America
Notebooks
Notifications
NSEC3
OAuth
Observability
Oceania
OCSP
Offices
Okta
Olympics
Onboarding
OpenAI
Open API
OpenBMC
OpenDNS
Open Source
OpenSSL
OpenTelemetry
Optimization
Origin Rules
Outage
Oxy
Pacific Northwest
Page Rules
Page Shield
Parallels
Partners
Partnership
Password-reuse
Passwords
Passwords (PT)
Patents
PAYGO
Payments
Pay Per Crawl
PCI Certified
Peering
Performance
Performance Optimization
Phishing
php
Phython
Pingora
Pipelines
PlanetScale
Plans
Platform Engineering
Platform Week
Plesk
Policy & Legal
Politics
Portugal
Postgres
Post Mortem
Post-Quantum
Precursor
Prepared Statements
Prisma
Privacy
Privacy Pass
Privacy Week
Private IP
Private Network
Product Design
Product News
Programming
Programming (PT)
Project Fair Shot
Project Galileo
Project Honey Pot
Project Pangea
Project Safekeeping
Project Turpentine
Prometheus
Protocols
Proudflare
Proxying
Public Sector
Python
Python Workers
Quantization
Queues
QUIC
QUICHE
Quicksilver
R2
R2 Super Slurper
Radar
Radar Alerts
Radar API
Radar Maps
Railgun
Randomness
Ransom Attacks
Rapid Reset
Raspberry Pi
Rate Limiting
RC4
RDDoS
React
Reading List
Real-time
Recruiting
Regional Services
Registrar
Reliability
Remote Browser Isolation
Remote Desktop Protocol
Remote Work
Replication
Research
Resolver
Restreaming
Retreat
Reverse Engineering
REvil
Risk Management
Road to Zero Trust
Rocket Loader
RocksDB
Routing
Routing Security
RPC
RPKI
RRDNS
RSA
Russia
Rust
Rust Workers
SaaS
SAAS Security
Sable
Salt
Sampling
Sandbox
SASE
Save The Web
SDK
Search Engine
Secrets Store
Secure Web Gateway
Security
Security Analytics
Security Center
Security Posture
Security Posture Management
Security Service Edge
security.txt
Security Week
SEO
Serverless
Serverless AI
Serverless (PT)
Serverless Week
Server Push
Servers
SIEM
Signed Exchanges (SXG)
SIM
Singapore
Single Sign On (SSO)
Smart Placement
Smart Shield
Snippets
SOC as a Service
South Africa
South America
Spain
spdy
Spectrum
Speed
Speed Brain
Speed & Reliability
Speed Week
Spoofing
Sports
SQL
SRE
SSE
SSH
SSL
Standards
Startup Enterprise Plan
Statistics
StopTheHacker
Storage
Sumo Logic
Super Bowl
Supercloud
Supply Chain Attacks
Support
Sustainability
SWAG
SWG
Swift
Switzerland
SXSW
SYN
SYN Flood
Syria
TCP
Team
Teams Dashboard
TechCrunch
Technical Writing
Tech Talks
Terraform
Testimonials
Testing
Texas
Thanksgiving
The Serverlist Newsletter
Threat Data
Threat Feeds
Threat Intelligence
Threat Operations
Threat Report
Threats
Tiered Cache
TikTok
TLS
TLS 1.3
Tools
Tor
Tracing
Traffic
Transform Rules
Transparency
Trends
Trust & Safety
TTFB
TTL
TURN
TURN Server
Turnstile
TypeScript
UDP
Ukraine
United Kingdom
Universal SSL
URL Scanner
USA
User Research
VDI
Vectorize
Vetflare
Video
Visibility
Vite
VoIP
VPC
VPN
Vulnerabilities
WAF
WAF Attack Score
WAF Rules
Waiting Room
WARP
WARP Connector
WASM
Web3
Web Application Firewall
WebAssembly
Web Asset Discovery
Webinars
WebMCP
WebP
WebRTC
WebSockets
Wildebeest
Womenflare
WordPress
Workers AI
Workers Launchpad
Workers Logs
Workers Observability
Workers Sites
Workers Unbound
Workers VPC
Workflows
World IPv6 Day
Wrangler
x402
Year in Review
Z3
Zaraz
Zero Day Threats
Zero Trust
Zero Trust Week
Zone Versioning
EngineeringOptimizationPerformanceRust
1.1.1.1Deep DiveDNSEngineeringOptimizationPerformanceRust
August 27, 2026
**Sebastiaan Neuteboom
12 minute read
** COPY URL
Big Pineapple, the platform behind 1.1.1.1, Gateway DNS, DNS Firewall, AS112, and several other Cloudflare DNS services, stores over 250 billion DNS cache entries at any given time. At that scale, wasting a single byte per entry costs more than 250 gigabytes of memory across our fleet.
Five successive changes to how cache entries are stored in memory cut the per-entry footprint by over 50%. Across our fleet, these changes freed up roughly 100 terabytes of memory, equivalent to the amount of RAM in 130 of our Gen 13 servers. The cache also got faster. Insert throughput rose 43% and lookup latency dropped 19%, as fewer allocations and better memory locality meant we did not trade speed for space.
On cold start, Big Pineapple starts out with an empty cache. As DNS queries arrive, the cache fills until it hits its maximum entry count, at which point we evict older or less popular items to make room.
The exact cache size varies by data center. When EDNS Client Subnet (ECS) is in use, authoritative servers return different answers depending on the client's network, so we cache multiple versions of the same query. This increases both the number of entries and the memory each one consumes, making the optimizations in this post especially impactful for ECS-heavy locations.
Each item in the cache is a key-value pair. The key identifies what was queried:
pub struct CacheKey {
qname: Name,
qtype: Rtype,
authenticated: bool,
tag: Vecu8>,
}
**
The value stores the DNS response itself: the answer, authority, and additional record sections, along with metadata like the creation time, a hit counter, and the Time-to-Live (TTL).
pub struct CacheEntry {
timestamp: UnixTimeStamp,
pub inception: Instant,
pub ttl: Ttl,
pub hits: u32,
pub answers: VecRecord>,
pub authority: VecRecord>,
pub additional: VecRecord>,
pub errors: VecExtendedError>,
...
}
**
Both structs have room for improvement. Several fields use types that carry overhead we don't need once the entry is stored.
To measure the impact of each change, we benchmark by filling the cache with randomly generated entries that roughly match the traffic distribution we see in production: 56% A records, 25% AAAA, and 19% TXT. Each entry contains between one and four records.
TXT records serve as a stand-in for all non-A/AAAA record types in the benchmark. Their size is randomized between 64 and 224 bytes, close to the average response size we see for variable-length record types.
We track memory usage using a custom allocator that wraps Rust’s Systemallocator and records the number and size of allocations per cache entry. Alongside memory, we measure insert throughput and lookup latency across the full cache flow to make sure memory savings don’t come at the cost of performance.
These inputs approximate production rather than reproduce it exactly. Process memory also depends on traffic mix, cache occupancy, allocator state, and memory used outside the cache. We therefore measured resident memory across production instances during the rollout.
Vec stores three fields: a pointer to heap-allocated data, the current length, and the total capacity. When you push an item, Vec checks whether the length exceeds the capacity and reallocates if needed. If there’s room, it just appends the item and increments the length.
Once we store a DNS response in the cache, however, we never modify it again. The capacity field serves no purpose, but still costs 8 bytes per Vec. The over-allocated heap space is wasted as well, as a Vec with capacity for eight items but only five stored leaves three slots unused on the heap.
Using Box solves both problems. It can’t grow after creation, so it doesn’t need a capacity field or reserve space for future elements. The same applies to String, which also carries a capacity field. Box drops it.
Each cache entry stores 8 Vec and String fields. Replacing them with Box and Box saves 8 bytes per field, 64 bytes per entry. It also eliminates the excess heap memory that Vec reserves for future growth. The combined savings add up to over 15 terabytes with over 250 billion cache entries.
Rather than storing the answer, authority, and additional sections in separate lists, we can store a single list with offsets to the start of each section. Since DNS record counts per section fit in a u16, we can use a u16 (2 bytes) for each offset, compared to the 8-byte pointer and 8-byte length that each separate Box requires.
This removes two lists, each with an 8-byte pointer and 8-byte length, and replaces them with two 2-byte offsets, saving 28 bytes per entry.
These savings do not always map directly to the number of bytes removed from individual fields. Rust inserts padding to satisfy alignment requirements and rounds a struct’s size up to a multiple of its alignment. Removing a small field can therefore eliminate additional padding. For example, we also packed several boolean fields into a single bitflag. This reduced the surrounding padding, causing the struct to shrink by more than the size of the individual booleans.
Each DNS record has an owner, the domain the record belongs to. In many cases, this owner is identical to the domain being queried. For example, a query for example.com A returns two records with the same owner:
$ dig example.com A
;; ANSWER SECTION:
example.com. 300 IN A 198.51.100.1
example.com. 300 IN A 198.51.100.2
**
But when a CNAME is involved, for example, the record owner can differ from the queried domain:
$ dig example.com A
;; ANSWER SECTION:
example.com. 300 IN CNAME cdn.example.com.
cdn.example.com. 300 IN A 198.51.100.1
cdn.example.com. 300 IN A 198.51.100.2
**
The DNS wire format handles repeated owners using name compression, as defined in RFC 1035. Rather than encoding the same domain twice, subsequent occurrences store a 2-byte pointer to the first occurrence. A domain like www.example.com can encode just www followed by a pointer to where example.com already appeared in the message.
This works well on the wire, but in our cache we store the full owner name alongside each record. Following compression pointers during cache lookups is expensive on the hot path, so we trade memory for speed.
Most records, however, have an owner identical to the queried domain. For those, we can drop the owner entirely and infer it at read time. When the owner differs, such as the A records behind a CNAME, we store the full name.
pub struct Record {
owner: OptionBoxName>>,
class: Class,
ttl: Ttl,
rtype: Rtype,
data: RecordData,
}
**
When owner is None, response construction restores the queried domain from the cache key, avoiding a heap allocation. This means the record is no longer self-contained, but the cache key is already available during every lookup. When the owner differs, Some stores a pointer to the full name on the heap.
In practice, most cached records have an owner identical to the queried domain, so the majority require no heap allocation for the owner field.
Rust enums are sum types: each variant can carry different data, but the enum is always the size of its largest variant.
pub enum OptionT> {
Some(T),
None,
}
**
Option is either Some and holds a value, or None and holds nothing. Both variants take the same amount of memory. The enum stores a tag indicating the active variant, followed by space large enough for the largest variant’s data. When the variant is None, that space is unused.
For record data, it seems natural to store each DNS record type as an enum variant:
pub enum RecordData {
A(Ipv4Addr),
Aaaa(Ipv6Addr),
Txt(Txt),
Naptr(Naptr),
Svcb(Svcb),
// ...
}
**
But the enum is always as large as its largest variant. In our case, that’s NAPTR at 136 bytes. It stores three variable-length text fields, a domain name, and two integers. As a result, the full enum, including the variant tag and padding, becomes 144 bytes.
An A record only needs 4 bytes, and an AAAA record needs 16 bytes. A and AAAA make up over 80% of our traffic, so most records waste over 120 bytes on padding. Since a single cache entry can store many records this quickly adds up.
To solve this problem, we can box the larger variants of the enum, moving them to a separate heap allocation. The enum then stores an 8-byte pointer to the heap, where the data takes up only the size it actually requires.
pub enum RecordData {
// Small and common variants are stored inline
A(Ipv4Addr),
Aaaa(Ipv6Addr),
// Large variants are stored on the heap
Txt(BoxTxt>),
Naptr(BoxNaptr>),
Svcb(BoxSvcb>),
// ...
}
**
For A and AAAA records, this saves 120 bytes per record. Smaller variant types like TXT and CNAME also benefit. They still occupy the 24-byte enum, but their heap allocation is sized to their actual data rather than padded to 144 bytes. NAPTR, the largest variant, actually pays slightly more. It now adds the cost of a heap pointer and allocation overhead. But NAPTR records are rare in practice, so the tradeoff is worth it.
But boxing the larger record variants introduces costs of its own.
Boxing has two costs. The first is allocator overhead. Each boxed variant becomes a separate heap allocation, and allocators round up to the nearest size class. Big Pineapple uses jemalloc, an allocator designed for multithreaded, allocation-heavy workloads. jemalloc groups allocations of similar sizes into fixed-size bins. A TXT record requests 32 bytes and fits exactly into a 32-byte bin, wasting nothing, but an MX record requests 40 bytes and rounds up to 48, wasting 8 bytes.
The second cost is poor memory locality. Without boxing, the record enum values for a cache entry sit in a single contiguous allocation. With boxing, data for each boxed variant lives in a separate heap region. Reading it requires following a pointer, and when that pointer lands far from the rest of the entry, the CPU has to fetch a new cache line. With millions of cache entries, boxed data ends up scattered across the heap rather than packed together.
Neither cost is catastrophic on its own, but eliminating both, as the next section shows, yields a measurable improvement in both memory usage and lookup latency.
An obvious next step would be to store the full DNS response in wire format, patching only per-client fields like the message ID on each lookup. But this has drawbacks. DNSSEC records are only included when the client sets the DO (DNSSEC OK) flag. Storing a complete wire format message means either caching two variants, one with DNSSEC and one without, or filtering them out of an already-built message. There is also a cost to parsing the full message on every lookup, which the enum approach we just described avoids by storing already-parsed records.
As a middle ground, we store just the record data as raw bytes, while keeping the rest of the cache entry as
…(truncated for reading performance)
Discussion
Sign in to join the discussion.
Keep reading
Optional: create a free account to save items, track programs, and sync across web + app. Reading stays free.