maxminddb-rust

A Rust library for reading MaxMind DB files, including GeoIP2 and GeoLite2 geolocation databases, with typed decoding and optional memory-mapped access.

Library
Cargo
v0.30.3
277stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
81/100Excellent
Development Activity96
Maintenance72
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
86/100Excellent
Architecture85
Code Quality92
Innovation78
Learning Curve90

maxminddb is the Rust reader for the MaxMind DB binary format used by MaxMind’s GeoIP2 and GeoLite2 databases. It opens a .mmdb file (via a plain read or, with the mmap feature, a memory map), walks the format’s binary search tree to resolve an IpAddr to a data record, and exposes that record either as a fully typed geoip2::City/Country/Enterprise/etc. struct through Serde, or as a single field via decode_path() without deserializing the whole record.

The crate is a low-level, single-purpose building block rather than a framework: it owns database parsing and IP-to-record resolution and leaves everything else — caching strategy, HTTP serving, database refresh — to the calling application. Reader is Send + Sync, so a single loaded database can be shared across threads in a web server or batch job without extra locking. within() and networks() let callers iterate every network a CIDR range or the whole database covers, which is used for exporting or diffing database contents rather than single-IP lookups.

Authored and maintained by Gregory Oschwald since 2014, it is one of the most widely used Rust crates for IP geolocation and sits underneath many Rust-based analytics, security, and CDN tooling projects that need to resolve IP addresses to country, city, or ASN data offline.

What You Get

  • A Reader<S> type that opens a MaxMind DB file by plain file read (open_readfile) or, with the mmap feature, by memory-mapping it for low-overhead repeated lookups
  • Strongly typed record structs in the geoip2 module (City, Country, Enterprise, Isp, AnonymousIp, ConnectionType, Domain, Asn, DensityIncome) that deserialize a lookup result via Serde with no manual field unwrapping for nested data
  • A decode_path() API for pulling a single field (e.g. country.iso_code) out of a record without paying the cost of decoding the entire structure
  • within() and networks() iterators for walking every network in a CIDR range or the full database, with WithinOptions to control aliasing and empty-value filtering
  • Feature flags for mmap (memory-mapped access), simdutf8 (SIMD-accelerated UTF-8 validation), and unsafe-str-decode (skip UTF-8 validation for trusted data), so the performance/safety tradeoff is opt-in per application

Common Use Cases

  • Web/API request enrichment - resolving a visitor’s IP to country or city inside a Rust web server to power geo-targeted content, pricing, or compliance logic
  • Fraud and abuse signals - looking up ASN, ISP, and anonymous-proxy/VPN flags for an incoming IP as one input into a fraud-scoring pipeline
  • Log and analytics enrichment - batch-annotating access logs or analytics events with country/city/ASN fields by iterating stored IPs against a loaded database
  • Network inventory and CIDR analysis - using within()/networks() to enumerate or diff which networks a GeoIP database assigns to particular locations or ASNs
  • CDN and routing decisions - embedding IP-to-region resolution directly in a Rust proxy or edge service instead of calling an external geolocation API

Under The Hood

Architecture The crate centers on a generic Reader<S: AsRef<[u8]>> (src/reader.rs) that owns the raw buffer plus positions computed once at open time — metadata, record_size, node_count, node_byte_size, ipv4_start, pointer_base, and data_section_len — located by scanning for the METADATA_START_MARKER trailer. lookup() walks the format’s binary search tree bit-by-bit against an IpAddr to resolve a data-section offset, returned as a lightweight LookupResult handle (src/result.rs) rather than an eagerly decoded value. Decoding is delegated to a hand-written Serde Decoder (src/decoder.rs) that interprets the MaxMind DB’s self-describing binary data format (pointers, maps, arrays, strings, numbers) and drives any Deserialize target, so decode() and decode_path() share one decoding path with different entry depths. within.rs reuses the same search tree with IpInt bit-level comparisons to iterate networks within a CIDR instead of resolving a single IP. This tree-walk/decoder split is the crate’s core abstraction — every typed record in geoip2.rs plugs in purely through Deserialize, so extending supported record types requires no changes to the reader or decoder.

Tech Stack Rust 2021 edition, published as the maxminddb crate. Runtime dependencies: ipnetwork 0.21 for CIDR/network types, serde 1.0 with derive for typed record decoding, memchr 2.4 for fast marker scanning, and thiserror 2.0 for the structured error enum. Two capabilities are opt-in via Cargo features: mmap pulls in memmap2 0.9 for Reader<Mmap> memory-mapped access, and simdutf8 0.1.5 gives SIMD-accelerated UTF-8 validation (mutually exclusive with the unsafe-str-decode feature, which skips validation entirely). Dev dependencies include criterion for four dedicated benchmarks (lookup, serde_usage, within, metadata), rayon, env_logger, and serde_json. CI (GitHub Actions) runs a standard test matrix plus codeql.yml for static analysis, audit.yml for dependency vulnerability scanning, fuzz.yml for continuous fuzzing, and doc.yml/release.yml for docs.rs and crates.io publishing.

Code Quality Testing is extensive: src/reader_test.rs is a ~48KB suite exercising lookups, within()/networks() iteration, and error paths against real .mmdb files pulled in via a git submodule, and error.rs carries its own inline #[cfg(test)] unit tests for error-message formatting. A fuzz/ directory with cargo-fuzz targets and a dedicated fuzzing feature (exposing internal decode/verify entry points) shows deliberate investment in fuzz coverage of the binary parser, the highest-risk surface in a crate that parses untrusted-format files. Error handling is fully typed through a #[non_exhaustive] thiserror-derived MaxMindDbError enum that carries byte offsets and JSON-pointer-like field paths on decode failures rather than swallowing context. The crate enforces #![deny(trivial_casts, trivial_numeric_casts, unused_import_braces)] at the lint level, and CI layers CodeQL and cargo-audit on top of the standard test run.

API Design decode_path() is the standout ergonomic choice — most GeoIP libraries force full-record deserialization even when only one field is needed, while this crate lets callers pull a single value (e.g. ["country", "iso_code"]) without paying for the rest. Typed geoip2 structs default nested sections to present-but-empty rather than requiring Option unwrapping at every nesting level, which removes a common source of caller boilerplate. WithinOptions uses a chained builder style (skip_empty_values(), include_aliased_networks()) that reads clearly at call sites, and the mmap/simdutf8/unsafe-str-decode feature flags make the safety/performance tradeoff an explicit opt-in rather than a silent default. Nearly every public entry point in lib.rs and geoip2.rs carries a runnable doctest, keeping the docs.rs examples compile-checked against the actual API surface.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search