Skip to main content

Modify a GeoIP Database

This guide walks through updating an existing GeoIP .mmdb database with MMDB CLI. You can add a new network record, edit fields on an existing record, replace a record completely, or remove a network from the database.

MMDB files map IP networks to records. When you modify a database, you are changing the record attached to a CIDR network such as 1.1.1.0/24, 8.8.8.8/32, or 2001:db8::/32.

Prerequisites

  • mmdb-cli installed (Installation)
  • An existing .mmdb file, for example GeoLite2-ASN.mmdb
  • Basic familiarity with JSON and CIDR notation
tip

Write updates to a new output file first. Keep the original database unchanged until you inspect and verify the updated file.


Step 1 - Inspect the current record

Before changing a network, inspect the current data so you know which fields already exist:

mmdb-cli inspect -i GeoLite2-ASN.mmdb 1.1.1.1

Example output:

- query: 1.1.1.1
records:
- network: 1.1.1.0/24
record:
autonomous_system_number: 13335
autonomous_system_organization: CLOUDFLARENET

If you want machine-readable output while preparing a change, use JSON:

mmdb-cli inspect -i GeoLite2-ASN.mmdb -f json 1.1.1.1

Step 2 - Create an update file

Create a file named geoip-updates.json. The update command expects a top-level dataset array. Each item describes one operation:

{
"version": "v1",
"dataset": [
{
"network": "1.1.1.0/24",
"method": "deep_merge",
"data": {
"is_anycast": true
}
}
]
}

Use data, not record, in update files. The record field is used by datasets for generate; update operations use data.

If your update data contains integers, add a top-level schema so MMDB CLI stores those values with the expected MMDB type. Without a schema, JSON numbers are stored as float64.

{
"version": "v1",
"schema": {
"autonomous_system_number": "uint32",
"geoname_id": "uint32",
"confidence": "float64"
},
"dataset": []
}

Step 3 - Choose the right method

MethodUse it when you want to
replaceAdd a new network record or replace the full record for an existing network
deep_mergeAdd or edit nested fields while keeping existing fields
top_level_mergeAdd or edit top-level fields; nested objects under the same top-level key are replaced as a whole
removeRemove the target network from the database

If you are unsure which edit method to use, start with deep_merge for partial updates and replace for full record changes.


Step 4 - Add a new record

To add a record for a new network, use replace. For a single IPv4 address, use /32. For a single IPv6 address, use /128.

{
"version": "v1",
"dataset": [
{
"network": "203.0.113.10/32",
"method": "replace",
"data": {
"country": {
"iso_code": "US",
"names": {
"en": "United States"
}
},
"custom": {
"source": "internal",
"confidence": 95.5
}
}
}
]
}

Apply the update:

mmdb-cli update -i GeoLite2-Country.mmdb -o GeoLite2-Country-Updated.mmdb -d geoip-updates.json

Step 5 - Edit an existing record

Use deep_merge when you want to add or change fields without losing the rest of the record.

{
"version": "v1",
"dataset": [
{
"network": "1.1.1.0/24",
"method": "deep_merge",
"data": {
"custom": {
"is_cloudflare": true,
"reviewed_by": "network-team"
}
}
}
]
}

Run the update:

mmdb-cli update -i GeoLite2-ASN.mmdb -o GeoLite2-ASN-Updated.mmdb -d geoip-updates.json

After the update, the original ASN fields remain and the new custom fields are added.


Step 6 - Replace a full record

Use replace when the new data should become the complete record for the network.

{
"version": "v1",
"schema": {
"autonomous_system_number": "uint32"
},
"dataset": [
{
"network": "1.1.1.0/24",
"method": "replace",
"data": {
"autonomous_system_number": 13335,
"autonomous_system_organization": "Cloudflare, Inc.",
"custom": {
"is_cloudflare": true
}
}
}
]
}

Fields that are not included in data are removed from that network's record.


Step 7 - Remove a record

Use remove to remove the target network from the database. The update schema still requires a data object, so pass an empty object.

{
"version": "v1",
"dataset": [
{
"network": "203.0.113.10/32",
"method": "remove",
"data": {}
}
]
}

Apply the removal:

mmdb-cli update -i GeoLite2-Country.mmdb -o GeoLite2-Country-Updated.mmdb -d geoip-updates.json
note

remove removes a network record. To remove only one field from a record, use replace and provide the complete record without that field.


Step 8 - Apply multiple changes at once

You can include multiple operations in one update file. They are processed in order.

{
"version": "v1",
"dataset": [
{
"network": "203.0.113.10/32",
"method": "replace",
"data": {
"country": {
"iso_code": "US"
},
"custom": {
"source": "internal"
}
}
},
{
"network": "1.1.1.0/24",
"method": "deep_merge",
"data": {
"custom": {
"is_cloudflare": true
}
}
},
{
"network": "198.51.100.0/24",
"method": "remove",
"data": {}
}
]
}

Apply all changes:

mmdb-cli update -i GeoLite2-Country.mmdb -o GeoLite2-Country-Updated.mmdb -d geoip-updates.json

Step 9 - Verify and inspect the result

Verify that the updated file is a valid MMDB database:

mmdb-cli verify -i GeoLite2-Country-Updated.mmdb

Expected output:

The MMDB file is valid

Inspect every changed network before replacing the original file:

mmdb-cli inspect -i GeoLite2-Country-Updated.mmdb 203.0.113.10 1.1.1.1 198.51.100.1

An IP address with no matching network returns an empty records list. If the removed network was inside a broader network that still exists, lookup may return that broader network instead.


Common mistakes

ProblemFix
The update file uses recordUse data for update operations
A single IP was updated without a prefixUse /32 for IPv4 or /128 for IPv6
Existing fields disappearedUse deep_merge for partial updates instead of replace
Integer fields became floatsAdd a top-level schema for numeric fields
A field should be deleted, not the whole networkUse replace with the complete desired record
The output file overwrote expectationsWrite to a new filename, verify it, then promote it

Summary

TaskMethodExample
Add a new recordreplace203.0.113.10/32 with full data
Edit part of a recorddeep_mergeAdd custom.is_cloudflare
Edit top-level fields onlytop_level_mergeAdd is_datacenter
Replace the full recordreplaceProvide the complete desired record
Remove a network recordremoveUse data: {}

See also