The perils of other peoples services

TL;DR: I created a completely self-contained reverse geo-location service which you can host yourself. Code and docker images available on github.

I have been using freegeoip for a long time, knowing that it had been deprecated. I forked the repository, thinking that at least I would have the source available to rebuild it in case I needed to. Everything worked just fine, so I didn't take the time to dive into the details of where the data came from. Silly me.

Eventually, that came back to bite me in the form of the service suddenly returning this instead of a json document containing a location:

Try again later.

So I dug into the logs to try and figure out what was going on. What I got was that the server could not download new geo data:

database error: download failed (will retry in 1m35s): Get http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz: dial tcp: lookup geolite.maxmind.com on 192.168.65.1:53: no such host

It seems that the geodata provider had discontinued their service (not surprising) and that the freegeoip server didn't just reuse the latest data it had retrieved (slightly more surprising).

Learning from my mistakes

So I could just choose another free reverse geo location provider. There are plenty to choose from, after all. But what happens next time one of them issues a deprecation warning, or even worse, discontinues their service?

This would be just one more external dependency that I would have to keep up to date and worry about for future proofing my service.

What happens when the assigned ip ranges change? Luckily the ip ranges assigned by country seems to be largely unchanged since 2012, giving enough stability to this data to allow me to treat it as essentially static. Much more static than the landscape of free services out there.

Finding a new data set

The next challenge was to find a new data set which is complete enough and has a permissive enough license to be useful in real world scenarios.

Luckily, the nice people at ip2location.com have shared location data under a license which allows copying, distribution, and creation of derivative works as long as one displays the following acknowledgement:

This site or product includes IP2Location LITE data available from https://www.ip2location.com.

Standards

Now that we have acquired a data set, and we are going to re-implement the service using this new data source, we might as well take a glimpse at what standards are out there.

Schema.org has a Place structure which seems useful for specifying where an ip number is located. This is not unlike the data structure which the now-defunct freegeoip service provided. It looks something like this:

{
  "@context": "http://schema.org",
  "@type": "Place",
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 12.34567,
    "longitude": 12.34567
  },
  "address": {
    "@type": "PostalAddress",
    "postalCode": "90210",
    "addressLocality": "Beverly Hills",
    "addressRegion": "California",
    "addressCountry": "US"
  },
  "additionalProperty": {
    "name": "timeZone",
    "value": "-08:00"
  }
}

Deploying

Getting the service up and running is as simple as:

docker-compose up

Of course, npm dependencies are known to be volatile, so there is a pre-built docker image:

docker run -p 3000:3000 \
  docker.pkg.github.com/cyborch/geoip/geoip-devel:1.0.0

This expects a database to exist. The database can be created using the sql scripts available in the schema folder to build the schema and import the geo location data.

Should you want to deploy to a production setup, make a copy of db.development.json in config called db.production.json, fill it with the credentials for your production database, set the NODE_CONFIG environment variable to production and run the new image. You could also use secrets from either docker or kubernetes to get the production configuration into the running container.

Voila

There you have it! This is all there is to getting your own self-hosted reverse geo-location service up and running.

Source and pre-built docker image: https://github.com/cyborch/geoip