---
title: Environment Variables Reference
description: Canonical reference with defaults, comments, restrictions, and restart behavior for GeoPulse environment variables.
---

# Environment Variables Reference

This page is the canonical environment variable reference for GeoPulse. Every listed variable includes default value, comments, restrictions, and restart guidance.

- Frontend runtime source: `frontend/docker-entrypoint.sh`
- Backend runtime source: `backend/src/main/resources/application.properties`
- Deployment source: `.env.example`, `docker-compose*.yml`, and Helm templates

## Frontend Runtime Vars

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_BACKEND_URL` | `http://geopulse-backend:8080` | Backend upstream URL injected into Nginx reverse-proxy config. | Reachable HTTP URL from frontend container network. | Frontend container restart |
| `CLIENT_MAX_BODY_SIZE` | `200M` | Nginx \`client_max_body_size\` for uploads. | Nginx size format (for example \`200M\`, \`1G\`). | Frontend container restart |
| `OSM_RESOLVER` | `Auto-detected (\`127.0.0.11 8.8.8.8\` in Docker)` | DNS resolvers used by Nginx for OpenStreetMap tile hosts. | Space-separated resolver IPs reachable from container. | Frontend container restart |

## Backend Runtime Vars

Backend runtime currently includes **310** distinct env vars.

Notes:
- `GEOPULSE_AUTH_SIGN_UP_ENABLED` is deprecated but still supported for backward compatibility.
- Runtime env changes require backend restart to take effect.

### Core and Database (9)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_CORS_ENABLED` | `true` | Enables/disables backend CORS handling. Property: `quarkus.http.cors.enabled`. New `.env.example` sets this to `false` for same-origin nginx deployments. | `true` or `false`. | Backend restart |
| `GEOPULSE_CORS_ORIGINS` | `(falls back to GEOPULSE_UI_URL)` | Comma-separated CORS origins when CORS is enabled. Property: `quarkus.http.cors.origins`. | One URL or comma-separated URLs. | Backend restart |
| `GEOPULSE_DATABASE_TRANSACTION_TIMEOUT_MINUTES` | `60` | Default database transaction timeout. Import processing has its own timeout. Property: \`quarkus.transaction-manager.default-transaction-timeout\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_AVATAR_MAX_SIZE_BYTES` | `1048576` | Maximum allowed size for uploaded custom avatars (in bytes). Property: `geopulse.avatar.max-size-bytes`. | Positive integer value (bytes). | Backend restart |
| `GEOPULSE_POSTGRES_PASSWORD` | `(required/no default)` | PostgreSQL configuration Property: \`quarkus.datasource.password\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_POSTGRES_URL` | `(required/no default)` | PostgreSQL configuration Property: \`quarkus.datasource.jdbc.url\`. | Valid PostgreSQL JDBC URL (\`jdbc:postgresql://...\`). | Backend restart |
| `GEOPULSE_POSTGRES_USERNAME` | `(required/no default)` | PostgreSQL configuration Property: \`quarkus.datasource.username\`. | Required; no default value is provided. | Backend restart |
| `GEOPULSE_PUBLIC_BASE_URL` | `(empty)` | Public base URL used for callback/link generation. Property: `geopulse.public-base-url`. | Valid URL. | Backend restart |
| `GEOPULSE_UI_URL` | `http://localhost:5555` | Legacy fallback variable for CORS origins and OIDC callback fallback. Deprecated: use `GEOPULSE_CORS_ORIGINS` and `GEOPULSE_PUBLIC_BASE_URL`. | One URL or comma-separated URLs. | Backend restart |

### Version Update Check (5)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_VERSION_CHECK_GITHUB_API_URL` | `https://api.github.com/repos/tess1o/geopulse/releases/latest` | GitHub Releases API endpoint used to resolve the latest public GeoPulse release. Property: `geopulse.version-check.github-api-url`. | Valid URL returning GitHub release JSON. | Backend restart |
| `GEOPULSE_VERSION_CHECK_RELEASE_URL` | `https://github.com/tess1o/geopulse/releases` | Fallback/public release page URL shown to users when an update is available. Property: `geopulse.version-check.release-url`. | Valid URL. | Backend restart |
| `GEOPULSE_VERSION_CHECK_CACHE_TTL_MINUTES` | `60` | Cache TTL for latest-release metadata to reduce GitHub API calls. Property: `geopulse.version-check.cache-ttl-minutes`. | Positive integer minutes. | Backend restart |
| `GEOPULSE_VERSION_CHECK_CONNECT_TIMEOUT_SECONDS` | `5` | HTTP connect timeout for the GitHub latest-release lookup. Property: `geopulse.version-check.connect-timeout-seconds`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_VERSION_CHECK_READ_TIMEOUT_SECONDS` | `8` | HTTP read timeout for the GitHub latest-release lookup. Property: `geopulse.version-check.read-timeout-seconds`. | Positive integer seconds. | Backend restart |

### Authentication and Access (24)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_ADMIN_EMAIL` | `(empty)` | Admin configuration Property: \`geopulse.admin.email\`. | Valid email address. | Backend restart |
| `GEOPULSE_FIRST_USER_ADMIN` | `true` | Promotes the first registered user to ADMIN when `GEOPULSE_ADMIN_EMAIL` is empty. Property: \`geopulse.admin.first-user-admin.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_ADMIN_LOGIN_BYPASS_ENABLED` | `true` | Property: \`geopulse.auth.admin-login-bypass.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_GUEST_ROOT_REDIRECT_TO_LOGIN_ENABLED` | `false` | Redirect signed-out users from `/` to `/login` instead of rendering Home. Property: \`geopulse.auth.guest-root-redirect-to-login.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_MOBILE_CODE_EXPIRY_SECONDS` | `5` | Lifetime of one-time mobile session exchange code in seconds. Property: \`geopulse.auth.mobile.code-expiry-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_AUTH_MOBILE_DEEPLINK_URL` | `app://auth/code/exchange` | Deep link URL used by web flow to open the mobile app for session exchange. Property: \`geopulse.auth.mobile.deeplink.url\`. | Valid mobile deep link URI. | Backend restart |
| `GEOPULSE_AUTH_MOBILE_DEEPLINK_CLEANUP_CRON` | `0 0 3 1 * ?` | Cron expression for cleanup of expired mobile deep-link auth codes (default: day 1 of month at 03:00). Property: \`geopulse.auth.mobile.deeplink.cleanup.cron\`. | Valid Quarkus cron expression. | Backend restart |
| `GEOPULSE_AUTH_MOBILE_DEEPLINK_CLEANUP_ENABLED` | `true` | Enables/disables scheduled cleanup of expired mobile deep-link auth codes. Property: \`geopulse.auth.mobile.deeplink.cleanup.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_LOGIN_ENABLED` | `true` | Property: \`geopulse.auth.login.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_OIDC_LOGIN_ENABLED` | `true` | Property: \`geopulse.auth.oidc.login.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_OIDC_REGISTRATION_ENABLED` | `true` | Property: \`geopulse.auth.oidc.registration.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_PASSWORD_LOGIN_ENABLED` | `true` | Property: \`geopulse.auth.password-login.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_PASSWORD_REGISTRATION_ENABLED` | `true` | Property: \`geopulse.auth.password-registration.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_REGISTRATION_ENABLED` | `true` | Property: \`geopulse.auth.registration.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_SECURE_COOKIES` | `false` | Property: \`geopulse.auth.secure-cookies\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AUTH_SIGN_UP_ENABLED` | `true` | Deprecated property, replaced by geopulse.auth.password-registration.enabled Property: \`geoupuse.auth.sign-up-enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_COOKIE_DOMAIN` | `(empty)` | Property: \`geopulse.auth.cookie-domain\`. | Optional; empty value uses fallback behavior. | Backend restart |
| `GEOPULSE_INVITATION_BASE_URL` | `(empty)` | User invitation configuration Base URL for generating invitation links (e.g., https://geopulse.example.com) If empty, frontend will use window.location.origin Property: \`geopulse.invitation.base-url\`. | Valid URL. | Backend restart |
| `GEOPULSE_JWT_ACCESS_TOKEN_LIFESPAN` | `1800` | JWT Property: \`smallrye.jwt.new-token.lifespan\`. | Integer seconds. | Backend restart |
| `GEOPULSE_JWT_ISSUER` | `http://localhost:8080` | Property: \`smallrye.jwt.new-token.issuer\`. | String value. Follow subsystem documentation. | Backend restart |
| `GEOPULSE_JWT_PRIVATE_KEY_LOCATION` | `file:/app/keys/jwt-private-key.pem` | Property: \`smallrye.jwt.sign.key.location\`. | Readable path/URI to private key file. | Backend restart |
| `GEOPULSE_JWT_PUBLIC_KEY_LOCATION` | `file:/app/keys/jwt-public-key.pem` | Property: \`mp.jwt.verify.publickey.location\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_JWT_REFRESH_TOKEN_LIFESPAN` | `604800` | JWT Property: \`jwt.refresh-token.lifespan\`. | Integer value. | Backend restart |
| `GEOPULSE_USER_DEFAULT_DISTANCE_UNIT` | `KILOMETERS` | Default distance unit for newly created users. Property: \`geopulse.user.default-distance-unit\`. Existing users keep their profile preference. | `KILOMETERS` or `MILES`. | Backend restart |
| `GEOPULSE_USER_DEFAULT_TEMPERATURE_UNIT` | `CELSIUS` | Default temperature unit for newly created users. Property: \`geopulse.user.default-temperature-unit\`. Existing users keep their profile preference. | `CELSIUS` or `FAHRENHEIT`. | Backend restart |

### OIDC Core (5)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_OIDC_AUTO_LINK_ACCOUNTS` | `false` | Account Linking Security When true, automatically links OIDC accounts to existing users with matching emails WARNING: Only enable this if you fully trust your OIDC providers to... Property: \`geopulse.oidc.auto-link-accounts\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_OIDC_CALLBACK_BASE_URL` | `(empty)` | OIDC callback base URL. Fallback order: `GEOPULSE_PUBLIC_BASE_URL`, then legacy `GEOPULSE_UI_URL`. Property: `geopulse.oidc.callback-base-url`. | Valid URL. | Backend restart |
| `GEOPULSE_OIDC_CLEANUP_ENABLED` | `true` | OIDC Cleanup Configuration Property: \`geopulse.oidc.cleanup.session-states.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_OIDC_ENABLED` | `false` | OIDC Configuration Property: \`geopulse.oidc.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_OIDC_JWKS_CACHE_TTL_HOURS` | `24` | JWKS (signing keys) caching Lower TTL recommended to handle provider key rotation Property: \`geopulse.oidc.jwks-cache.ttl-hours\`. | Non-negative numeric value. | Backend restart |

### AI, Immich, and Memos (10)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_AI_CHAT_MEMORY_MAX_MESSAGES` | `10` | AI Feature Configuration Property: \`geopulse.ai.chat-memory.max-messages\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_AI_ENCRYPTION_KEY_LOCATION` | `file:/app/keys/ai-encryption-key.txt` | AI Feature Configuration Property: \`geopulse.ai.encryption.key.location\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_AI_LOGGING_ENABLED` | `false` | AI Feature Configuration Property: \`geopulse.ai.logging.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_AI_TOOL_RESULT_MAX_LENGTH` | `12000` | AI Feature Configuration Property: \`geopulse.ai.tool-result.max-length\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMMICH_GEONAMES_NORMALIZATION_MAX_DISTANCE_METERS` | `50000` | Immich search cache Property: \`immich.photos.geonames-normalization.max-distance-meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMMICH_PHOTO_SEARCH_CACHE_MAX_ENTRIES` | `200` | Immich search cache Property: \`immich.photos.search-cache-max-entries\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMMICH_PHOTO_SEARCH_CACHE_TTL_SECONDS` | `300` | Immich search cache Property: \`immich.photos.search-cache-ttl-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_IMMICH_PHOTO_SEARCH_TIMEOUT_SECONDS` | `15` | Immich search timeout Property: \`immich.photos.search-timeout-seconds\`. | Positive numeric value. | Backend restart |
| `GEOPULSE_MEMOS_NOTES_SEARCH_CACHE_MAX_ENTRIES` | `200` | Memos notes search cache maximum entries. Property: \`geopulse.memos.notes.search-cache-max-entries\`. | Positive integer value. | Backend restart |
| `GEOPULSE_MEMOS_NOTES_SEARCH_CACHE_TTL_SECONDS` | `300` | Memos notes search cache TTL in seconds. Property: \`geopulse.memos.notes.search-cache-ttl-seconds\`. | Positive integer seconds. | Backend restart |

### Geocoding and GeoNames (49)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_GEOCODING_DELAY_MS` | `1000` | Delay between geocoding requests (milliseconds) Property: \`geocoding.provider.delay.ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_CACHE_MAX_BBOX_AREA_KM2` | `5000` | Maximum provider bbox area accepted for cache containment matching. Oversized bboxes are dropped and matching falls back to coordinate tolerance. Property: \`geocoding.cache.max-bbox-area-km2\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RETRY_MAX_RETRIES` | `5` | Maximum retry attempts for geocoding provider calls Property: \`quarkus.fault-tolerance.global.retry.max-retries\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RETRY_DELAY_MS` | `1250` | Delay between geocoding provider retry attempts (milliseconds) Property: \`quarkus.fault-tolerance.global.retry.delay\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RETRY_JITTER_MS` | `250` | Retry delay jitter for geocoding provider calls (milliseconds) Property: \`quarkus.fault-tolerance.global.retry.jitter\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_CB_FAILURE_RATIO` | `0.7` | Circuit breaker failure ratio threshold for geocoding provider calls Property: \`quarkus.fault-tolerance.global.circuit-breaker.failure-ratio\`. | Decimal between 0 and 1. | Backend restart |
| `GEOPULSE_GEOCODING_CB_REQUEST_VOLUME` | `10` | Circuit breaker rolling window size for geocoding provider calls Property: \`quarkus.fault-tolerance.global.circuit-breaker.request-volume-threshold\`. | Positive numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_CB_DELAY_SECONDS` | `20` | Circuit breaker open-state delay before half-open probe (seconds) Property: \`quarkus.fault-tolerance.global.circuit-breaker.delay\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_CB_SUCCESS_THRESHOLD` | `2` | Successful half-open calls required to close geocoding circuit breaker Property: \`quarkus.fault-tolerance.global.circuit-breaker.success-threshold\`. | Positive numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RECONCILE_ITEM_MAX_ATTEMPTS` | `4` | Maximum attempts per record during geocoding reconciliation jobs Property: \`geocoding.reconcile.item.max-attempts\`. | Positive numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RECONCILE_CIRCUIT_OPEN_WAIT_MS` | `20000` | Wait before retrying reconciliation when circuit breaker is open (milliseconds) Property: \`geocoding.reconcile.circuit-open-wait.ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_RECONCILE_INTER_ITEM_DELAY_MS` | `1000` | Delay between reconciliation job items (milliseconds) Property: \`geocoding.reconcile.inter-item-delay.ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_FALLBACK_PROVIDER` | `photon` | Fallback geocoding provider (optional) Property: \`geocoding.provider.fallback\`. | Empty or one of \`nominatim\`, \`photon\`, \`googlemaps\`, \`mapbox\`, \`geoapify\`, \`chibigeo\`. | Backend restart |
| `GEOPULSE_GEOCODING_GEOAPIFY_API_KEY` | `(empty)` | Geoapify API key Property: \`geocoding.geoapify.api-key\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_GEOCODING_GEOAPIFY_DELAY_MS` | `0` | Delay between Geoapify requests. Property: \`geocoding.provider.geoapify.delay.ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_GEOAPIFY_ENABLED` | `false` | Property: \`geocoding.provider.geoapify.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_GEOAPIFY_LANGUAGE` | `(empty)` | Geoapify language preference Property: \`geocoding.geoapify.language\`. | Language code or empty. | Backend restart |
| `GEOPULSE_GEOCODING_CHIBIGEO_API_KEY` | `(empty)` | ChibiGeo API key sent as \`X-Api-Key\` Property: \`geocoding.chibigeo.api-key\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_GEOCODING_CHIBIGEO_DELAY_MS` | `0` | Delay between ChibiGeo requests. Property: \`geocoding.provider.chibigeo.delay.ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEOCODING_CHIBIGEO_ENABLED` | `false` | Property: \`geocoding.provider.chibigeo.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_CHIBIGEO_URL` | `https://app.chibigeo.com/v1/photon` | ChibiGeo Photon-compatible endpoint Property: \`quarkus.rest-client.chibigeo-api.url\`. | Valid URL. | Backend restart |
| `GEOPULSE_GEOCODING_CHIBIGEO_LANGUAGE` | `(empty)` | ChibiGeo Photon-compatible language preference Property: \`geocoding.chibigeo.language\`. | One Photon-supported code or empty. | Backend restart |
| `GEOPULSE_GEOCODING_GOOGLE_MAPS_API_KEY` | `(empty)` | API Keys (can also be set via encrypted storage in admin panel) Property: \`geocoding.googlemaps.api-key\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_GEOCODING_GOOGLE_MAPS_ENABLED` | `false` | Property: \`geocoding.provider.googlemaps.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_GOOGLE_MAPS_LANGUAGE` | `(empty)` | Google Maps reverse geocoding language preference sent as the `language` query param. Property: \`geocoding.googlemaps.language\`. | Google supported language code (for example \`en\`, \`uk\`, \`pt-BR\`, \`zh-CN\`) or empty. | Backend restart |
| `GEOPULSE_GEOCODING_MAPBOX_ACCESS_TOKEN` | `(empty)` | Property: \`geocoding.mapbox.access-token\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_GEOCODING_MAPBOX_ENABLED` | `false` | Property: \`geocoding.provider.mapbox.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_NOMINATIM_ENABLED` | `true` | Provider availability flags Property: \`geocoding.provider.nominatim.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_NOMINATIM_PUBLIC_HOST_FORWARD_SEARCH_ENABLED` | `false` | Allow Nominatim forward search/autocomplete on public \`nominatim.openstreetmap.org\` (self-hosted Nominatim is allowed even when this is \`false\`). Property: \`geocoding.nominatim.public-host-forward-search-enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_NOMINATIM_LANGUAGE` | `(empty)` | Provider availability flags Nominatim geocoding language preference (BCP 47 format: en-US, de, uk, ja, etc.) If not set, no Accept-Language header will be sent (existing behavior) Property: \`geocoding.nominatim.language\`. | BCP 47 language tag (for example \`en-US\`) or empty. | Backend restart |
| `GEOPULSE_GEOCODING_NOMINATIM_URL` | `https://nominatim.openstreetmap.org` | Provider availability flags Property: \`quarkus.rest-client.nominatim-api.url\`. | Valid URL. | Backend restart |
| `GEOPULSE_GEOCODING_PHOTON_ENABLED` | `true` | Property: \`geocoding.provider.photon.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEOCODING_PHOTON_LANGUAGE` | `(empty)` | Photon geocoding language preference. Allowed values: \`de\`, \`pl\`, \`el\`, \`en\`, \`es\`, \`fa\`, \`fr\`, \`it\`, \`ja\`, \`ko\`. If not set, no Accept-Language header/lang query parameter will be sent (existing behavior). Property: \`geocoding.photon.language\`. | One allowed code or empty. | Backend restart |
| `GEOPULSE_GEOCODING_PHOTON_URL` | `https://photon.komoot.io` | Property: \`quarkus.rest-client.photon-api.url\`. | Valid URL. | Backend restart |
| `GEOPULSE_GEOCODING_PRIMARY_PROVIDER` | `nominatim` | Primary geocoding provider (nominatim, photon, googlemaps, mapbox, geoapify, chibigeo) Property: \`geocoding.provider.primary\`. | One of \`nominatim\`, \`photon\`, \`googlemaps\`, \`mapbox\`, \`geoapify\`, \`chibigeo\`. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_BATCH_SIZE` | `200` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_CONNECT_TIMEOUT_SECONDS` | `20` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.connect-timeout-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_ENABLED` | `true` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_FORCE_REFRESH` | `false` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.force-refresh\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_MIN_ROW_THRESHOLD` | `200` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.min-row-threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_READ_TIMEOUT_SECONDS` | `120` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.read-timeout-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEONAMES_COUNTRY_IMPORT_URL` | `https://download.geonames.org/export/dump/countryInfo.txt` | GeoNames country dataset import (ISO2 -> country metadata) Property: \`geopulse.geonames.country-import.url\`. | Valid URL. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_BATCH_SIZE` | `1000` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_CONNECT_TIMEOUT_SECONDS` | `20` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.connect-timeout-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_ENABLED` | `true` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_FORCE_REFRESH` | `false` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.force-refresh\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_MIN_ROW_THRESHOLD` | `100000` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.min-row-threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_READ_TIMEOUT_SECONDS` | `300` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.read-timeout-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GEONAMES_IMPORT_URL` | `https://download.geonames.org/export/dump/cities500.zip` | GeoNames cities dataset import (used for city normalization) Property: \`geopulse.geonames.import.url\`. | Valid URL. | Backend restart |

### Import (18)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_IMPORT_BULK_INSERT_BATCH_SIZE` | `500` | Import batch size configuration Property: \`geopulse.import.bulk-insert-batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_CHUNK_SIZE_MB` | `50` | Chunked upload configuration (for bypassing Cloudflare's 100MB upload limit) Files >80MB are split into chunks on the frontend and reassembled on the backend Property: \`geopulse.import.chunk-size-mb\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_CHUNKS_DIR` | `/tmp/geopulse/chunks` | Chunked upload configuration (for bypassing Cloudflare's 100MB upload limit) Files >80MB are split into chunks on the frontend and reassembled on the backend Property: \`geopulse.import.chunks-directory\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_IMPORT_DROP_FOLDER_ENABLED` | `false` | Drop folder import configuration (server-side file pickup) Property: \`geopulse.import.drop-folder.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_IMPORT_DROP_FOLDER_GEOPULSE_MAX_SIZE_MB` | `200` | Drop folder import configuration (server-side file pickup) Property: \`geopulse.import.drop-folder.geopulse-max-size-mb\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_DROP_FOLDER_PATH` | `/data/geopulse-import` | Drop folder import configuration (server-side file pickup) Property: \`geopulse.import.drop-folder.path\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_IMPORT_DROP_FOLDER_POLL_INTERVAL_SECONDS` | `10` | Drop folder import configuration (server-side file pickup) Property: \`geopulse.import.drop-folder.poll-interval-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_IMPORT_DROP_FOLDER_STABLE_AGE_SECONDS` | `10` | Drop folder import configuration (server-side file pickup) Property: \`geopulse.import.drop-folder.stable-age-seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_IMPORT_GEOJSON_STREAMING_BATCH_SIZE` | `500` | GeoJSON streaming parser configuration Batch size for streaming GeoJSON imports - aligns with bulk insert batch size for optimal performance During streaming, batches are flushe... Property: \`geopulse.import.geojson.streaming-batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_GOOGLETIMELINE_STREAMING_BATCH_SIZE` | `500` | Google Timeline streaming parser configuration Batch size for streaming Google Timeline imports - aligns with bulk insert batch size for optimal performance During streaming, ba... Property: \`geopulse.import.googletimeline.streaming-batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_LARGE_FILE_THRESHOLD_MB` | `100` | Import temp file configuration (for large file handling) Files larger than this threshold are saved to temp directory instead of loading into memory This prevents OOM errors wit... Property: \`geopulse.import.large-file-threshold-mb\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_MAX_FILE_SIZE_GB` | `10` | Chunked upload configuration (for bypassing Cloudflare's 100MB upload limit) Files >80MB are split into chunks on the frontend and reassembled on the backend Property: \`geopulse.import.max-file-size-gb\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_MERGE_BATCH_SIZE` | `250` | Import batch size configuration Property: \`geopulse.import.merge-batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_IMPORT_TEMP_DIR` | `/tmp/geopulse/imports` | Import temp file configuration (for large file handling) Files larger than this threshold are saved to temp directory instead of loading into memory This prevents OOM errors wit... Property: \`geopulse.import.temp-directory\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_IMPORT_TEMP_FILE_RETENTION_HOURS` | `24` | Import temp file configuration (for large file handling) Files larger than this threshold are saved to temp directory instead of loading into memory This prevents OOM errors wit... Property: \`geopulse.import.temp-file-retention-hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_IMPORT_TRANSACTION_TIMEOUT_MINUTES` | `1440` | Transaction timeout for import processing only. Reverse-geocoding data is committed separately and survives import rollback. Property: \`geopulse.import.transaction-timeout-minutes\`. | Positive integer minutes. | Backend restart |
| `GEOPULSE_IMPORT_UPLOAD_CLEANUP_MINUTES` | `15` | Chunked upload configuration (for bypassing Cloudflare's 100MB upload limit) Files >80MB are split into chunks on the frontend and reassembled on the backend Property: \`geopulse.import.upload-cleanup-minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_IMPORT_UPLOAD_TIMEOUT_HOURS` | `2` | Chunked upload configuration (for bypassing Cloudflare's 100MB upload limit) Files >80MB are split into chunks on the frontend and reassembled on the backend Property: \`geopulse.import.upload-timeout-hours\`. | Non-negative numeric value. | Backend restart |

### Export (8)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_EXPORT_BATCH_SIZE` | `1000` | Batch sizes for streaming exports Property: \`geopulse.export.batch-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_EXPORT_CONCURRENT_JOBS_LIMIT` | `3` | Export configuration Job management settings Property: \`geopulse.export.concurrent-jobs-limit\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_EXPORT_JOB_EXPIRY_HOURS` | `24` | Export configuration Job management settings Property: \`geopulse.export.job-expiry-hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_EXPORT_MAX_JOBS_PER_USER` | `5` | Export configuration Job management settings Property: \`geopulse.export.max-jobs-per-user\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_EXPORT_SCHEDULER_INTERVAL` | `2s` | Export configuration Job management settings Scheduler interval for processing export jobs (uses Quarkus time expression format) Note: This setting requires application restart... Property: \`geopulse.export.scheduler-interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_EXPORT_TEMP_DIR` | `/tmp/geopulse/exports` | Export temp file configuration (for large file handling) Export files are written to temp directory and streamed to clients to prevent OOM Property: \`geopulse.export.temp-directory\`. | Readable path/URI in container filesystem. | Backend restart |
| `GEOPULSE_EXPORT_TEMP_FILE_RETENTION_HOURS` | `24` | Export temp file configuration (for large file handling) Export files are written to temp directory and streamed to clients to prevent OOM Property: \`geopulse.export.temp-file-retention-hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_EXPORT_TRIP_POINT_LIMIT` | `10000` | Batch sizes for streaming exports Property: \`geopulse.export.trip-point-limit\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |

### Coverage (1)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_COVERAGE_PROCESSING_BATCH_SIZE` | `50000` | Number of GPS points processed per coverage calculation batch. Larger values are faster but may use more PostgreSQL temporary disk space. Property: \`geopulse.coverage.processing.batch-size\`. | Positive integer value. | Backend restart |

### Timeline and Trip Intelligence (94)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_BOAT_WATER_EVIDENCE_MAINTENANCE_INTERVAL` | `15m` | Interval for the Boat water-evidence maintenance job, which repairs missed or stale GPS water-evidence rows for Boat-enabled users. Property: \`geopulse.boat.water-evidence.maintenance.interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_BOAT_WATER_EVIDENCE_MAINTENANCE_MAX_USERS_PER_RUN` | `25` | Maximum number of Boat-enabled users repaired per water-evidence maintenance run. Property: \`geopulse.boat.water-evidence.maintenance.max-users-per-run\`. | Positive integer value. | Backend restart |
| `GEOPULSE_TIMELINE_BICYCLE_ENABLED` | `false` | Bicycle (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.bicycle.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_BICYCLE_MAX_AVG_SPEED` | `25.0` | Bicycle (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.bicycle.max_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BICYCLE_MAX_MAX_SPEED` | `35.0` | Bicycle (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.bicycle.max_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BICYCLE_MIN_AVG_SPEED` | `8.0` | Bicycle (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.bicycle.min_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BOAT_ENABLED` | `false` | Boat (optional - disabled by default). Uses water evidence; speed is only a sanity ceiling. Property: \`geopulse.timeline.travel.classification.boat.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_BOAT_MAX_PLAUSIBLE_SPEED` | `120.0` | Boat (optional - disabled by default). Maximum plausible speed sanity ceiling for boat classification. Property: \`geopulse.timeline.travel.classification.boat.max_plausible_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BOAT_MIN_CONTINUOUS_WATER_DISTANCE_METERS` | `1000.0` | Boat (optional - disabled by default). Minimum continuous water distance required for boat classification. Property: \`geopulse.timeline.travel.classification.boat.min_continuous_water_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BOAT_MIN_WATER_DISTANCE_METERS` | `2000.0` | Boat (optional - disabled by default). Minimum total water distance required for boat classification. Property: \`geopulse.timeline.travel.classification.boat.min_water_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_BOAT_MIN_WATER_RATIO` | `0.60` | Boat (optional - disabled by default). Minimum route water-evidence ratio required for boat classification. Property: \`geopulse.timeline.travel.classification.boat.min_water_ratio\`. | Decimal between 0 and 1. | Backend restart |
| `GEOPULSE_TIMELINE_CAR_ENABLED` | `true` | Enables the Car label for detected motor vehicle trips. Uses the shared motor vehicle speed thresholds. Property: \`geopulse.timeline.travel.classification.car.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_CAR_MIN_AVG_SPEED` | `10.0` | Shared motor vehicle minimum average speed threshold used for Car and Motorcycle classification. Property: \`geopulse.timeline.travel.classification.car.min_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_CAR_MIN_MAX_SPEED` | `15.0` | Shared motor vehicle minimum peak speed threshold used for Car and Motorcycle classification. Property: \`geopulse.timeline.travel.classification.car.min_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_MIN_DURATION_SECONDS` | `1800` | Data Gap Detection Configuration Property: \`geopulse.timeline.data_gap.min_duration_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_ADJACENT_TOLERANCE_SECONDS` | `120` | Manual Data Gap -> Stay override matching and merge tolerance (re-apply overrides after timeline rebuild/import) Property: \`geopulse.timeline.data_gap_stay_override.adjacent_tolerance_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_LOCATION_MATCH_MAX_DISTANCE_METERS` | `80.0` | Manual Data Gap -> Stay override location matching fallback distance Property: \`geopulse.timeline.data_gap_stay_override.location_match.max_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_MATCHING_MAX_BOUNDARY_DISTANCE_METERS` | `350.0` | Manual Data Gap -> Stay override matching (re-apply overrides after timeline rebuild/import) Property: \`geopulse.timeline.data_gap_stay_override.matching.max_boundary_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_MATCHING_MAX_DURATION_RATIO` | `1.8` | Manual Data Gap -> Stay override matching (re-apply overrides after timeline rebuild/import) Property: \`geopulse.timeline.data_gap_stay_override.matching.max_duration_ratio\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_MATCHING_MAX_TIMESTAMP_DELTA_SECONDS` | `2700` | Manual Data Gap -> Stay override matching (re-apply overrides after timeline rebuild/import) Property: \`geopulse.timeline.data_gap_stay_override.matching.max_timestamp_delta_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_STAY_OVERRIDE_MATCHING_MIN_DURATION_RATIO` | `0.6` | Manual Data Gap -> Stay override matching (re-apply overrides after timeline rebuild/import) Property: \`geopulse.timeline.data_gap_stay_override.matching.min_duration_ratio\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_DATA_GAP_THRESHOLD_SECONDS` | `10800` | Data Gap Detection Configuration Property: \`geopulse.timeline.data_gap.threshold_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_FLIGHT_ENABLED` | `false` | Flight (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.flight.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_FLIGHT_MIN_AVG_SPEED` | `400.0` | Flight (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.flight.min_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_FLIGHT_MIN_MAX_SPEED` | `500.0` | Flight (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.flight.min_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_ENABLED` | `false` | Gap Stay Inference Configuration When enabled, infers a stay instead of creating a data gap when points before/after gap are at same location Property: \`geopulse.timeline.gap_stay_inference.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_IN_TRIP_LOCAL_EXCURSION_MAX_DURATION_MINUTES` | `30` | Advanced gap stay inference heuristic for `IN_TRIP` local excursions Property: \`geopulse.timeline.gap_stay_inference.in_trip_local_excursion.max_duration_minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_IN_TRIP_LOCAL_EXCURSION_RADIUS_MULTIPLIER` | `2.0` | Advanced gap stay inference heuristic multiplier for allowed local `IN_TRIP` spread Property: \`geopulse.timeline.gap_stay_inference.in_trip_local_excursion.radius_multiplier\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_MAX_GAP_HOURS` | `24` | Gap Stay Inference Configuration When enabled, infers a stay instead of creating a data gap when points before/after gap are at same location Property: \`geopulse.timeline.gap_stay_inference.max_gap_hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_SPARSE_IN_TRIP_MAX_BOUNDARY_DISTANCE_METERS` | `800.0` | Advanced sparse `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.sparse_in_trip.max_boundary_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_SPARSE_IN_TRIP_MAX_IMPLIED_SPEED_KMH` | `1.0` | Advanced sparse `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.sparse_in_trip.max_implied_speed_kmh\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_SPARSE_IN_TRIP_MIN_BOUNDARY_DISTANCE_METERS` | `150.0` | Advanced sparse `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.sparse_in_trip.min_boundary_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_SPARSE_IN_TRIP_MIN_GAP_DURATION_FLOOR_HOURS` | `3` | Advanced sparse `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.sparse_in_trip.min_gap_duration_floor_hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_STATIONARY_BOUNDARY_IN_TRIP_ENABLED` | `false` | Advanced stationary-boundary `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.stationary_boundary_in_trip.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_STATIONARY_BOUNDARY_IN_TRIP_MAX_BOUNDARY_DISTANCE_METERS` | `100.0` | Advanced stationary-boundary `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.stationary_boundary_in_trip.max_boundary_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_STATIONARY_BOUNDARY_IN_TRIP_MAX_IMPLIED_SPEED_KMH` | `1.0` | Advanced stationary-boundary `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.stationary_boundary_in_trip.max_implied_speed_kmh\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_STAY_INFERENCE_STATIONARY_BOUNDARY_IN_TRIP_MIN_GAP_DURATION_FLOOR_HOURS` | `3` | Advanced stationary-boundary `IN_TRIP` gap stay inference heuristic Property: \`geopulse.timeline.gap_stay_inference.stationary_boundary_in_trip.min_gap_duration_floor_hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_TRIP_INFERENCE_ENABLED` | `false` | Gap Trip Inference Configuration When enabled, infers a trip instead of creating a data gap when distance between points exceeds threshold Useful for detecting flights, long-dis... Property: \`geopulse.timeline.gap_trip_inference.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_TRIP_INFERENCE_MAX_GAP_HOURS` | `24` | Gap Trip Inference Configuration When enabled, infers a trip instead of creating a data gap when distance between points exceeds threshold Useful for detecting flights, long-dis... Property: \`geopulse.timeline.gap_trip_inference.max_gap_hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_TRIP_INFERENCE_MIN_DISTANCE_METERS` | `100000` | Gap Trip Inference Configuration When enabled, infers a trip instead of creating a data gap when distance between points exceeds threshold Useful for detecting flights, long-dis... Property: \`geopulse.timeline.gap_trip_inference.min_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_GAP_TRIP_INFERENCE_MIN_GAP_HOURS` | `1` | Gap Trip Inference Configuration When enabled, infers a trip instead of creating a data gap when distance between points exceeds threshold Useful for detecting flights, long-dis... Property: \`geopulse.timeline.gap_trip_inference.min_gap_hours\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_JOB_DELAY` | `1m` | Real-time Timeline Processing Configuration Property: \`geopulse.timeline.job.delay\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_TIMELINE_JOB_INTERVAL` | `5m` | Real-time Timeline Processing Configuration Property: \`geopulse.timeline.job.interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_TIMELINE_MOTORCYCLE_ENABLED` | `false` | Enables the Motorcycle label for detected motor vehicle trips. Uses the shared motor vehicle speed thresholds. Property: \`geopulse.timeline.travel.classification.motorcycle.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_PATH_SIMPLIFICATION_ADAPTIVE` | `true` | GPS Path Simplification Configuration Property: \`geopulse.timeline.path.simplification.adaptive\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_PATH_SIMPLIFICATION_ENABLED` | `true` | GPS Path Simplification Configuration Property: \`geopulse.timeline.path.simplification.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_PATH_SIMPLIFICATION_MAX_POINTS` | `100` | GPS Path Simplification Configuration Property: \`geopulse.timeline.path.simplification.max_points\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_PATH_SIMPLIFICATION_TOLERANCE` | `15.0` | GPS Path Simplification Configuration Property: \`geopulse.timeline.path.simplification.tolerance\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_PROCESSING_THREADS` | `2` | Real-time Timeline Processing Configuration Property: \`geopulse.timeline.processing.thread-pool-size\`. | Integer value. | Backend restart |
| `GEOPULSE_TIMELINE_PREFERRED_MOTORIZED_TYPE` | `CAR` | Preferred label when both Car and Motorcycle labels are enabled for detected motor vehicle trips. Property: \`geopulse.timeline.travel.classification.preferred_motorized_type\`. | One of \`CAR\`, \`MOTORCYCLE\`. | Backend restart |
| `GEOPULSE_TIMELINE_REGENERATION_CAMPAIGN_DELAY` | `2m` | Timeline regeneration campaign worker Property: \`geopulse.timeline.regeneration-campaign.delay\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_TIMELINE_REGENERATION_CAMPAIGN_INTERVAL` | `5m` | Timeline regeneration campaign worker Property: \`geopulse.timeline.regeneration-campaign.interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_TIMELINE_REGENERATION_CAMPAIGN_MAX_ATTEMPTS` | `5` | Timeline regeneration campaign worker Property: \`geopulse.timeline.regeneration-campaign.max-attempts\`. | Integer value. | Backend restart |
| `GEOPULSE_TIMELINE_REGENERATION_CAMPAIGN_MAX_CONCURRENT_TASKS` | `2` | Timeline regeneration campaign worker Property: \`geopulse.timeline.regeneration-campaign.max-concurrent-tasks\`. | Integer value. | Backend restart |
| `GEOPULSE_TIMELINE_RUNNING_ENABLED` | `false` | Running (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.running.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_RUNNING_MAX_AVG_SPEED` | `14.0` | Running (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.running.max_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_RUNNING_MAX_MAX_SPEED` | `18.0` | Running (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.running.max_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_RUNNING_MIN_AVG_SPEED` | `7.0` | Running (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.running.min_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_SHORT_DISTANCE_KM` | `1.0` | Short trip distance threshold used by travel classification heuristics. Property: \`geopulse.timeline.travel.classification.short_distance_km\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_ACCURACY_THRESHOLD` | `60.0` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.accuracy.threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_MERGE_ENABLED` | `true` | Merge staypoints Property: \`geopulse.timeline.staypoint.merge.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_MERGE_MAX_DISTANCE_METERS` | `400` | Merge staypoints Property: \`geopulse.timeline.staypoint.merge.max_distance_meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_MERGE_MAX_TIME_GAP_MINUTES` | `15` | Merge staypoints Property: \`geopulse.timeline.staypoint.merge.max_time_gap_minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_MIN_ACCURACY_RATIO` | `0.5` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.min_accuracy_ratio\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_MIN_DURATION_MINUTES` | `7` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.min_duration_minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_RADIUS_METERS` | `50` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.radius_meters\`. | Integer value. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_USE_VELOCITY_ACCURACY` | `true` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.use_velocity_accuracy\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_STAYPOINT_VELOCITY_THRESHOLD` | `2.5` | Default timeline configs. They can be overwritten by each user individually or via ENV variables Property: \`geopulse.timeline.staypoint.velocity.threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_ENABLED` | `false` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_MAX_AVG_SPEED` | `150.0` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.max_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_MAX_MAX_SPEED` | `180.0` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.max_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_MAX_SPEED_VARIANCE` | `15.0` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.max_speed_variance\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_MIN_AVG_SPEED` | `30.0` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.min_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRAIN_MIN_MAX_SPEED` | `80.0` | Train (optional - disabled by default) Property: \`geopulse.timeline.travel.classification.train.min_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_ARRIVAL_MIN_DURATION_SECONDS` | `90` | Travel Classification Configuration Property: \`geopulse.timeline.trip.arrival.min_duration_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_ARRIVAL_MIN_POINTS` | `3` | Travel Classification Configuration Property: \`geopulse.timeline.trip.arrival.min_points\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_DETECTION_ALGORITHM` | `single` | Trip Property: \`geopulse.timeline.trip.detection.algorithm\`. | String value. Follow subsystem documentation. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MAX_DISTANCE_RATIO` | `1.8` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.max_distance_ratio\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MAX_DURATION_RATIO` | `1.8` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.max_duration_ratio\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MAX_POINT_DISTANCE_METERS` | `350.0` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.max_point_distance_meters\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MAX_TIMESTAMP_DELTA_SECONDS` | `2700` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.max_timestamp_delta_seconds\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MIN_DISTANCE_RATIO` | `0.6` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.min_distance_ratio\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_MOVEMENT_OVERRIDE_MIN_DURATION_RATIO` | `0.6` | Manual trip movement override matching (re-apply overrides after timeline rebuild) Property: \`geopulse.timeline.trip.movement_override.matching.min_duration_ratio\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TIMELINE_TRIP_SUSTAINED_STOP_MIN_DURATION_SECONDS` | `60` | Travel Classification Configuration Property: \`geopulse.timeline.trip.sustained_stop.min_duration_seconds\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_TIMELINE_VIEW_ITEM_LIMIT` | `150` | Timeline View Item Limit - maximum number of items to load on Timeline page For larger datasets, users will be guided to use Timeline Reports Property: \`geopulse.timeline.view.item-limit\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_WALKING_MAX_AVG_SPEED` | `6.0` | Walking (mandatory) Property: \`geopulse.timeline.travel.classification.walking.max_avg_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TIMELINE_WALKING_MAX_MAX_SPEED` | `8.0` | Walking (mandatory) Property: \`geopulse.timeline.travel.classification.walking.max_max_speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_SUMMARY_PLACES_PAGE` | `1` | Trip summary aggregation Property: \`geopulse.trip.summary.places.page\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_SUMMARY_PLACES_PAGE_SIZE` | `10000` | Trip summary aggregation Property: \`geopulse.trip.summary.places.page-size\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_VISIT_MATCHING_AUTO_APPLY_ON_REGENERATION` | `true` | Trip visit auto-matching Property: \`geopulse.trip.visit-matching.auto-apply-on-timeline-regeneration\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_TRIP_VISIT_MATCHING_AUTO_THRESHOLD` | `0.85` | Trip visit auto-matching Property: \`geopulse.trip.visit-matching.auto-threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_VISIT_MATCHING_EXACT_NAME_BOOST_DISTANCE_METERS` | `120` | Trip visit auto-matching Property: \`geopulse.trip.visit-matching.exact-name-boost-distance-meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_VISIT_MATCHING_MAX_DISTANCE_METERS` | `400` | Trip visit auto-matching Property: \`geopulse.trip.visit-matching.max-distance-meters\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_TRIP_VISIT_MATCHING_SUGGEST_THRESHOLD` | `0.55` | Trip visit auto-matching Property: \`geopulse.trip.visit-matching.suggest-threshold\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |

### Boat Water Dataset (8)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_WATER_DATASET_AUTO_IMPORT` | `true` | Boat water dataset setup. Automatically imports the configured water-surface dataset when boat evidence is needed. Property: \`geopulse.water-dataset.auto-import\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_WATER_DATASET_CONNECT_TIMEOUT_SECONDS` | `30` | Boat water dataset setup. HTTP connect timeout for dataset download. Property: \`geopulse.water-dataset.connect-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WATER_DATASET_DOWNLOAD_STALL_TIMEOUT_SECONDS` | `120` | Boat water dataset setup. Maximum allowed download stall before aborting. Property: \`geopulse.water-dataset.download-stall-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WATER_DATASET_DOWNLOAD_TIMEOUT_HOURS` | `6` | Boat water dataset setup. Overall timeout for dataset download. Property: \`geopulse.water-dataset.download-timeout-hours\`. | Positive integer hours. | Backend restart |
| `GEOPULSE_WATER_DATASET_LOCAL_PATH` | `(empty)` | Boat water dataset setup. Optional path to a pre-mounted dataset copy archive inside the backend container. Property: \`geopulse.water-dataset.local-path\`. | Optional readable path in backend container filesystem. | Backend restart |
| `GEOPULSE_WATER_DATASET_SETUP_START_TIMEOUT_MINUTES` | `5` | Boat water dataset setup. Timeout while waiting for setup/import startup. Property: \`geopulse.water-dataset.setup-start-timeout-minutes\`. | Positive integer minutes. | Backend restart |
| `GEOPULSE_WATER_DATASET_SHA256` | `(empty)` | Boat water dataset setup. Optional SHA-256 checksum used to verify the dataset archive. Property: \`geopulse.water-dataset.sha256\`. | Hex SHA-256 checksum or empty to skip verification. | Backend restart |
| `GEOPULSE_WATER_DATASET_URL` | `https://github.com/tess1o/GeoPulse/releases/download/water-surfaces-v1/geopulse-water-surfaces-v1.copy.gz` | Boat water dataset setup. Remote dataset archive URL; the dataset is intentionally not bundled into Flyway or the backend image. Property: \`geopulse.water-dataset.url\`. | Valid URL, or configure `GEOPULSE_WATER_DATASET_LOCAL_PATH`. | Backend restart |

### GPS Filtering and Deduplication (7)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_GPS_DUPLICATE_DETECTION_ENABLED` | `false` | Per-source duplicate detection defaults (for new sources) Property: \`geopulse.gps.duplicate-detection.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GPS_DUPLICATE_DETECTION_LOCATION_TIME_THRESHOLD_MINUTES` | `2` | GPS Point Duplicate Detection Configuration DEPRECATED: This setting is used for fallback when per-source threshold is NULL For new sources, use the per-source settings below in... Property: \`geopulse.gps.duplicate-detection.location-time-threshold-minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GPS_DUPLICATE_DETECTION_THRESHOLD_MINUTES` | `2` | Per-source duplicate detection defaults (for new sources) Property: \`geopulse.gps.duplicate-detection.threshold-minutes\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_GPS_FILTER_NEGATIVE_ACCURACY_ENABLED` | `true` | Reject GPS points with negative accuracy values before applying per-source filtering rules Property: \`geopulse.gps.filter.negative-accuracy.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GPS_FILTER_INACCURATE_DATA_ENABLED` | `false` | GPS Filtering Configuration (per-source defaults) These values are used as defaults when creating new GPS sources Property: \`geopulse.gps.filter.inaccurate-data.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_GPS_MAX_ALLOWED_ACCURACY` | `100` | GPS Filtering Configuration (per-source defaults) These values are used as defaults when creating new GPS sources Property: \`geopulse.gps.max-allowed-accuracy\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_GPS_MAX_ALLOWED_SPEED` | `250` | GPS Filtering Configuration (per-source defaults) These values are used as defaults when creating new GPS sources Property: \`geopulse.gps.max-allowed-speed\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |

### Weather (35)

Weather feature/provider/sampling/quota settings can also be managed from **Admin Dashboard > System Settings > Weather**. Saved Admin Settings values are stored in the database and take precedence over these environment defaults. By default, Weather is enabled for ongoing/current timeline activity, while historical backfill is opt-in. Scheduler cadence, target cleanup, and HTTP timeout variables are backend runtime properties and require a backend restart when changed.

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_WEATHER_ENABLED` | `true` | Master switch for weather samples on timeline stays, trips, maps, and insights. Property: \`geopulse.weather.enabled\`. Default enables ongoing/current weather collection. | \`true\` or \`false\`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_PRIMARY_PROVIDER` | `OPEN_METEO` | Primary weather provider. Property: \`geopulse.weather.primary-provider\`. | `OPEN_METEO` or `PIRATE_WEATHER`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_SECONDARY_PROVIDER` | `(empty)` | Optional fallback weather provider. Property: \`geopulse.weather.secondary-provider\`. | Empty, `OPEN_METEO`, or `PIRATE_WEATHER`; must differ from primary. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_OPEN_METEO_ENABLED` | `true` | Enable Open-Meteo as a selectable weather provider. Property: \`geopulse.weather.open-meteo.enabled\`. | `true` or `false`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_OPEN_METEO_FORECAST_URL` | `https://api.open-meteo.com` | Open-Meteo forecast/current API base URL. Property: \`geopulse.weather.open-meteo.forecast-url\`. | Valid URL reachable from the backend. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_OPEN_METEO_ARCHIVE_URL` | `https://archive-api.open-meteo.com` | Open-Meteo historical archive API base URL. Property: \`geopulse.weather.open-meteo.archive-url\`. | Valid URL reachable from the backend. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_OPEN_METEO_API_KEY` | `(empty)` | Optional Open-Meteo API key. Property: \`geopulse.weather.open-meteo.api-key\`. | Sensitive secret. Store in secret manager or Admin Settings; do not commit to VCS. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_OPEN_METEO_CONNECT_TIMEOUT_SECONDS` | `5` | HTTP connect timeout for Open-Meteo requests. Property: \`geopulse.weather.open-meteo.connect-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WEATHER_OPEN_METEO_READ_TIMEOUT_SECONDS` | `15` | HTTP read timeout for Open-Meteo requests. Property: \`geopulse.weather.open-meteo.read-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WEATHER_PIRATE_ENABLED` | `false` | Enable Pirate Weather as a selectable weather provider. Property: \`geopulse.weather.pirate.enabled\`. | `true` or `false`; requires API key when enabled. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_PIRATE_BASE_URL` | `https://api.pirateweather.net` | Pirate Weather forecast API base URL. Property: \`geopulse.weather.pirate.base-url\`. | Valid URL reachable from the backend. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_PIRATE_TIME_MACHINE_URL` | `https://timemachine.pirateweather.net` | Pirate Weather historical time machine API base URL. Property: \`geopulse.weather.pirate.time-machine-url\`. | Valid URL reachable from the backend. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_PIRATE_API_KEY` | `(empty)` | Pirate Weather API key. Property: \`geopulse.weather.pirate.api-key\`. | Sensitive secret. Store in secret manager or Admin Settings; do not commit to VCS. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_PIRATE_CONNECT_TIMEOUT_SECONDS` | `5` | HTTP connect timeout for Pirate Weather requests. Property: \`geopulse.weather.pirate.connect-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WEATHER_PIRATE_READ_TIMEOUT_SECONDS` | `15` | HTTP read timeout for Pirate Weather requests. Property: \`geopulse.weather.pirate.read-timeout-seconds\`. | Positive integer seconds. | Backend restart |
| `GEOPULSE_WEATHER_ONGOING_ENABLED` | `true` | Create weather targets for each active user's latest stay/trip. Property: \`geopulse.weather.ongoing.enabled\`. | \`true\` or \`false\`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_ONGOING_INTERVAL_MINUTES` | `60` | Minimum interval between ongoing weather samples. Property: \`geopulse.weather.ongoing.interval-minutes\`. | Integer >= 30. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_BACKFILL_ENABLED` | `false` | Discover historical weather targets from existing timeline stays/trips. Disabled by default so past timeline locations are not sent for weather enrichment unless an admin opts in. Property: \`geopulse.weather.backfill.enabled\`. | \`true\` or \`false\`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_QUOTA_DAILY_REQUEST_LIMIT` | `10000` | GeoPulse-side maximum weather target attempts per UTC day. Property: \`geopulse.weather.quota.daily-request-limit\`. | Integer >= 0. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_QUOTA_ONGOING_RESERVE` | `500` | Daily request reserve kept available for ongoing samples before backfill work can consume quota. Property: \`geopulse.weather.quota.ongoing-reserve\`. | Integer >= 0. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_COORDINATE_PRECISION` | `2` | Decimal precision for weather coordinate buckets; lower values reuse more samples, higher values fetch more precise locations. Property: \`geopulse.weather.coordinate-precision\`. | Integer from 0 to 5. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_FAILED_TARGET_RETRY_ENABLED` | `true` | Retry stale failed weather targets after cooldown. Property: \`geopulse.weather.failed-target-retry.enabled\`. | \`true\` or \`false\`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_FAILED_TARGET_RETRY_COOLDOWN_HOURS` | `24` | Cooldown before failed weather targets become retryable. Property: \`geopulse.weather.failed-target-retry.cooldown-hours\`. | Positive integer hours. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_WEATHER_TARGET_CLEANUP_JOB_CRON` | `0 30 3 * * ?` | Cron schedule for cleaning completed/skipped/failed weather target queue records. Property: \`geopulse.weather.target-cleanup.job.cron\`. | Valid Quarkus cron expression. | Backend restart |
| `GEOPULSE_WEATHER_TARGETS_COMPLETED_RETENTION_DAYS` | `7` | Retention for completed and skipped weather target queue records. Stored weather samples are not removed by this cleanup. Property: \`geopulse.weather.targets.completed-retention-days\`. | Positive integer days. | Backend restart |
| `GEOPULSE_WEATHER_TARGETS_FAILED_RETENTION_DAYS` | `30` | Retention for failed weather target queue records. Property: \`geopulse.weather.targets.failed-retention-days\`. | Positive integer days. | Backend restart |
| `GEOPULSE_WEATHER_TARGETS_IN_PROGRESS_TIMEOUT_MINUTES` | `60` | Age after which locked in-progress weather targets are recovered for retry. Property: \`geopulse.weather.targets.in-progress-timeout-minutes\`. | Positive integer minutes. | Backend restart |

### Map Matching (18)

Map matching can use a self-hosted Valhalla instance to display road/path-snapped trip geometry. Saved values from **Admin Dashboard > System Settings > Map Matching** override these environment defaults for the managed settings. User profile opt-in is available only when map matching is globally enabled and Valhalla is configured.

| Variable | Default        | Comment | Restrictions | Restart |
|---|----------------|---|---|---|
| `GEOPULSE_TIMELINE_MAP_MATCHING_ENABLED` | `false`        | Global master switch for map matching. Property: \`geopulse.timeline.map-matching.enabled\`. | `true` or `false`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_PROVIDER` | `valhalla`     | Map matching provider. Property: \`geopulse.timeline.map-matching.provider\`. | `valhalla`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_VALHALLA_BASE_URL` | `(empty)`      | Valhalla API base URL. Property: \`geopulse.timeline.map-matching.valhalla.base-url\`. Required before users can enable map matching. | Valid URL reachable from the backend, or empty. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_AUTOMATIC_ENABLED` | `false`        | Automatically map-match stable new trips for all users. Property: \`geopulse.timeline.map-matching.automatic.enabled\`. | `true` or `false`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_BACKFILL_ENABLED` | `false`        | Discover and map-match historical trips for all users in the background. Property: \`geopulse.timeline.map-matching.backfill.enabled\`. | `true` or `false`. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_AUTOMATIC_QUIET_PERIOD_MINUTES` | `15`           | Minutes a changed timeline must remain quiet before automatic matching starts. Property: \`geopulse.timeline.map-matching.automatic.quiet-period-minutes\`. | Positive integer minutes. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_CONNECT_TIMEOUT_SECONDS` | `3`            | HTTP connect timeout for Valhalla requests. Property: \`geopulse.timeline.map-matching.connect-timeout-seconds\`. | Positive integer seconds. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_READ_TIMEOUT_SECONDS` | `20`           | HTTP read timeout for Valhalla responses. Property: \`geopulse.timeline.map-matching.read-timeout-seconds\`. | Positive integer seconds. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_MAX_INPUT_POINTS` | `100`          | Maximum GPS points sent in each contiguous Valhalla trace chunk. Property: \`geopulse.timeline.map-matching.max-input-points\`. | Positive integer. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_MAX_TRIP_DURATION_HOURS` | `24`           | Maximum trip duration eligible for map matching. Property: \`geopulse.timeline.map-matching.max-trip-duration-hours\`. | Positive integer hours. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_WORKER_BATCH_SIZE` | `5`            | Map matching targets processed per worker run. Property: \`geopulse.timeline.map-matching.worker.batch-size\`. | Positive integer. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_WORKER_INTERVAL` | `60s`           | Scheduler cadence for map matching worker ticks. Property: \`geopulse.timeline.map-matching.worker.interval\`. | Quarkus duration expression (for example `15s`, `5m`, `1h`). | Backend restart |
| `GEOPULSE_TIMELINE_MAP_MATCHING_MAX_ATTEMPTS` | `3`            | Maximum attempts per map matching target. Property: \`geopulse.timeline.map-matching.max-attempts\`. | Positive integer. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_QUALITY_MIN_RAW_DISTANCE_METERS` | `500`          | Minimum raw chunk distance before matched-route quality checks apply. Property: \`geopulse.timeline.map-matching.quality.min-raw-distance-meters\`. | Positive integer meters. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_QUALITY_MIN_DISTANCE_COVERAGE_PERCENT` | `35`           | Minimum matched distance as a percent of raw chunk distance. Property: \`geopulse.timeline.map-matching.quality.min-distance-coverage-percent\`. | Integer from 1 to 100. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_QUALITY_MAX_DISCONTINUITY_PERCENT` | `10`           | Maximum unmatched gap distance between matched fragments as a percent of raw chunk distance. Property: \`geopulse.timeline.map-matching.quality.max-discontinuity-percent\`. | Integer from 1 to 100. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_QUALITY_MAX_SHORT_DISCONTINUITY_METERS` | `100`          | Minimum absolute unmatched gap allowance between matched fragments. Property: \`geopulse.timeline.map-matching.quality.max-short-discontinuity-meters\`. | Positive integer meters. | Backend restart for env changes; Admin UI changes apply through system settings |
| `GEOPULSE_TIMELINE_MAP_MATCHING_CACHE_CLEANUP_CRON` | `0 45 3 * * ?` | Cron schedule for cleaning detached map matching cache rows. Property: \`geopulse.timeline.map-matching.cache.cleanup.cron\`. | Valid Quarkus cron expression. | Backend restart |

### MQTT (14)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_MQTT_BROKER_HOST` | `geopulse-mosquitto` | MQTT Configuration (optional - only active when GEOPULSE_MQTT_ENABLED true) Property: \`geopulse.mqtt.broker.host\`. | String value. Follow subsystem documentation. | Backend restart |
| `GEOPULSE_MQTT_BROKER_PORT` | `1883` | MQTT Configuration (optional - only active when GEOPULSE_MQTT_ENABLED true) Property: \`geopulse.mqtt.broker.port\`. | Integer in range \`1-65535\`. | Backend restart |
| `GEOPULSE_MQTT_ENABLED` | `false` | MQTT Configuration (optional - only active when GEOPULSE_MQTT_ENABLED true) Property: \`geopulse.mqtt.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_MQTT_PASSWORD` | `geopulse_mqtt_pass_123` | MQTT Configuration (optional - only active when GEOPULSE_MQTT_ENABLED true) Property: \`geopulse.mqtt.password\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_MQTT_TLS_ENABLED` | `false` | Enables TLS for external MQTT broker connections. Property: \`geopulse.mqtt.tls.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_MQTT_TLS_PROTOCOL` | `TLSv1.2` | TLS protocol used by MQTT client when TLS is enabled. Property: \`geopulse.mqtt.tls.protocol\`. | Supported JVM TLS protocol (for example \`TLSv1.2\`, \`TLSv1.3\`). | Backend restart |
| `GEOPULSE_MQTT_TLS_TRUSTSTORE_PATH` | `(empty)` | Optional truststore path for private/self-signed broker cert validation. Property: \`geopulse.mqtt.tls.truststore.path\`. | Readable path in backend container filesystem. | Backend restart |
| `GEOPULSE_MQTT_TLS_TRUSTSTORE_PASSWORD` | `(empty)` | Truststore password for MQTT TLS. Property: \`geopulse.mqtt.tls.truststore.password\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_MQTT_TLS_TRUSTSTORE_TYPE` | `PKCS12` | Truststore format used by MQTT TLS. Property: \`geopulse.mqtt.tls.truststore.type\`. | Typically \`PKCS12\` or \`JKS\`. | Backend restart |
| `GEOPULSE_MQTT_TLS_KEYSTORE_PATH` | `(empty)` | Optional client certificate keystore path (required for mTLS brokers). Property: \`geopulse.mqtt.tls.keystore.path\`. | Readable path in backend container filesystem. | Backend restart |
| `GEOPULSE_MQTT_TLS_KEYSTORE_PASSWORD` | `(empty)` | Keystore password for MQTT TLS client certificate. Property: \`geopulse.mqtt.tls.keystore.password\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_MQTT_TLS_KEYSTORE_TYPE` | `PKCS12` | Keystore format for MQTT TLS client cert keypair. Property: \`geopulse.mqtt.tls.keystore.type\`. | Typically \`PKCS12\` or \`JKS\`. | Backend restart |
| `GEOPULSE_MQTT_TLS_INSECURE_SKIP_HOSTNAME_VERIFICATION` | `false` | Disables TLS hostname verification for MQTT (debugging only). Property: \`geopulse.mqtt.tls.insecure-skip-hostname-verification\`. | \`true\` or \`false\`; keep \`false\` for production. | Backend restart |
| `GEOPULSE_MQTT_USERNAME` | `geopulse_mqtt_admin` | MQTT Configuration (optional - only active when GEOPULSE_MQTT_ENABLED true) Property: \`geopulse.mqtt.username\`. | String value. Follow subsystem documentation. | Backend restart |

### Notifications / Apprise (8)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_NOTIFICATIONS_APPRISE_ENABLED` | `false` | Enables/disables external Apprise delivery for geofence alerts. Property: \`geopulse.notifications.apprise.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_NOTIFICATIONS_APPRISE_API_URL` | `(empty)` | Base URL for Apprise API service. Property: \`geopulse.notifications.apprise.api-url\`. | Valid URL (for example `http://apprise-api:8000`). | Backend restart |
| `GEOPULSE_NOTIFICATIONS_APPRISE_AUTH_TOKEN` | `(empty)` | Optional API token for Apprise. Property: \`geopulse.notifications.apprise.auth-token\`. | Sensitive secret. Store in secret manager; do not commit to VCS. | Backend restart |
| `GEOPULSE_NOTIFICATIONS_APPRISE_TIMEOUT_MS` | `5000` | HTTP timeout for Apprise requests in milliseconds. Property: \`geopulse.notifications.apprise.timeout-ms\`. | Non-negative numeric value. | Backend restart |
| `GEOPULSE_NOTIFICATIONS_APPRISE_VERIFY_TLS` | `true` | Whether TLS certificate verification is enabled for Apprise calls. Property: \`geopulse.notifications.apprise.verify-tls\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_NOTIFICATIONS_GEOFENCE_EVENTS_CLEANUP_ENABLED` | `true` | Enables/disables scheduled cleanup of geofence notification events. Property: \`geopulse.notifications.geofence-events.cleanup.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_NOTIFICATIONS_GEOFENCE_EVENTS_CLEANUP_SCHEDULER_CADENCE` | `12h` | Scheduler cadence for cleanup job ticks. Property: \`geopulse.notifications.geofence-events.cleanup.scheduler-cadence\`. | Quarkus duration expression (for example \`30m\`, \`12h\`, \`1d\`). | Backend restart |
| `GEOPULSE_NOTIFICATIONS_GEOFENCE_EVENTS_RETENTION_DAYS` | `90` | Deletes geofence events older than this many days. Property: \`geopulse.notifications.geofence-events.retention-days\`. | Integer >= 1. | Backend restart |

### Prometheus (9)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_PROMETHEUS_ENABLED` | `false` | Custom Prometheus Metrics Configuration Note: The Prometheus endpoint is always available at build time. Use GEOPULSE_PROMETHEUS_ENABLED to control custom metrics collection at... Property: \`geopulse.prometheus.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_FAVORITES_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.favorites.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_GEOCODING_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.geocoding.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_GPS_POINTS_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.gps-points.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_MEMORY_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.memory.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_REFRESH_INTERVAL` | `10m` | Custom Prometheus Metrics Configuration Note: The Prometheus endpoint is always available at build time. Use GEOPULSE_PROMETHEUS_ENABLED to control custom metrics collection at... Property: \`geopulse.prometheus.refresh-interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_PROMETHEUS_TIMELINE_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.timeline.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_USER_METRICS_ENABLED` | `true` | Per-metric-class control (optional - all enabled by default) Property: \`geopulse.prometheus.user-metrics.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_PROMETHEUS_WORKLOAD_ENABLED` | `true` | Enables/disables workload timers and counters for GPS ingestion, timeline regeneration, coverage, boat/water evidence, and weather jobs. Property: \`geopulse.prometheus.workload.enabled\`. | \`true\` or \`false\`. | Backend restart |

### Sharing and OwnTracks (2)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_OWNTRACKS_PING_TIMESTAMP_OVERRIDE` | `false` | Property: \`geopulse.owntracks.ping.timestamp.override\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_SHARE_BASE_URL` | `(empty)` | Sharing Property: \`geopulse.share.base-url\`. | Valid URL. | Backend restart |

### Warmup and Background Jobs (5)

| Variable | Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_BADGES_CALCULATION_DELAY` | `5m` | Badge Calculation Scheduler Configuration Interval for running badge calculations (uses Quarkus time expression format: 1s, 5m, 1h, etc.) Note: This setting requires application... Property: \`geopulse.badges.calculation.delay\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_BADGES_CALCULATION_INTERVAL` | `30m` | Badge Calculation Scheduler Configuration Interval for running badge calculations (uses Quarkus time expression format: 1s, 5m, 1h, etc.) Note: This setting requires application... Property: \`geopulse.badges.calculation.interval\`. | Duration format (for example \`1s\`, \`5m\`, \`1h\`). | Backend restart |
| `GEOPULSE_WARMUP_ENABLED` | `true` | Warmup Configuration Enable aggressive warmup on startup (loads real data, then forces GC) Trade-off: Longer startup time (5-8s) for safe first-request handling in 512MB containers Property: \`geopulse.warmup.enabled\`. | \`true\` or \`false\`. | Backend restart |
| `GEOPULSE_WARMUP_MAX_ITEMS` | `5000` | Maximum number of items to convert to DTOs during warmup (default: 5000) Prevents memory spikes from users with excessive data (100K+ timeline items) Queries still execute fully... Property: \`geopulse.warmup.max-items\`. | Numeric value; keep positive unless documented otherwise. | Backend restart |
| `GEOPULSE_WARMUP_SAMPLE_DAYS` | `30` | Number of days of timeline data to load during warmup (default: 30) Higher more thorough warmup, but longer startup Lower faster startup, but less comprehensive warmup Set to 0... Property: \`geopulse.warmup.sample-days\`. | Non-negative numeric value. | Backend restart |

## Deployment Vars

These vars are primarily for deployment wiring and image/runtime composition.

### Core Deployment and Service Wiring

| Variable | Typical Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_VERSION` | `1.22.0` | Version tag used for backend/frontend image selection in Compose manifests. | Must match published image tags. | Redeploy backend/postgres services |
| `GEOPULSE_POSTGRES_HOST` | `geopulse-postgres` | Postgres hostname used to build JDBC URL and service wiring. | Resolvable host inside deployment network. | Redeploy backend/postgres services |
| `GEOPULSE_POSTGRES_PORT` | `5432` | Postgres TCP port used for service wiring. | Integer in range \`1-65535\`. | Redeploy backend/postgres services |
| `GEOPULSE_POSTGRES_DB` | `geopulse` | Postgres database name used by app and database container initialization. | Valid PostgreSQL database identifier. | Redeploy backend/postgres services |
| `GEOPULSE_POSTGRES_USERNAME` | `geopulse-user` | Database username used by backend and Postgres initialization. | Non-empty PostgreSQL role name. | Redeploy backend/postgres services |
| `GEOPULSE_POSTGRES_PASSWORD` | `change-this-secure-password` | Database password used by backend and Postgres initialization. | Use strong secret; do not commit real value to VCS. | Redeploy backend/postgres services |

### Predefined OIDC Provider Deployment Vars

| Variable | Typical Default | Comment | Restrictions | Restart |
|---|---|---|---|---|
| `GEOPULSE_OIDC_PROVIDER_GOOGLE_ENABLED` | `false` | Enable predefined Google provider in deployment templates. | \`true\` or \`false\`. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GOOGLE_CLIENT_ID` | `(empty)` | Google OIDC client ID. | Required when Google provider is enabled. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GOOGLE_CLIENT_SECRET` | `(empty)` | Google OIDC client secret. | Sensitive secret. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GOOGLE_DISCOVERY_URL` | `https://accounts.google.com/.well-known/openid-configuration` | Google OIDC discovery URL. | Must be valid discovery endpoint URL. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GENERIC_ENABLED` | `false` | Enable predefined generic OIDC provider. | \`true\` or \`false\`. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GENERIC_NAME` | `Custom OIDC` | Display name for generic provider. | Non-empty label when enabled. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GENERIC_CLIENT_ID` | `(empty)` | Generic OIDC client ID. | Required when generic provider is enabled. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GENERIC_CLIENT_SECRET` | `(empty)` | Generic OIDC client secret. | Sensitive secret. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_GENERIC_DISCOVERY_URL` | `(empty)` | Generic OIDC discovery URL. | Valid \`https://.../.well-known/openid-configuration\` URL. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_MICROSOFT_ENABLED` | `(from Helm values)` | Enable predefined Microsoft provider in Helm templates. | \`true\` or \`false\`. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_MICROSOFT_CLIENT_ID` | `(from Helm values)` | Microsoft OIDC client ID. | Required when Microsoft provider is enabled. | Backend redeploy |
| `GEOPULSE_OIDC_PROVIDER_MICROSOFT_CLIENT_SECRET` | `(from Helm values)` | Microsoft OIDC client secret. | Sensitive secret. | Backend redeploy |

## PostgreSQL Tuning Vars

These variables are passed to the PostgreSQL container as startup parameters.

| Variable | Standard Default | Dev Default | Comment | Restrictions | Restart |
|---|---|---|---|---|---|
| `GEOPULSE_POSTGRES_SHARED_BUFFERS` | `256MB` | `128MB` | Main PostgreSQL buffer cache size. | PostgreSQL memory unit (for example \`128MB\`, \`1GB\`). | Postgres container restart |
| `GEOPULSE_POSTGRES_WORK_MEM` | `8MB` | `6MB` | Memory per sort/hash operation per query node. | PostgreSQL memory unit. | Postgres container restart |
| `GEOPULSE_POSTGRES_MAINTENANCE_WORK_MEM` | `64MB` | `32MB` | Memory for maintenance operations (vacuum/index build). | PostgreSQL memory unit. | Postgres container restart |
| `GEOPULSE_POSTGRES_EFFECTIVE_CACHE_SIZE` | `1GB` | `512MB` | Planner estimate for available OS cache. | PostgreSQL memory unit. | Postgres container restart |
| `GEOPULSE_POSTGRES_MAX_WAL_SIZE` | `512MB` | `256MB` | Maximum WAL size before checkpoints are forced. | PostgreSQL memory unit. | Postgres container restart |
| `GEOPULSE_POSTGRES_CHECKPOINT_TARGET` | `0.9` | `0.9` | Target fraction of checkpoint interval used for checkpoint completion. | Decimal \`0.0\` to \`1.0\`. | Postgres container restart |
| `GEOPULSE_POSTGRES_WAL_BUFFERS` | `16MB` | `8MB` | Memory used for WAL buffering. | PostgreSQL memory unit. | Postgres container restart |
| `GEOPULSE_POSTGRES_RANDOM_PAGE_COST` | `1.1` | `1.1` | Planner random I/O cost estimate (often lower on SSD). | Positive decimal. | Postgres container restart |
| `GEOPULSE_POSTGRES_IO_CONCURRENCY` | `100` | `50` | Expected concurrent disk I/O operations. | Positive integer. | Postgres container restart |
| `GEOPULSE_POSTGRES_AUTOVACUUM_NAPTIME` | `60s` | `60s` | Delay between autovacuum runs. | PostgreSQL duration value. | Postgres container restart |
| `GEOPULSE_POSTGRES_VACUUM_SCALE_FACTOR` | `0.2` | `0.2` | Tuple-update fraction threshold before autovacuum. | Decimal greater than or equal to \`0\`. | Postgres container restart |
| `GEOPULSE_POSTGRES_LOG_SLOW_QUERIES` | `5000` | `1000` | Minimum duration (ms) before query is logged as slow. | Integer milliseconds. | Postgres container restart |
| `GEOPULSE_POSTGRES_LOG_STATEMENT` | `(not set)` | `ddl` | Statement logging level for dev compose profile. | One of PostgreSQL accepted values (\`none\`, \`ddl\`, \`mod\`, \`all\`). | Postgres container restart |
| `GEOPULSE_POSTGRES_LOG_CHECKPOINTS` | `(not set)` | `on` | Checkpoint logging flag for dev profile. | \`on\` or \`off\`. | Postgres container restart |
| `GEOPULSE_POSTGRES_LOG_AUTOVACUUM` | `(not set)` | `0` | Autovacuum logging threshold in milliseconds for dev profile. | Integer milliseconds (or \`-1\` to disable). | Postgres container restart |
| `GEOPULSE_POSTGRES_TRACK_FUNCTIONS` | `(not set)` | `all` | Function stats collection level for dev profile. | One of \`none\`, \`pl\`, \`all\`. | Postgres container restart |

## Pattern-Based Vars

### Dynamic OIDC Provider Pattern

Pattern: `GEOPULSE_OIDC_PROVIDER_{PROVIDER}_{PROPERTY}`

| Pattern Part | Comment | Restrictions |
|---|---|---|
| `{PROVIDER}` | Provider identifier (for example `GOOGLE`, `COMPANY`, `KEYCLOAK`). | Uppercase letters, numbers, underscore recommended. Converted to lowercase internally. |
| `{PROPERTY}=ENABLED` | Provider activation flag. | Must be `true` to activate provider. |
| `{PROPERTY}=CLIENT_ID` | OIDC client ID. | Required when provider is enabled. |
| `{PROPERTY}=CLIENT_SECRET` | OIDC client secret. | Required when provider is enabled; treat as secret. |
| `{PROPERTY}=DISCOVERY_URL` | OIDC discovery endpoint. | Required when provider is enabled; valid discovery URL expected. |
| `{PROPERTY}=NAME` | Login button display name. | Optional string. |
| `{PROPERTY}=ICON` | Icon class or URL for provider branding. | Optional string/URL. |
| `{PROPERTY}=SCOPES` | OAuth scopes requested during auth. | Optional, defaults to `openid profile email`. |

## Related Docs

- [Docker Deployment](./docker-compose.md)
- [Helm Chart Deployment & Configuration](./helm-deployment.md)
- [Frontend Nginx Configuration](/docs/system-administration/configuration/frontend)
- [Authentication Configuration](/docs/system-administration/configuration/authentication)
- [OIDC / SSO Configuration](/docs/system-administration/configuration/oidc-sso)
