diff --git a/.env.example b/.env.example index e1bc76e38..0bf9d08a8 100644 --- a/.env.example +++ b/.env.example @@ -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" diff --git a/.env.production.example b/.env.production.example index 93aa99e1e..dfc0a7497 100644 --- a/.env.production.example +++ b/.env.production.example @@ -328,14 +328,27 @@ API_TOKEN_HMAC_KEY="" # 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 diff --git a/docs/self-hosting/geolite2.md b/docs/self-hosting/geolite2.md index 4e69c1c3e..7910de171 100644 --- a/docs/self-hosting/geolite2.md +++ b/docs/self-hosting/geolite2.md @@ -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 + , 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 , 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 diff --git a/scripts/env-manifest.json b/scripts/env-manifest.json index 4613b0c9d..2a7ab0c02 100644 --- a/scripts/env-manifest.json +++ b/scripts/env-manifest.json @@ -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." } ] },