Document the runtime GeoLite2 fetch path

Cover the new MAXMIND_LICENSE_KEY route in the self-hosting guide as the
first of the three ways to get the offline databases (fetch, mount, bake),
and note that the monthly runtime refresh keeps a keyed host current on its
own. Add the variable to both env examples and to the env-check manifest so
the optional group lists it.

Refs #659
This commit is contained in:
Marc-André Bombeck committed 2026-08-09 22:09:12 +02:00
1 parent 869d91c4c2
commit 0a2cacb8b3
4 files changed
+84 -17

No files matched your search

+3
View File
@@ -264,3 +264,6 @@ API_TOKEN_HMAC_KEY="REPLACE-WITH-OUTPUT-OF-openssl-rand-hex-32"
#
# Directory the offline GeoLite2 databases are read from; default /opt/geolite2 (the path the image bakes its own copy into). Point it at a directory you mount yourself to bring your own MMDBs — see docs/self-hosting/geolite2.md.
# GEOLITE2_DIR="/var/lib/geolite2"
#
# MaxMind licence key for the runtime GeoLite2 fetch (issue #659): set it and the worker downloads both databases into GEOLITE2_DIR itself, no rebuild or manual mount. Free key at https://www.maxmind.com/en/geolite2/signup — see docs/self-hosting/geolite2.md.
# MAXMIND_LICENSE_KEY="your-maxmind-licence-key"
+20 -7
View File
@@ -328,14 +328,27 @@ API_TOKEN_HMAC_KEY="<openssl rand -hex 32>"
# a stock `:latest` the directory is usually empty and every login IP goes
# to the online provider instead.
#
# To resolve locations on your own host: create a free MaxMind account,
# pull both databases with `geoipupdate`, mount the directory into the
# container read-only (the commented `volumes:` block in docker-compose.yml
# shows the line), and point this at the container path. Pair it with
# IP_GEO_LOOKUP_DISABLED="1" above for zero IP egress. Refreshing the files
# on the host is enough -- the reader picks them up on the next container
# restart. See docs/self-hosting/geolite2.md.
# Three ways to get the databases:
#
# 1. RUNTIME FETCH (issue #659) -- set MAXMIND_LICENSE_KEY below and the
# worker downloads both databases into GEOLITE2_DIR itself on the next
# boot and monthly thereafter. No image rebuild, no manual mount. This is
# the way for a stock `:latest` pull.
# 2. Bring your own -- create a free MaxMind account, pull both databases
# with `geoipupdate`, mount the directory into the container read-only
# (the commented `volumes:` block in docker-compose.yml shows the line),
# and point GEOLITE2_DIR at the container path. Refreshing the files on
# the host is enough -- the reader picks them up on the next restart.
# 3. Bake them at build time (build your own image with a licence key).
#
# Pair any of them with IP_GEO_LOOKUP_DISABLED="1" above for zero IP egress.
# See docs/self-hosting/geolite2.md.
# GEOLITE2_DIR="/var/lib/geolite2"
#
# MaxMind licence key for the runtime fetch (option 1 above). Free: sign up at
# https://www.maxmind.com/en/geolite2/signup, then My Account -> Manage
# License Keys. Unset, nothing is fetched and the online provider answers.
# MAXMIND_LICENSE_KEY="your-maxmind-licence-key"
# -----------------------------------------------------------------------------
# Environmental-context module (Open-Meteo) -- optional
+57 -10
View File
@@ -16,11 +16,53 @@ next to every sign-in. Two tiers can produce that:
The offline tier is optional and absent by default. The published image
only carries the databases when the image build had a MaxMind licence key
(`scripts/fetch-geolite2.sh`, run from the release workflow), which is a
build-time decision you do not control when you pull `:latest`. Bringing
your own copy is the way to get the offline tier on a self-host, and it
is the subject of this page.
build-time decision you do not control when you pull `:latest`. Two ways
get the offline tier onto a stock `:latest` self-host, and both are on this
page: let the app **fetch the databases itself** at runtime (simplest, one
env var), or **bring your own** copy and mount it.
## What you need
## Runtime fetch (one env var)
Set a MaxMind licence key and the app downloads the databases itself — no
rebuild, no mount, no `geoipupdate` on the host.
1. Create a free MaxMind account at
<https://www.maxmind.com/en/geolite2/signup>, then a licence key under
**My Account → Manage License Keys**. GeoLite2 is free; the account and
key exist because MaxMind requires them for the download.
2. Put the key in your `.env`:
```env
MAXMIND_LICENSE_KEY="your-licence-key"
```
`MAXMIND_LICENSE_KEY` is on the compose `environment:` whitelist, so a
value in `.env` reaches the container. Leave `GEOLITE2_DIR` unset to use
the default `/opt/geolite2`, which the image already owns as the app
user for exactly this write.
3. `docker compose up -d`. On the next worker boot the app fetches
`GeoLite2-City.mmdb` and `GeoLite2-ASN.mmdb` into `GEOLITE2_DIR` and
starts reading them — no restart needed beyond the one that picks up the
new env var. It refreshes once a month afterwards (MaxMind reissues on
the first Tuesday), so a keyed host stays current on its own.
The fetch is fail-soft: if the key is wrong or MaxMind is unreachable, the
previous state stands (the online provider keeps answering) and the worker
log carries the reason. It never writes a partial database. Confirm it
landed under **Admin → System status** (`Geo data: Offline (GeoLite2)`) or
from the host: `docker compose exec app ls -l /opt/geolite2`.
If you would rather not have the app reach MaxMind on its own schedule —
for example you keep a curated database copy — skip the key and use
**bring your own** below instead. The two do not conflict: a read-only
bind mount wins, because the runtime fetch cannot write over it and just
fails soft.
## Bring your own (mount a copy)
### What you need
- A free MaxMind account. Sign up at
<https://www.maxmind.com/en/geolite2/signup>, then create a licence key
@@ -30,7 +72,7 @@ is the subject of this page.
two `.mmdb` files onto the host. Most distributions package
`geoipupdate`; there is also an official container image.
## Setup
### Setup
1. Pick a directory on the host and fetch both databases into it. With
`geoipupdate`, `/etc/GeoIP.conf` looks like this:
@@ -88,12 +130,17 @@ health data is a reasonable place to make that trade.
MaxMind reissues GeoLite2 on its own schedule, and a stale city database
is quietly wrong rather than loudly broken — it keeps answering, with
last month's allocations. Run `geoipupdate` on a schedule (a weekly cron
or systemd timer is enough) against the same directory.
last month's allocations.
The reader loads each database once per worker process and holds it, so a
refreshed file on the host is picked up at the next container restart,
not immediately. A weekly `geoipupdate` followed by
The **runtime fetch** handles this for you: it re-downloads both databases
once a month and resets the reader in place, so a keyed host stays current
with no cron of your own and no restart.
The **bring-your-own** path is yours to keep fresh. Run `geoipupdate` on a
schedule (a weekly cron or systemd timer is enough) against the same
directory. The reader loads each database once per worker process and holds
it, so a refreshed file on the host is picked up at the next container
restart, not immediately. A weekly `geoipupdate` followed by
`docker compose restart app` keeps the two in step.
## Licence and attribution
+4
View File
@@ -140,6 +140,10 @@
{
"name": "GEOLITE2_DIR",
"purpose": "Directory holding GeoLite2-City.mmdb + GeoLite2-ASN.mmdb. Point it at a read-only mount to bring your own databases. Default /opt/geolite2."
},
{
"name": "MAXMIND_LICENSE_KEY",
"purpose": "MaxMind licence key for the runtime GeoLite2 fetch (issue #659). Set it and the worker downloads both databases into GEOLITE2_DIR on boot and monthly, no rebuild or manual mount. Free at maxmind.com/en/geolite2/signup."
}
]
},