COREDNS-GEOIP(7)

πŸ“¦
version 1.15.0 Β· 3 versions
, in opensuse-leap 16.0

🌸 COREDNS-GEOIP(7) 🌸

okay so before we even get into it β€” welcome to the geoip page of the CoreDNS Plugins manual, bestie. grab a snack, this one's a chonky read but it's honestly so good. πŸ’–


✨ NAME ✨

geoip β€” it looks up .mmdb databases (that's the MaxMind db file format, over at https://maxmind.github.io/MaxMind-DB/) using the client IP, and then it adds all that associated geoip data into the context request. slay. πŸ—ΊοΈ


πŸ’… DESCRIPTION πŸ’…

So the geoip plugin is literally here to enrich the data tied to Client IP addresses β€” think geoip info like City, Country, and Network ASN. 🌍 GeoIP data usually comes in .mmdb format, which is a database format that maps IPv4 and IPv6 addresses to data records using a binary search tree. ikyk. 🌳

All that data gets added by leveraging the metadata plugin, and then you can retrieve the values by using it too. it's giving teamwork. 🫢

Longitude example β€” here's the vibe:

import (
    "strconv"
    "github.com/coredns/coredns/plugin/metadata"
)
// ...
if getLongitude := metadata.ValueFunc(ctx, "geoip/longitude"); getLongitude != nil {
    if longitude, err := strconv.ParseFloat(getLongitude(), 64); err == nil {
        // Do something useful with longitude.
    }
} else {
    // The metadata label geoip/longitude for some reason, was not set.
}
// ...

City example β€” yes we're doing this one too πŸ™οΈ:

import (
    "github.com/coredns/coredns/plugin/metadata"
)
// ...
if getCity := metadata.ValueFunc(ctx, "geoip/city/name"); getCity != nil {
    city := getCity()
    // Do something useful with city.
} else {
    // The metadata label geoip/city/name for some reason, was not set.
}
// ...

ASN example β€” ok last one, and it's a two-parter πŸ”₯:

import (
    "strconv"
    "github.com/coredns/coredns/plugin/metadata"
)
// ...
if getASN := metadata.ValueFunc(ctx, "geoip/asn/number"); getASN != nil {
    if asn, err := strconv.ParseUint(getASN(), 10, 32); err == nil {
        // Do something useful with asn.
    }
}
if getASNOrg := metadata.ValueFunc(ctx, "geoip/asn/org"); getASNOrg != nil {
    asnOrg := getASNOrg()
    // Do something useful with asnOrg.
}
// ...

πŸŽ€ DATABASES πŸŽ€

The supported databases use city schema β€” like ASN, City, and Enterprise. basically .mmdb files are generally supported, as long as their field names correctly map to the Metadata Labels down below. πŸ‘‘ Other database types with different schemas? not supported yet, sorry bestie. πŸ™ˆ

Free and commercial GeoIP .mmdb files are commonly available from vendors like:


⭐ SYNTAX ⭐

geoip [DBFILE]

orrrr

geoip [DBFILE] {
    [edns-subnet]
}
  • DBFILE β€” the mmdb database file path. highly recommend you update your mmdb database periodically for more accurate results. like, keep it fresh. 🧚
  • edns-subnet β€” Optional. Use EDNS0 subnet (https://en.wikipedia.org/wiki/EDNS_Client_Subnet) (if present) for Geo IP instead of the source IP of the DNS request. This helps identifying the closest source IP address through intermediary DNS resolvers, and it also makes GeoIP testing easy β€” look: dig +subnet=1.2.3.4 @dns-server.example.com www.geo-aware.com. πŸ’€βœ¨
NOTE: due to security reasons, recursive DNS resolvers may mask a few bits off of the clients' IP address, which can cause inaccuracies in GeoIP resolution. ⚠️

There's no defined mask size in the standards, but there are examples, so let's look at those: RFC 7871's example (https://www.rfc-editor.org/info/rfc7871/#section-13) conceals the last 72 bits of an IPv6 source address, and the NS1 Help Center mentions (https://help.ns1.com/hc/en-us/articles/360020256573-About-the-EDNS-Client-Subnet-ECS-DNS-extension) that ECS-enabled DNS resolvers send only the first three octets (eg. /24) of the source IPv4 address. the way they just chop off bits 😭


πŸ¦‹ EXAMPLES πŸ¦‹

This config configures the City database, and looks up geolocation based on EDNS0 subnet if present. iconic behaviour honestly:

. {
    geoip /opt/geoip2/db/GeoLite2-City.mmdb {
      edns-subnet
    }
    metadata # Note that metadata plugin must be enabled as well.
}

The view plugin can use geoip metadata as selection criteria to give you GSLB functionality. ✨ In this example, clients from the city "Exampleshire" will receive answers for example.com from the zone defined in example.com.exampleshire-db. All other clients will receive answers from the zone defined in example.com.db. Note that the order of the two example.com server blocks below is important; the default viewless server block must be last. don't mess with the order, i'm serious. πŸ‘€

example.com {
    view exampleshire {
      expr metadata('geoip/city/name') == 'Exampleshire'
    }
    geoip /opt/geoip2/db/GeoLite2-City.mmdb
    metadata
    file example.com.exampleshire-db
}

example.com {
    file example.com.db
}

πŸ’œ METADATA LABELS πŸ’œ

A limited set of fields will be exported as labels, and all values are stored using strings regardless of their underlying value type β€” so you may have to convert it back to its original type. πŸ“ note that numeric values are always represented in base 10. no hex shenanigans here, bestie.


ok and that's literally only the beginning β€” the labels themselves are coming up next, so don't go anywhere 🫢✨

  • geoip/city/name β€” string β€” e.g. Cambridge β€” the city name in English πŸ”₯
  • geoip/country/code β€” string β€” e.g. GB β€” the country's ISO 3166-1 code, iykyk (https://en.wikipedia.org/wiki/ISO_3166-1)
  • geoip/country/name β€” string β€” e.g. United Kingdom β€” the country name, in English, no cap
  • geoip/country/is_in_european_union β€” bool β€” e.g. false β€” literally just either true or false πŸ’…
  • geoip/continent/code β€” string β€” e.g. EU β€” check the Continent codes #continent-codes section for the full lore
  • geoip/continent/name β€” string β€” e.g. Europe β€” the continent name in English 🌍
  • geoip/latitude β€” float64 β€” e.g. 52.2242 β€” base 10, max available precision, she's precise ✨
  • geoip/longitude β€” float64 β€” e.g. 0.1315 β€” base 10, max available precision, same energy πŸŽ€
  • geoip/timezone β€” string β€” e.g. Europe/London β€” the timezone, bestie πŸ•°οΈ
  • geoip/postalcode β€” string β€” e.g. CB4 β€” the postal code πŸ“
  • geoip/subdivisions/code β€” string β€” e.g. ENG,TWH β€” comma separated ISO 3166-2
  • (this one's the tail end of the subdivision label above β€” no type, no default listed) β€” <https://en.wikipedia.org/wiki/ISO_3166-2> subdivision(region) codes, e.g. first level (province), second level (state). πŸ—ΊοΈ
  • geoip/asn/number β€” type uint, default 396982 ✨ β€” literally the autonomous system number.
  • geoip/asn/org β€” type string, default GOOGLE-CLOUD-PLATFORM πŸ’– β€” the autonomous system organization. ok that default is such a flex ngl.

🌍 CONTINENT CODES 🌍

bestie these are the continent values, memorise them or keep this open, either works 🫢

  • AF β€” Africa
  • AN β€” Antarctica
  • AS β€” Asia
  • EU β€” Europe
  • NA β€” North America
  • OC β€” Oceania
  • SA β€” South America

πŸ’€ NOTABLE CHANGES πŸ’€

🚨 real talk: if your code was checking for 0 to mean "no coords", that check is lowkey cooked now. it's "" in v2, and 0 was never actually "missing" anyway because Null Island is a whole real place. so yeah β€” possibly-breaking, read the migration guide, do not come for me. 🫠

and that's the whole page, bestie!! πŸŽ€ you officially know more about geoip than like 99% of people. the vibe check passed, the docs ate, periodt. πŸ’…βœ¨

this little icon was brought to you by CoreDNS, October 2026 edition, filed under COREDNS-GEOIP(7) in the manual shelf β€” go read it again, iykyk πŸ¦‹πŸ’œ

πŸ“¦ where this page comes from