Adding geographic context to IP addresses turns raw logs into something that can be filtered, mapped, and alerted on. Country and region breakdowns make traffic analysis and anomaly hunting easier than scanning unstructured text.

The geoip filter in Logstash performs lookups against a MaxMind GeoIP2-compatible database (.mmdb) and enriches events with location metadata. Results are written under a target field (commonly geoip), which can include country, region, city, and latitude/longitude pairs.

Only routable public IP addresses reliably resolve; private, loopback, and reserved ranges typically produce no match and may add failure tags. When reading from a file input, the logstash user must be able to read the log file and write the configured sincedb path. For geospatial features in Elasticsearch and Kibana, ensure geoip.location is mapped as a geo_point field in the destination index.

Steps to use the Logstash geoip filter:

  1. Create a pipeline configuration file at /etc/logstash/conf.d/70-geoip.conf.
    input {
      file {
        id => "read_app_log"
        path => "/var/lib/logstash/examples/geoip.log"
        start_position => "beginning"
        sincedb_path => "/var/lib/logstash/sincedb-geoip"
      }
    }
    
    filter {
      if [log][file][path] == "/var/lib/logstash/examples/geoip.log" {
        grok {
          id => "extract_client_ip"
          match => { "message" => "%{IP:client_ip}" }
          tag_on_failure => ["_no_client_ip"]
        }
    
        if [client_ip] {
          geoip {
            id => "enrich_client_ip"
            source => "client_ip"
            target => "geoip"
          }
        }
      }
    }
    
    output {
      if [log][file][path] == "/var/lib/logstash/examples/geoip.log" {
        elasticsearch {
          id => "send_to_elasticsearch"
          hosts => ["http://elasticsearch.example.net:9200"]
          index => "app-geoip-%{+YYYY.MM.dd}"
        }
      }
    }

    The example extracts an IP into client_ip using grok so geoip receives a single IP string; adjust the pattern to match the log format, or point source at an existing IP field such as clientip or [client][ip].

  2. Test the pipeline configuration.
    $ sudo -u logstash /usr/share/logstash/bin/logstash --path.settings /etc/logstash --path.data /tmp/logstash-configtest --config.test_and_exit
    Configuration OK

    Syntax validation does not confirm file permissions or Elasticsearch connectivity, which are validated when the service runs.

  3. Restart the Logstash service to apply the geoip filter.
    $ sudo systemctl restart logstash

    Restarting Logstash briefly interrupts ingestion while pipelines reload.

  4. Append a test log line containing a public IP address to /var/lib/logstash/examples/geoip.log.
    $ printf '%s\n' '8.8.8.8 - - [07/Jan/2026:21:38:15 +0000] "GET /geoiptest HTTP/1.1" 200 123 "-" "curl/8.5.0"' | sudo tee -a /var/lib/logstash/examples/geoip.log
    8.8.8.8 - - [07/Jan/2026:21:38:15 +0000] "GET /geoiptest HTTP/1.1" 200 123 "-" "curl/8.5.0"

    If no new events appear, remove or change the configured sincedb_path so the file input treats the file as new and re-tracks its position.

    Private or reserved ranges (for example 10.0.0.0/8, 192.168.0.0/16, 127.0.0.0/8) typically return no GeoIP match.

  5. Query the Elasticsearch index for the test event.
    $ curl -s -G "http://elasticsearch.example.net:9200/app-geoip-*/_search" \
      --data-urlencode "q=client_ip:8.8.8.8" \
      --data-urlencode "size=1" \
      --data-urlencode "filter_path=hits.hits._index,hits.hits._source.message,hits.hits._source.client_ip,hits.hits._source.geoip" \
      --data-urlencode "pretty"
    {
      "hits" : {
        "hits" : [
          {
            "_index" : "app-geoip-2026.01.07",
            "_source" : {
              "message" : "8.8.8.8 - - [07/Jan/2026:21:38:15 +0000] \"GET /geoiptest HTTP/1.1\" 200 123 \"-\" \"curl/8.5.0\"",
              "client_ip" : "8.8.8.8",
              "geoip" : {
                "geo" : {
                  "location" : {
                    "lat" : 37.751,
                    "lon" : -97.822
                  },
                  "timezone" : "America/Chicago",
                  "continent_code" : "NA",
                  "country_name" : "United States",
                  "country_iso_code" : "US"
                },
                "ip" : "8.8.8.8"
              }
    ##### snipped #####
            }
          }
        ]
      }
    }

    Geospatial maps require geoip.geo.location to be indexed as a geo_point; incorrect mapping commonly results in location fields appearing but not being usable in map visualizations.

  6. Review pipeline metrics for geoip processing.
    $ curl -s http://localhost:9600/_node/stats/pipelines?pretty
    {
      "pipelines" : {
        "main" : {
          "plugins" : {
            "filters" : [
              {
                "id" : "extract_client_ip",
                "name" : "grok",
                "events" : {
                  "in" : 1,
                  "out" : 1
                }
              },
              {
                "id" : "enrich_client_ip",
                "name" : "geoip",
                "events" : {
                  "in" : 1,
                  "out" : 1
                }
              }
            ]
          }
    ##### snipped #####
        }
      }
    }

    Setting explicit id values on the grok and geoip plugins makes them easier to spot in the monitoring API output.