geometrikks

geometrikks

Docker app from GilbN's Repository

Overview

GeoMetrikks is a real-time geolocation analytics tool that tails your reverse proxy's access logs, performs GeoIP lookups, and visualizes traffic on a live interactive map. No external services required.

Reads nginx, Traefik JSON and Caddy JSON access logs, detected per file.

Features:

  • Live GeoIP map with real-time request visualization

  • Traffic analytics dashboard with configurable date ranges, including a Top ASNs view of the networks your traffic comes from

  • CrowdSec integration

  • Searchable access log history with filtering and sorting

  • WebSocket-backed live log tail

  • Structured application logging with a live in-UI log viewer, a fail2ban/CrowdSec-friendly login log, and downloadable rotated archives

  • Batch import of archived logs

  • Multi-source ingestion: one instance can tail several files and keep the sources apart, or headless agents can write into the same database

  • Requires a TimescaleDB + PostGIS database - install the companion "geometrikks-timescaledb" template from this same repository first, or point this container at your own TimescaleDB+PostGIS instance.

  • Also requires a free MaxMind GeoLite2 account for GeoIP data (https://www.maxmind.com/en/geolite2/signup). See the repository README for full setup steps.

GeoMetrikks Unraid Templates

Unraid Community Apps templates for GeoMetrikks - a real-time geolocation analytics tool that tails your reverse proxy's access logs, does GeoIP lookups, and visualizes traffic on a live interactive map. It reads nginx, Traefik JSON and Caddy JSON logs, and works out which is which per file.

This repository ships two Docker templates:

Template What it is
geometrikks-timescaledb TimescaleDB + PostGIS database - install this first
geometrikks The GeoMetrikks app and web UI

Setup

1. Install the database

Add geometrikks-timescaledb from Community Apps. Set a POSTGRES_PASSWORD - you'll reuse this exact value in step 2. Leave POSTGRES_USER (geouser) and POSTGRES_DB (geometrikks) at their defaults unless you have a reason to change them.

The DB Port config (default 5432) is exposed to your LAN so the app container can reach it.

The template starts Postgres with timescaledb.max_background_workers=40 (and the matching max_parallel_workers / max_worker_processes). GeoMetrikks registers around 32 TimescaleDB jobs whose refresh policies all fire on the same tick, and the image's default of 16 workers can't cover that: the database log fills with failed to launch job ... out of background workers and continuous aggregates stop refreshing. If you installed this template before September 2026, remove and re-add the container to pick the setting up, or add the three -c flags yourself under Post Arguments.

2. Install the app

Add geometrikks from Community Apps and fill in:

  • DB_HOST - your Unraid server's IP address (e.g. 192.168.1.50)
  • DB_PASSWORD - the same password you set for geometrikks-timescaledb in step 1
  • APP_ADMIN_USER / APP_ADMIN_PASSWORD - your web UI login or APP_AUTH_DISABLED=true
  • Access Logs path - point this at wherever your reverse proxy writes its access logs (defaults to a SWAG-style path; change it for Nginx Proxy Manager, Traefik, Caddy, or whatever you actually run)

Leave DB_PORT/DB_USER/DB_DATABASE at their defaults unless you changed the matching values in step 1.

PUID / PGID and file ownership

The app container starts as root only long enough to remap its internal user to PUID:PGID, fix ownership of the GeoIP Data and App Logs folders, and then drop privileges - the app itself never runs as root. The template defaults to Unraid's usual 99:100 (nobody:users), which matches the rest of your appdata and the ownership SWAG/Nginx Proxy Manager give their log files, so no manual chown step is needed.

Your Access Logs mount is read-only and is not touched by that fix-up - those files just need to be readable by PUID:PGID on the host.

App logs

The App Logs path mount holds GeoMetrikks' own logs:

  • geometrikks.log - structured JSONL application log
  • login.log - plain-text login/logout/failed-login events, in a format fail2ban and CrowdSec can parse

Both rotate by size and gzip their archives (LOG_MAIN_* / LOG_LOGIN_*). You can also browse and download them in the web UI under Settings -> Logs. Mounting the path is optional but recommended: without it the logs are lost whenever the container is recreated, and host-side tools can't read login.log.

Which log format?

The mount lands at /var/log/nginx inside the container whatever proxy you run, and LOGPARSER_LOG_PATHS names the file there. The format is detected per file, so Traefik and Caddy JSON logs need nothing beyond the path. Pin it with LOGPARSER_LOG_FORMATS (geometrikks-json, nginx, traefik-json, caddy-json) if detection gets it wrong.

Nginx is the one that needs work on your side: GeoMetrikks reads a keyed JSON log_format you have to add to your nginx.conf. The older positional format still parses, so an existing setup keeps working. Both are in the upstream README.

Tailing several files? LOGPARSER_LOG_PATHS takes a JSON list, and giving LOGPARSER_HOST_NAME a list of the same length labels each file separately, so the UI can filter them as separate sources.

3. Get a free MaxMind GeoLite2 key

GeoMetrikks needs MaxMind's free GeoLite2 databases for GeoIP lookups:

  1. Sign up at https://www.maxmind.com/en/geolite2/signup
  2. Create a license key under your account
  3. Set MAXMINDDB_USER_ID and MAXMINDDB_LICENSE_KEY on the geometrikks template

Without these, GeoMetrikks runs in degraded mode (no GeoIP lookups) until you add them.

Two databases are downloaded into the GeoIP Data mount: City, which drives the map and geo analytics, and ASN, which records the network behind each request and fills the Top ASNs view. Set GEOIP_ASN_ENABLED=false to skip the second one.

4. Open the web UI

http://<your-unraid-ip>:8000/ - log in with the admin credentials you set in step 2.

Notes

  • Full environment variable reference: https://github.com/GilbN/geometrikks/blob/main/docs/configuration.md. Most of it is exposed as "advanced" settings on the geometrikks template - expand "Show more settings" when adding the container to see database pool tuning, log parser tuning, log rotation, analytics retention, and map settings.
  • LOGPARSER_IGNORE_IPS is worth setting: give it your own public IP (or any CIDR) to drop that traffic entirely - no geo event, access log or debug row. LAN traffic is never ingested in the first place, so this is for your own WAN-side hits.
  • Reaching the UI through a TLS reverse proxy? Set APP_SESSION_SECURE=true, and put your proxy's IP/CIDR in APP_TRUSTED_PROXIES (e.g. 172.17.0.0/16) so client IPs are read from X-Forwarded-For. Leave APP_SESSION_SECURE at false if you browse to http://<unraid-ip>:8000 directly, or logins won't stick.
  • API_LOG_LEVEL is deprecated - use LOG_LEVEL. If you're updating an existing container, clear the old API_LOG_LEVEL value.
  • APP_PROXY_ADVISORY puts a warning on Settings > Status when the traffic in a tailed file comes from CDN or private addresses, which means your proxy is logging the hop in front of it instead of the visitor. Set it to false if that's deliberate (Tailscale-only access, a CDN you front on purpose).
  • APP_MODE is full here and should stay that way. agent turns the container headless: no UI, no API, just tail, geolocate and write into a shared database. It's for a second GeoMetrikks next to another proxy, feeding this one.
  • The template sets --stop-timeout=20 so the app can finish flushing its ingestion batch on shutdown. Unraid's default of 10 seconds kills it mid-write.
  • GeoMetrikks itself: https://github.com/GilbN/geometrikks

Media gallery

1 / 9

Install Geometrikks on Unraid in a few clicks.

Find Geometrikks in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Geometrikks Review the template variables and paths Click Install

Requirements

Requires a reachable TimescaleDB+PostGIS database (see the companion geometrikks-timescaledb template) and a free MaxMind GeoLite2 account for GeoIP downloads.
Nginx access logs need a specific log_format. Traefik and Caddy JSON access logs are read as they come. See the repository README for the nginx log_format to use.

Related apps

Explore more like this

Explore all

Details

Repository
ghcr.io/gilbn/geometrikks:latest
Last Updated2026-09-08
First Seen2026-07-20

Runtime arguments

Web UI
http://[IP]:[PORT:8000]/
Network
bridge
Shell
bash
Privileged
false
Extra Params
--restart=unless-stopped --stop-timeout=20

Template configuration

WebUIPorttcp

HTTP port for the GeoMetrikks web UI.

Target
8000
Default
8000
GeoIP DataPathrw

Persistent storage for the downloaded MaxMind GeoLite2 City and ASN databases. The container fixes ownership of this folder to PUID:PGID on every start, so no manual chown is needed.

Target
/app/data/geoip
Default
/mnt/cache/appdata/geometrikks/geoip
App LogsPathrw

Where GeoMetrikks writes its own logs: geometrikks.log (structured JSONL) and login.log (plain text, fail2ban/CrowdSec-friendly), plus their gzipped archives. Mounting this keeps logs across container recreation and lets host-side tools read login.log. Ownership is fixed to PUID:PGID on start.

Target
/app/logs
Default
/mnt/cache/appdata/geometrikks/logs
Access LogsPathro

Host directory holding your reverse proxy's access logs, mounted read-only. The default assumes SWAG. Point it at wherever nginx, Nginx Proxy Manager, Traefik or Caddy writes instead. The files must be readable by PUID:PGID. Whichever proxy you run, the container-side path stays /var/log/nginx, so that is what LOGPARSER_LOG_PATHS refers to.

Target
/var/log/nginx
Default
/mnt/cache/appdata/swag/log/nginx
PUIDVariable

User ID the app runs as. The container starts as root only long enough to remap its internal user and fix ownership of the GeoIP and App Logs folders, then drops privileges. 99 is Unraid's 'nobody' - keep it so the appdata folders match the rest of your shares, and so nginx logs written by SWAG/NPM (which also default to 99:100) stay readable.

Default
99
PGIDVariable

Group ID the app runs as. 100 is Unraid's 'users' group.

Default
100
APP_ADMIN_USERVariable

Web UI login username.

Default
admin
APP_ADMIN_PASSWORDVariable

Web UI login password. Required unless APP_AUTH_DISABLED=true.

APP_AUTH_DISABLEDVariable

Set true ONLY if this container sits behind an authenticating reverse proxy (Authelia, Tailscale, etc).

Default
false
APP_SESSION_SECUREVariable

Mark the session cookie Secure so browsers only send it over HTTPS. Set true when you reach the UI through a TLS reverse proxy; leave false if you browse to http://server-ip:8000 directly, or you will not be able to log in.

Default
false
APP_TRUSTED_PROXIESVariable

Reverse-proxy IPs/CIDRs allowed to supply X-Forwarded-For, e.g. 172.17.0.0/16 for Unraid's docker bridge. Single value, comma-separated, or a JSON list. Empty (default): forwarded headers are never trusted.

MAXMINDDB_USER_IDVariable

MaxMind account ID, for GeoLite2 database auto-download. Free account: https://www.maxmind.com/en/geolite2/signup

MAXMINDDB_LICENSE_KEYVariable

MaxMind license key, for GeoLite2 database auto-download.

LOGPARSER_LOG_PATHSVariable

Access log file(s) to tail, named as the container sees them under /var/log/nginx. Single path, or a JSON list [&quot;/var/log/nginx/access.log&quot;, &quot;/var/log/nginx/site2/access.log&quot;] for several files.

Default
/var/log/nginx/access.log
LOGPARSER_LOG_FORMATSVariable

Format of the file(s) above: auto (detected per file), geometrikks-json, nginx, traefik-json or caddy-json. One value covers every path, or give a JSON list matched to LOGPARSER_LOG_PATHS by position. Leave at auto unless detection gets it wrong.

Default
auto
LOGPARSER_HOST_NAMEVariable

Hostname label attached to ingested log events. A JSON list matched to LOGPARSER_LOG_PATHS by position labels each file separately, e.g. [&quot;edge-01&quot;, &quot;edge-02&quot;], and the UI then filters them as separate sources.

Default
Unraid
DB_HOSTVariable

IP address of your TimescaleDB container - typically your Unraid server's IP if you installed the companion geometrikks-timescaledb template.

DB_PASSWORDVariable

Database password. Must match POSTGRES_PASSWORD in the geometrikks-timescaledb template.

DB_PORTVariable

Database port.

Default
5432
DB_USERVariable

Database user. Must match POSTGRES_USER in the geometrikks-timescaledb template.

Default
geouser
DB_DATABASEVariable

Database name. Must match POSTGRES_DB in the geometrikks-timescaledb template.

Default
geometrikks
LOG_LEVELVariable

Application log verbosity (DEBUG, INFO, WARNING, ERROR).

Default
INFO
DB_ECHOVariable

Enable SQLAlchemy query logging.

Default
false
DB_ECHO_POOLVariable

Enable SQLAlchemy connection pool logging.

Default
false
DB_MAX_OVERFLOWVariable

Max connections above pool_size.

Default
10
DB_POOL_SIZEVariable

Database connection pool size.

Default
5
DB_POOL_TIMEOUTVariable

Connection pool timeout in seconds.

Default
30
DB_POOL_RECYCLEVariable

Connection recycle time in seconds.

Default
3600
DB_POOL_DISABLEDVariable

Disable connection pooling.

Default
false
DB_POOL_PRE_PINGVariable

Enable pool pre-ping to check connections before use.

Default
true
DB_MIGRATE_ON_STARTUPVariable

Run pending database migrations at startup. Leave true. With it off the app expects the schema to already be at head and refuses to start otherwise.

Default
true
DB_DROP_ON_STARTUPVariable

DESTRUCTIVE - drops all tables on startup. Development only. Leave false.

Default
false
GEOIP_LOCALESVariable

JSON list of GeoIP locales to use.

Default
[&quot;en&quot;]
GEOIP_VALIDATE_DB_PATHVariable

Fail settings validation when the GeoIP database file is missing.

Default
false
GEOIP_VALIDATE_LOCALESVariable

Validate that the specified GeoIP locales are supported.

Default
true
GEOIP_REFRESH_DAYSVariable

Re-download the GeoLite2 databases when older than this many days.

Default
7
GEOIP_ASN_ENABLEDVariable

Download the GeoLite2 ASN database alongside the City one and record the network and organization behind each request. This is what fills the Top ASNs view. Uses the same MaxMind credentials; without them the app ingests without ASN data.

Default
true
GEOIP_DB_PATHVariable

Path to the GeoLite2 City database, relative to /app. Leave it alone so the file lands in the GeoIP Data mount.

Default
data/geoip/GeoLite2-City.mmdb
GEOIP_ASN_DB_PATHVariable

Path to the GeoLite2 ASN database, relative to /app. Leave it alone so the file lands in the GeoIP Data mount.

Default
data/geoip/GeoLite2-ASN.mmdb
LOG_DIRVariable

Directory GeoMetrikks writes its own log files to, relative to /app. Leave at 'logs' so it lands in the App Logs mount.

Default
logs
LOG_MAIN_MAX_BYTESVariable

Rotate the main JSONL log (geometrikks.log) when it exceeds this size in bytes. Default 10 MiB.

Default
10485760
LOG_MAIN_BACKUP_COUNTVariable

Number of gzipped main-log archives to keep.

Default
5
LOG_LOGIN_MAX_BYTESVariable

Rotate the login log (login.log) when it exceeds this size in bytes. Default 10 MiB.

Default
10485760
LOG_LOGIN_BACKUP_COUNTVariable

Number of gzipped login-log archives to keep.

Default
5
API_LOG_LEVELVariable

DEPRECATED - use LOG_LEVEL instead. Still honoured as a fallback for existing deployments; leave blank on new installs.

LOGPARSER_IGNORE_IPSVariable

IPs/CIDRs the parser drops entirely - no geo event, access log or debug row. Use it to keep your own WAN traffic through the reverse proxy out of the data(LAN traffic is never ingested), e.g. 88.99.77.66. Single value, comma-separated, or a JSON list. Empty (default): nothing is ignored.

LOGPARSER_ENABLEDVariable

Tail and ingest the access logs. Set false for an instance that only serves the UI over data another instance writes.

Default
true
LOGPARSER_POLL_INTERVALVariable

Interval in seconds to poll the log file for new entries.

Default
1.0
LOGPARSER_SEND_LOGSVariable

Send parsed logs to the database.

Default
true
LOGPARSER_BATCH_SIZEVariable

Max records before a forced commit.

Default
100
LOGPARSER_COMMIT_INTERVALVariable

Max time interval in seconds between database commits.

Default
5.0
LOGPARSER_SKIP_VALIDATIONVariable

Skip validation of log lines.

Default
false
LOGPARSER_STORE_DEBUG_LINESVariable

Store all raw log lines in the debug table, not just malformed ones.

Default
false
ANALYTICS_RAW_RETENTION_DAYSVariable

Days to keep raw geo_events and access_logs data.

Default
180
ANALYTICS_DEBUG_RETENTION_DAYSVariable

Days to keep access_log_debug data.

Default
30
ANALYTICS_HOURLY_RETENTION_DAYSVariable

Days to keep hourly continuous aggregate data.

Default
60
ANALYTICS_CAGG_REFRESH_INTERVAL_MINUTESVariable

Minutes between continuous aggregate refreshes.

Default
5
ANALYTICS_COMPRESSION_AFTER_DAYSVariable

Days after which to compress hypertable chunks.

Default
7
SCHEDULER_ENABLEDVariable

Enable scheduled background tasks.

Default
true
SCHEDULER_LOCATION_REFRESH_INTERVAL_MINUTESVariable

Minutes between GeoLocation.last_hit refresh jobs.

Default
10
MAP_HOME_LATITUDEVariable

Optional destination latitude for live request routes. Set both home coordinates to override external-IP auto-detection.

MAP_HOME_LONGITUDEVariable

Optional destination longitude for live request routes.

MAP_AUTO_DETECT_HOMEVariable

Resolve the server's public IP at startup and geolocate it when home coordinates are unset.

Default
true
MAP_PUBLIC_IP_URLVariable

JSON endpoint used for public-IP discovery; response must contain an 'ip' field.

Default
https://api64.ipify.org?format=json
MAP_PUBLIC_IP_TIMEOUTVariable

Timeout in seconds for public-IP discovery.

Default
3.0
MAP_HOME_REFRESH_HOURSVariable

Hours between re-detections of this instance's own public IP and its map home location.

Default
24
MAP_HOME_LOCATIONSVariable

Per-hostname home overrides as a JSON object of latitude/longitude pairs, e.g. {&quot;edge-01&quot;: [59.91, 10.75]}. Use it for a source whose public IP geolocates wrong (CGNAT, VPN) or for hostnames in logs shipped from another machine. An override beats auto-detection; delete the entry and its row goes at the next start.

CROWDSEC_LAPI_URLVariable

CrowdSec Local API base URL, e.g. http://crowdsec:8080. Leave blank to disable CrowdSec integration.

CROWDSEC_BOUNCER_API_KEYVariable

Bouncer API key (cscli bouncers add geometrikks) - grants read access to CrowdSec decisions.

CROWDSEC_MACHINE_IDVariable

Machine ID (cscli machines add) - optional, enables ban/unban from the GeoMetrikks UI.

CROWDSEC_MACHINE_PASSWORDVariable

Machine password - optional, enables ban/unban from the GeoMetrikks UI.

CROWDSEC_DEFAULT_BAN_DURATIONVariable

Default duration for manual bans (Go duration string).

Default
4h
CROWDSEC_REQUEST_TIMEOUTVariable

LAPI request timeout in seconds.

Default
10.0
CROWDSEC_VERIFY_TLSVariable

Verify TLS when LAPI uses https.

Default
true
CROWDSEC_STREAM_POLL_INTERVALVariable

Seconds between decision-stream polls feeding live ban/unban updates.

Default
15.0
APP_NAMEVariable

Application name shown in API metadata.

Default
GeoMetrikks API
APP_DESCRIPTIONVariable

Application description shown in API metadata.

Default
Real-time GeoIP lookups and traffic analytics API
APP_DEBUGVariable

Enable debug mode. Development only.

Default
false
APP_ENVIRONMENTVariable

Application environment name.

Default
production
APP_PROXY_ADVISORYVariable

Warn on Settings &gt; Status when recent traffic for a tailed file comes from CDN or private addresses, which means the proxy is logging the hop in front of it rather than the visitor. Set false when that traffic mix is deliberate, such as Tailscale-only access or a CDN you front on purpose.

Default
true
APP_MODEVariable

full runs everything: web UI, API, migrations, scheduler, CrowdSec and ingestion. agent is headless. It tails, geolocates and writes into a shared database and serves only /health and /health/ready, so the WebUI port and the admin login do nothing. Set agent only on an extra copy of this container that feeds a full instance elsewhere, and give it the same image tag as that instance.

Default
full
API_HOSTVariable

Address the server binds inside the container. Leave at 0.0.0.0 or the port mapping cannot reach it.

Default
0.0.0.0
API_PORTVariable

Port the server listens on inside the container. Change it and you must change the container side of the WebUI port mapping to match, or the UI becomes unreachable.

Default
8000