How to Install, Update, and Monitor MaxMind’s GeoLite2 City Database on a Self-Hosted Server

geolite2 city database auto update monitoring guide

How to Install, Update, and Monitor MaxMind’s GeoLite2 City Database on a Self-Hosted Server

Managing a GeoIP database efficiently is critical for enhancing your server logs, analytics, and user experience. In this post, I’ll walk you through the complete, professional-grade setup to:

  • Install the free MaxMind GeoLite2-City database
  • Create an auto-update script that downloads new database files monthly
  • Set up a cron wrapper script to email you if an update fails or succeeds

This guide assumes you’re running a Debian/Ubuntu-based Linux server and have SSH access.

Let’s dive in!


Prerequisites

Before we start, ensure you have:

  • wget, tar, and bash installed (default on most servers)
  • Basic knowledge of SSH and cron jobs
  • msmtp or another minimal SMTP relay for sending email notifications

If you haven’t installed msmtp, you can do so with:

sudo apt update
sudo apt install msmtp msmtp-mta

This provides a lightweight email sending capability without needing a full SMTP server.


Step 1: Get Your MaxMind License Key

MaxMind requires a license key to download GeoLite2 databases.

  1. Go to MaxMind’s GeoLite2 Signup Page
  2. Create an account and generate a License Key
  3. Save your license key securely

We’ll use this key to automate downloads.


Step 2: Create the Auto-Update Script

Let’s build a script that:

  • Downloads the latest GeoLite2-City database
  • Extracts it
  • Moves it to the correct server folder
  • Cleans up temporary files
  • Sends a success or failure email notification

Create the script:

sudo nano /usr/local/bin/update-geoip-db.sh

Paste the following into the file:

#!/bin/bash

# Variables
LICENSE_KEY="YOUR_LICENSE_KEY" # Replace with your MaxMind license key
DOWNLOAD_DIR="/usr/share/GeoIP"
TMP_DIR="/tmp/geoip_update"
EMAIL="your@email.com" # Replace with your email

# Create temp directory
mkdir -p "$TMP_DIR"
cd "$TMP_DIR" || { echo "GeoIP Update: Failed to cd to $TMP_DIR" | mailx -s "GeoIP Update FAILED on $(hostname)" "$EMAIL"; exit 1; }

# Download latest GeoLite2-City database
wget "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${LICENSE_KEY}&suffix=tar.gz" -O GeoLite2-City.tar.gz
if [ $? -ne 0 ]; then
    echo "GeoIP Update: Failed to download GeoLite2-City.tar.gz" | mailx -s "GeoIP Update FAILED on $(hostname)" "$EMAIL"
    exit 1
fi

# Extract
tar -xvzf GeoLite2-City.tar.gz
if [ $? -ne 0 ]; then
    echo "GeoIP Update: Failed to extract GeoLite2-City.tar.gz" | mailx -s "GeoIP Update FAILED on $(hostname)" "$EMAIL"
    exit 1
fi

# Move .mmdb file
find . -name "*.mmdb" -exec mv -f {} "${DOWNLOAD_DIR}/GeoLite2-City.mmdb" \;
if [ $? -ne 0 ]; then
    echo "GeoIP Update: Failed to move GeoLite2-City.mmdb" | mailx -s "GeoIP Update FAILED on $(hostname)" "$EMAIL"
    exit 1
fi

# Clean up temp files
rm -rf "$TMP_DIR"

# Success Notification
echo "GeoIP Update: Successfully updated at $(date)" | mailx -s "GeoIP Update SUCCESSFUL on $(hostname)" "$EMAIL"

exit 0

Make the script executable:

sudo chmod +x /usr/local/bin/update-geoip-db.sh

✅ Done! You now have a script that updates the database and notifies you on success or failure.


Step 3: Create a Cron Wrapper Script

We want an additional layer of monitoring for any critical cron jobs.

This wrapper will:

  • Run any script
  • Monitor its exit status
  • Email you the output if it fails
  • (Optional) Email you even on success

Create the wrapper:

sudo nano /usr/local/bin/cron_email_on_failure.sh

Paste the following:

#!/bin/bash

# Arguments
SCRIPT="$1"
LOGFILE="$2"
EMAIL="your@email.com" # Replace with your email

# Run the script
/bin/bash "$SCRIPT" >> "$LOGFILE" 2>&1
EXIT_CODE=$?

# Tail log content
BODY=$(tail -n 50 "$LOGFILE")

# Email based on result
if [ $EXIT_CODE -ne 0 ]; then
    SUBJECT="CRON JOB FAILED: $(basename "$SCRIPT") on $(hostname)"
    echo "$BODY" | mailx -s "$SUBJECT" "$EMAIL"
else
    SUBJECT="CRON JOB SUCCESS: $(basename "$SCRIPT") on $(hostname)"
    echo "$BODY" | mailx -s "$SUBJECT" "$EMAIL"
fi

exit 0

Make it executable:

sudo chmod +x /usr/local/bin/cron_email_on_failure.sh

✅ This wrapper gives you full, monitored control over any important script.


Step 4: Set Up Crontab

Now, let’s schedule our database updates automatically.

Edit your crontab:

crontab -e

Add the following lines:

MAILTO=""

# GeoIP Database Update (runs at 3:00 AM on the 1st of each month)
0 3 1 * * /usr/local/bin/cron_email_on_failure.sh /usr/local/bin/update-geoip-db.sh /var/log/update-geoip-db.log

✅ This ensures:

  • No noisy default emails
  • You get professional failure/success notifications
  • Last 50 lines of the script log are included in the email

Additional Recommendations

  • Log Rotation:
    • You may want to rotate your /var/log/update-geoip-db.log once every few months to prevent it from growing indefinitely.
  • Database Backup:
    • Optionally, archive the previous month’s .mmdb file before overwriting it.
  • System Monitoring:
    • Combine this setup with broader monitoring tools like Monit or Zabbix for full server oversight.

Conclusion

Congratulations! You now have a:

  • Fully automated
  • Email-monitored
  • Failure-resilient
  • Production-grade

GeoLite2-City database update system running on your server.

This setup not only keeps your GeoIP data current but also ensures you are immediately notified if anything breaks — keeping your operations clean, professional, and low-maintenance.

If you found this guide helpful, feel free to bookmark it for future reference, or leave a comment below if you have questions!

By William McGill

William McGill is an IT veteran and independent contractor with over 20 years of experience in technology, networking, and insurance. He blends tech expertise with real-world problem-solving, working across industries from insurance claims to system administration. While most of his writing focuses on tech, freelancing, and adapting to an ever-evolving digital landscape, he occasionally explores topics that simply spark his curiosity—because life isn’t just about work, but about being human.

Leave a comment

Your email address will not be published. Required fields are marked *