Skip to main content

GeoPulse Helm Chart Deployment & Configuration

This comprehensive guide covers manual Helm installation, configuration options, and advanced customization for GeoPulse on Kubernetes.

Canonical env var reference: Environment Variables Reference

Quick Start

For an interactive, guided installation experience, see the Kubernetes Quick Start Guide which uses automated scripts.


Overview​

The GeoPulse Helm chart provides a production-ready deployment with the following features:

  • Full Stack Deployment: Backend (Java/Quarkus in Native mode), Frontend (Vue.js), PostgreSQL with PostGIS
  • Optional MQTT Support: Conditional Mosquitto MQTT broker deployment
  • Production Ready: Health checks, resource limits, persistent storage
  • Flexible Configuration: Extensive values.yaml for customization
  • Security: Automatic encryption key generation, secrets management
  • Ingress Support: Optional ingress with TLS
  • High Availability Ready: Support for replicas and pod disruption budgets

Prerequisites​

  • Kubernetes 1.19+
  • Helm 3.2.0+
  • PV provisioner support in the underlying infrastructure (for persistence)
  • (Optional) Ingress controller for external access
  • (Optional) cert-manager for TLS certificates

Manual Installation​

This section describes how to install the chart directly with Helm for advanced users or automated workflows.

1. Clone the Repository​

git clone https://github.com/tess1o/GeoPulse.git
cd GeoPulse

2. Install the Chart​

You can install the chart using the helm install command. You must provide your own values, either with --set flags or a custom values file (-f my-values.yaml).

# Example installing with a custom values file from the examples
helm install geopulse ./helm/geopulse -f helm/examples/medium-deployment.yaml

3. Verify Installation​

# Check pod status
kubectl get pods -l app.kubernetes.io/instance=geopulse

# Run Helm tests
helm test geopulse

Configuration​

The GeoPulse Helm chart provides two ways to configure the application:

  1. Built-in Values - Common configuration options available in values.yaml
  2. Custom Environment Variables - For advanced settings not included in the default chart

Understanding values.yaml​

The Helm chart uses a hierarchical values.yaml file to configure GeoPulse. When you install or upgrade the chart, you can override these values:

# Option 1: Command-line flags
helm install geopulse ./helm/geopulse --set config.admin.email="admin@example.com"

# Option 2: Custom values file (recommended)
helm install geopulse ./helm/geopulse -f my-values.yaml

Built-in Configuration Options​

Core Configuration​

Featurevalues.yaml PathEnvironment VariableDefault
CORS Enabledconfig.cors.enabledGEOPULSE_CORS_ENABLEDfalse
CORS Originsconfig.cors.originsGEOPULSE_CORS_ORIGINShttp://localhost:5555
Public Base URLconfig.publicBaseUrlGEOPULSE_PUBLIC_BASE_URL"" (empty)
Frontend URL (legacy)config.uiUrlGEOPULSE_UI_URLhttp://localhost:5555
Cookie Domainconfig.cookieDomainGEOPULSE_COOKIE_DOMAIN"" (empty)
Secure Cookiesconfig.authSecureCookiesGEOPULSE_AUTH_SECURE_COOKIESfalse

config.uiUrl / GEOPULSE_UI_URL is a legacy compatibility fallback and is deprecated for new deployments.

Admin Configuration​

Featurevalues.yaml PathEnvironment VariableDefault
Admin Emailconfig.admin.emailGEOPULSE_ADMIN_EMAIL""
First User Adminconfig.admin.firstUserAdminGEOPULSE_FIRST_USER_ADMINtrue

Example:

config:
admin:
email: "admin@example.com"
firstUserAdmin: true

When config.admin.email is empty and config.admin.firstUserAdmin is true, the first registered account becomes admin. Set config.admin.email if the instance may be publicly reachable before the owner registers.

User Defaults​

Featurevalues.yaml PathEnvironment VariableDefault
Default Distance Unitconfig.user.defaultDistanceUnitGEOPULSE_USER_DEFAULT_DISTANCE_UNITKILOMETERS
Default Temperature Unitconfig.user.defaultTemperatureUnitGEOPULSE_USER_DEFAULT_TEMPERATURE_UNITCELSIUS

Allowed distance values are KILOMETERS and MILES; allowed temperature values are CELSIUS and FAHRENHEIT. This applies only to newly created users.

Authentication & Registration​

Featurevalues.yaml PathEnvironment VariableDefault
Registration Enabledconfig.auth.registrationEnabledGEOPULSE_AUTH_REGISTRATION_ENABLEDtrue
Password Registrationconfig.auth.passwordRegistrationEnabledGEOPULSE_AUTH_PASSWORD_REGISTRATION_ENABLEDtrue
OIDC Registrationconfig.auth.oidcRegistrationEnabledGEOPULSE_AUTH_OIDC_REGISTRATION_ENABLEDtrue

Example:

config:
auth:
registrationEnabled: false # Disable all registration
passwordRegistrationEnabled: false # Disable password registration only
oidcRegistrationEnabled: true # Allow OIDC registration

Session Configuration​

Sign-in sessions are stored in the database, so a session can be ended at any time (sign out, password change, disabling a user, OIDC single logout). Durations accept s, m, h and d suffixes.

Featurevalues.yaml PathEnvironment VariableDefault
Idle Timeoutconfig.session.idleTimeoutGEOPULSE_AUTH_SESSION_IDLE_TIMEOUT7d
Absolute Timeoutconfig.session.absoluteTimeoutGEOPULSE_AUTH_SESSION_ABSOLUTE_TIMEOUT90d
API Access Token Lifeconfig.session.accessTokenLifespanGEOPULSE_AUTH_ACCESS_TOKEN_LIFESPAN30m

Example:

config:
session:
idleTimeout: "14d"
absoluteTimeout: "30d"

GPS Filtering​

Featurevalues.yaml PathEnvironment VariableDefault
Filter Inaccurate Dataconfig.gps.filterInaccurateDataGEOPULSE_GPS_FILTER_INACCURATE_DATA_ENABLEDtrue
Max Allowed Accuracyconfig.gps.maxAllowedAccuracyGEOPULSE_GPS_MAX_ALLOWED_ACCURACY100 meters
Max Allowed Speedconfig.gps.maxAllowedSpeedGEOPULSE_GPS_MAX_ALLOWED_SPEED250 m/s

Example:

config:
gps:
filterInaccurateData: true
maxAllowedAccuracy: 50 # More strict filtering
maxAllowedSpeed: 200

Prometheus Metrics​

Featurevalues.yaml PathEnvironment VariableDefault
Enabledconfig.prometheus.enabledGEOPULSE_PROMETHEUS_ENABLEDfalse
Refresh Intervalconfig.prometheus.refreshIntervalGEOPULSE_PROMETHEUS_REFRESH_INTERVAL10m
GPS Points Metricsconfig.prometheus.gpsPoints.enabledGEOPULSE_PROMETHEUS_GPS_POINTS_ENABLEDtrue
User Metricsconfig.prometheus.userMetrics.enabledGEOPULSE_PROMETHEUS_USER_METRICS_ENABLEDtrue
Timeline Metricsconfig.prometheus.timeline.enabledGEOPULSE_PROMETHEUS_TIMELINE_ENABLEDtrue
Favorites Metricsconfig.prometheus.favorites.enabledGEOPULSE_PROMETHEUS_FAVORITES_ENABLEDtrue
Geocoding Metricsconfig.prometheus.geocoding.enabledGEOPULSE_PROMETHEUS_GEOCODING_ENABLEDtrue
Memory Metricsconfig.prometheus.memory.enabledGEOPULSE_PROMETHEUS_MEMORY_ENABLEDtrue
Workload Metricsconfig.prometheus.workload.enabledGEOPULSE_PROMETHEUS_WORKLOAD_ENABLEDtrue

Example:

config:
prometheus:
enabled: true
refreshInterval: "5m"
# Optionally disable specific metric classes
memory:
enabled: false
workload:
enabled: true

Geocoding Configuration​

Featurevalues.yaml PathEnvironment VariableDefault
Primary Providerconfig.geocoding.primaryProviderGEOPULSE_GEOCODING_PRIMARY_PROVIDERnominatim
Fallback Providerconfig.geocoding.fallbackProviderGEOPULSE_GEOCODING_FALLBACK_PROVIDER""
Delay (ms)config.geocoding.delayMsGEOPULSE_GEOCODING_DELAY_MS1000
Max BBox Area (km²)config.geocoding.cacheMaxBboxAreaKm2GEOPULSE_GEOCODING_CACHE_MAX_BBOX_AREA_KM25000
Nominatim Enabledconfig.geocoding.nominatim.enabledGEOPULSE_GEOCODING_NOMINATIM_ENABLEDtrue
Nominatim URLconfig.geocoding.nominatim.urlGEOPULSE_GEOCODING_NOMINATIM_URLhttps://nominatim.openstreetmap.org
Photon Enabledconfig.geocoding.photon.enabledGEOPULSE_GEOCODING_PHOTON_ENABLEDfalse
Photon URLconfig.geocoding.photon.urlGEOPULSE_GEOCODING_PHOTON_URLhttps://photon.komoot.io
Google Maps Enabledconfig.geocoding.googleMaps.enabledGEOPULSE_GEOCODING_GOOGLE_MAPS_ENABLEDfalse
Google Maps API Keyconfig.geocoding.googleMaps.apiKeyGEOPULSE_GEOCODING_GOOGLE_MAPS_API_KEY"" (secret)
Mapbox Enabledconfig.geocoding.mapbox.enabledGEOPULSE_GEOCODING_MAPBOX_ENABLEDfalse
Mapbox Access Tokenconfig.geocoding.mapbox.accessTokenGEOPULSE_GEOCODING_MAPBOX_ACCESS_TOKEN"" (secret)
Geoapify Enabledconfig.geocoding.geoapify.enabledGEOPULSE_GEOCODING_GEOAPIFY_ENABLEDfalse
Geoapify API Keyconfig.geocoding.geoapify.apiKeyGEOPULSE_GEOCODING_GEOAPIFY_API_KEY"" (secret)
Geoapify Languageconfig.geocoding.geoapify.languageGEOPULSE_GEOCODING_GEOAPIFY_LANGUAGE""
Geoapify Delayconfig.geocoding.geoapify.delayMsGEOPULSE_GEOCODING_GEOAPIFY_DELAY_MS0
ChibiGeo Enabledconfig.geocoding.chibigeo.enabledGEOPULSE_GEOCODING_CHIBIGEO_ENABLEDfalse
ChibiGeo URLconfig.geocoding.chibigeo.urlGEOPULSE_GEOCODING_CHIBIGEO_URLhttps://app.chibigeo.com/v1/photon
ChibiGeo API Keyconfig.geocoding.chibigeo.apiKeyGEOPULSE_GEOCODING_CHIBIGEO_API_KEY"" (secret)
ChibiGeo Languageconfig.geocoding.chibigeo.languageGEOPULSE_GEOCODING_CHIBIGEO_LANGUAGE""
ChibiGeo Delayconfig.geocoding.chibigeo.delayMsGEOPULSE_GEOCODING_CHIBIGEO_DELAY_MS0

Example:

config:
geocoding:
primaryProvider: "googlemaps"
fallbackProvider: "nominatim"
delayMs: 500
googleMaps:
enabled: true
apiKey: "your-api-key-here" # Stored in Kubernetes Secret
nominatim:
enabled: true
language: "en-US" # Optional: BCP 47 language tag
photon:
language: "en" # Optional: allowed Photon codes: de, pl, el, en, es, fa, fr, it, ja, ko
geoapify:
enabled: false
apiKey: "your-api-key-here"
language: "en"
delayMs: 0
chibigeo:
enabled: false
url: "https://app.chibigeo.com/v1/photon"
apiKey: "your-api-key-here"
language: "en"
delayMs: 0

Location Sharing​

Featurevalues.yaml PathEnvironment VariableDefault
Base URL Overrideconfig.share.baseUrlGEOPULSE_SHARE_BASE_URL"" (uses window.location.origin)

Example:

config:
share:
baseUrl: "https://share.geopulse.example.com"

User Invitation​

Featurevalues.yaml PathEnvironment VariableDefault
Base URL Overrideconfig.invitation.baseUrlGEOPULSE_INVITATION_BASE_URL"" (uses window.location.origin)

Example:

config:
invitation:
baseUrl: "https://invite.geopulse.example.com"

OwnTracks​

Featurevalues.yaml PathEnvironment VariableDefault
Ping Timestamp Overrideconfig.owntracks.pingTimestampOverrideGEOPULSE_OWNTRACKS_PING_TIMESTAMP_OVERRIDEfalse

Example:

config:
owntracks:
pingTimestampOverride: true

OIDC / SSO Configuration​

Featurevalues.yaml PathEnvironment VariableDefault
OIDC Enabledconfig.oidc.enabledGEOPULSE_OIDC_ENABLEDfalse
Callback Base URLconfig.oidc.callbackBaseUrlGEOPULSE_OIDC_CALLBACK_BASE_URL""
Auto-Link Accountsconfig.oidc.autoLinkAccountsGEOPULSE_OIDC_AUTO_LINK_ACCOUNTSfalse
Cleanup Enabledconfig.oidc.cleanupEnabledGEOPULSE_OIDC_CLEANUP_ENABLEDtrue
Google OIDC​
config:
oidc:
enabled: true
google:
enabled: true
clientId: "your-google-client-id.apps.googleusercontent.com"
clientSecret: "your-google-client-secret" # Stored in Kubernetes Secret
Microsoft OIDC​
config:
oidc:
enabled: true
microsoft:
enabled: true
clientId: "your-microsoft-client-id"
clientSecret: "your-microsoft-client-secret" # Stored in Kubernetes Secret
Generic OIDC (Keycloak, Authentik, Okta, etc.)​
config:
oidc:
enabled: true
generic:
enabled: true
name: "Keycloak" # Display name in UI
clientId: "geopulse"
clientSecret: "your-keycloak-client-secret" # Stored in Kubernetes Secret
discoveryUrl: "https://keycloak.example.com/realms/master/.well-known/openid-configuration"

Advanced Configuration: Custom Environment Variables​

Some GeoPulse features are not exposed in the default Helm chart values.yaml. For these advanced settings, you can add custom environment variables to the backend deployment.

Method 1: Using extraEnv in values.yaml​

This is the recommended approach for most custom configuration needs.

Step 1: Create a custom values file (custom-values.yaml):

backend:
extraEnv:
- name: GEOPULSE_TIMELINE_PROCESSING_THREADS
value: "4"
- name: GEOPULSE_TIMELINE_VIEW_ITEM_LIMIT
value: "200"
- name: GEOPULSE_IMPORT_BULK_INSERT_BATCH_SIZE
value: "1000"

Step 2: Apply the configuration:

helm upgrade geopulse ./helm/geopulse -f custom-values.yaml

Method 2: Using ConfigMap for Multiple Variables​

For many custom variables, create a dedicated ConfigMap:

Step 1: Create custom-config.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
name: geopulse-custom-config
namespace: default
data:
GEOPULSE_TIMELINE_PROCESSING_THREADS: "4"
GEOPULSE_TIMELINE_VIEW_ITEM_LIMIT: "200"
GEOPULSE_IMPORT_BULK_INSERT_BATCH_SIZE: "1000"
GEOPULSE_TIMELINE_STAYPOINT_RADIUS_METERS: "75"

Step 2: Apply the ConfigMap:

kubectl apply -f custom-config.yaml

Step 3: Reference it in your values file:

backend:
extraEnvFrom:
- configMapRef:
name: geopulse-custom-config

Method 3: Using Secrets for Sensitive Data​

For sensitive configuration like API keys:

Step 1: Create a Kubernetes Secret:

kubectl create secret generic geopulse-extra-secrets \
--from-literal=GEOPULSE_AI_SOME_API_KEY='your-secret-key'

Step 2: Reference it in values:

backend:
extraEnv:
- name: GEOPULSE_AI_SOME_API_KEY
valueFrom:
secretKeyRef:
name: geopulse-extra-secrets
key: GEOPULSE_AI_SOME_API_KEY

Configuration Examples​

Minimal Setup (Local Testing)​

# minimal-values.yaml
postgres:
persistence:
enabled: false

keygen:
persistence:
enabled: false
helm install geopulse ./helm/geopulse -f minimal-values.yaml

Notes:

  • With keygen.persistence.enabled=false, keys are generated at backend startup and stored in an emptyDir. This is suitable only for single-backend dev/testing.
  • For multiple backend replicas on different nodes, use a storage class that supports ReadWriteMany, or force backend pods onto the same node.
  • backend.backupPersistence.enabled=false uses emptyDir backup storage and is suitable only for throwaway testing. Full restore operations need durable backup storage.

Production Setup with Ingress​

# production-values.yaml
# Resource allocation for production
backend:
replicaCount: 2
resources:
limits:
memory: 2Gi
cpu: 2000m
requests:
memory: 1Gi
cpu: 1000m

frontend:
replicaCount: 2

postgres:
persistence:
enabled: true
size: 50Gi
storageClass: "fast-ssd"
resources:
limits:
memory: 4Gi
cpu: 2000m

# Ingress configuration
ingress:
enabled: true
className: nginx
hostname: geopulse.example.com
tls:
enabled: true
secretName: geopulse-tls
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"

# Application configuration
config:
uiUrl: "https://geopulse.example.com"
cookieDomain: ".example.com"
authSecureCookies: true
admin:
email: "admin@example.com"
oidc:
enabled: true
google:
enabled: true
clientId: "..."
clientSecret: "..."
helm install geopulse ./helm/geopulse -f production-values.yaml

The production example runs two backend replicas for normal availability. Before activating a full restore from Administration > Settings > Backup, scale the backend down to exactly one replica and keep PostgreSQL running:

kubectl scale deployment geopulse-backend --replicas=1
kubectl rollout status deployment/geopulse-backend

Production Setup with Gateway API​

GeoPulse can create a Gateway API HTTPRoute for clusters that use Gateway API instead of Ingress. This is disabled by default, so existing installs are unchanged by a normal helm upgrade.

When route.enabled=true, the route must be attached to a Gateway. The recommended configuration is to set route.parentRefs to the Gateway listener that should serve GeoPulse. This follows the Gateway API model for attaching HTTPRoutes to Gateways.

# gateway-values.yaml
route:
enabled: true
hostnames:
- geopulse.example.com
parentRefs:
- name: public-gateway
namespace: gateway-system
sectionName: https

config:
uiUrl: "https://geopulse.example.com"
authSecureCookies: true
helm install geopulse ./helm/geopulse -f gateway-values.yaml

If your Gateway API installation uses default Gateways, you can explicitly opt in with route.useDefaultGateways: All instead of setting route.parentRefs. Leaving both route.parentRefs empty and route.useDefaultGateways unset is invalid because the HTTPRoute would not attach to a Gateway. See the Gateway API useDefaultGateways field for the upstream behavior.

Enable MQTT Broker​

# mqtt-values.yaml
mosquitto:
enabled: true
username: mqtt_admin
service:
type: LoadBalancer # Or NodePort for external access
persistence:
enabled: true

config:
# Update UI URL if needed
uiUrl: "http://your-domain:5555"
helm install geopulse ./helm/geopulse -f mqtt-values.yaml

Use External PostgreSQL​

# external-db-values.yaml
postgres:
enabled: false

externalPostgres:
host: postgres.example.com
port: 5432
database: geopulse
username: geopulse-user
password: "your-secure-password"
helm install geopulse ./helm/geopulse -f external-db-values.yaml

Full restore requires a direct PostgreSQL connection to one server. Do not point externalPostgres.host at a transaction-pooling endpoint. The restore role must be able to connect to the maintenance database, create and rename databases, terminate sessions for the GeoPulse databases, create the required PostGIS extensions, and assume the application role with SET ROLE. Managed PostgreSQL services may need a separate privileged restore role supplied through GEOPULSE_BACKUP_RESTORE_USERNAME and GEOPULSE_BACKUP_RESTORE_PASSWORD, and some managed services may not support this workflow.

Backup and Restore on Kubernetes​

The chart enables backend.backupPersistence by default, creating a 20 GiB PVC mounted at /data/geopulse-backups. The backend sets GEOPULSE_BACKUP_WORK_PATH to /data/geopulse-backups/.work, so restore journals, encrypted uploads, and extracted dumps survive backend pod replacement.

Configure backup password, scheduled backups, retention count, local backup path, and operation timeout in Administration > Settings > Backup. Their environment variables are optional startup defaults for GitOps/bootstrap setups; once a value is saved in GeoPulse, the database setting takes precedence over the environment fallback. Use backend.extraEnv with secretKeyRef for GEOPULSE_BACKUP_PASSWORD or GEOPULSE_BACKUP_RESTORE_PASSWORD instead of putting secrets in ConfigMaps.

For restore:

  1. Confirm backend.backupPersistence.enabled=true or provide backend.backupPersistence.existingClaim.
  2. Confirm keygen.persistence.enabled=true; the destination key PVC must remain mounted while GeoPulse re-encrypts restored secrets.
  3. Scale the backend deployment to exactly one replica before restore activation.
  4. Keep PostgreSQL running during activation. GeoPulse renames databases through PostgreSQL and exits only the backend process.
  5. If the backend does not come back automatically, restart or replace only the backend pod while keeping the backup PVC attached.
  6. After validating the restored data, clean up any retained previous database using the exact name recorded by the restore journal.

The backend images include PostgreSQL 17 and 18 client tools and use the ones matching the server, so the bundled PostgreSQL and external PostgreSQL 17 or 18 work without configuration. For other major versions, or a custom backend image, provide matching pg_dump and pg_restore and point GEOPULSE_BACKUP_BINARY_DIRECTORY at them through backend.extraEnv.

Self-Hosted Geocoding​

config:
geocoding:
primaryProvider: "nominatim"
nominatim:
enabled: true
url: "https://nominatim.mycompany.internal"
language: "de" # Optional: BCP 47 language tag
# Self-hosted Photon as fallback
fallbackProvider: "photon"
photon:
enabled: true
url: "https://photon.mycompany.internal"
language: "de" # Optional: allowed Photon codes: de, pl, el, en, es, fa, fr, it, ja, ko

High-Performance Import Configuration​

backend:
resources:
limits:
memory: 4Gi
cpu: 2000m
extraEnv:
- name: GEOPULSE_IMPORT_BULK_INSERT_BATCH_SIZE
value: "2000"
- name: GEOPULSE_IMPORT_MERGE_BATCH_SIZE
value: "1000"
- name: GEOPULSE_IMPORT_LARGE_FILE_THRESHOLD_MB
value: "200"
- name: GEOPULSE_TIMELINE_PROCESSING_THREADS
value: "4"

Monitoring with Prometheus​

config:
prometheus:
enabled: true
refreshInterval: "5m"
# All metrics enabled by default

# Optional: If using Prometheus Operator
serviceMonitor:
enabled: true
interval: 30s
path: /api/v1/system/metrics

Parameters Reference​

Global Parameters​

ParameterDescriptionDefault
global.imagePullPolicyImage pull policyIfNotPresent
global.imagePullSecretsImage pull secrets[]

Backend Parameters​

ParameterDescriptionDefault
backend.image.repositoryBackend image repositorytess1o/geopulse-backend
backend.image.tagBackend image tag. Use 1.9.0-native for optimized builds (modern CPUs). Use 1.9.0-native-compat for older CPUs (x86-64-v2) or Raspberry Pi 3/4. See CPU Compatibility section below for troubleshooting.1.9.0-native
backend.replicaCountNumber of backend replicas1
backend.service.typeBackend service typeClusterIP
backend.service.portBackend service port8080
backend.resources.limits.memoryBackend memory limit1Gi
backend.resources.limits.cpuBackend CPU limit1000m
backend.backupPersistence.enabledCreate durable backend backup storage for encrypted .gpb files and restore journalstrue
backend.backupPersistence.existingClaimExisting PVC to mount for backup storage instead of creating one""
backend.backupPersistence.storageClassStorage class for the backup PVC""
backend.backupPersistence.accessModeAccess mode for the backup PVC; use storage compatible with your replica schedulingReadWriteOnce
backend.backupPersistence.sizeBackup PVC size20Gi
backend.backupPersistence.mountPathBackend mount path for local encrypted full backup files/data/geopulse-backups
backend.backupPersistence.workPathPersistent work path for restore journals, uploads, and extracted dumps/data/geopulse-backups/.work

Frontend Parameters​

ParameterDescriptionDefault
frontend.image.repositoryFrontend image repositorytess1o/geopulse-ui
frontend.image.tagFrontend image tag1.9.0
frontend.replicaCountNumber of frontend replicas1
frontend.service.typeFrontend service typeClusterIP
frontend.service.portFrontend service port80
frontend.service.nodePortFixed NodePort (optional)null

PostgreSQL Parameters​

ParameterDescriptionDefault
postgres.enabledDeploy PostgreSQLtrue
postgres.image.repositoryPostgreSQL imagepostgis/postgis
postgres.image.tagPostgreSQL image tag18-3.6
postgres.persistence.enabledEnable persistencetrue
postgres.persistence.sizePVC size10Gi
postgres.persistence.storageClassStorage class""
postgres.databaseDatabase namegeopulse
postgres.usernameDatabase usernamegeopulse-user
postgres.config.sharedBuffersPostgreSQL shared_buffers256MB

MQTT (Mosquitto) Parameters​

ParameterDescriptionDefault
mosquitto.enabledDeploy MQTT brokerfalse
mosquitto.image.repositoryMosquitto imageiegomez/mosquitto-go-auth
mosquitto.image.tagMosquitto image tag3.0.0-mosquitto_2.0.18
mosquitto.usernameMQTT admin usernamegeopulse_mqtt_admin
mosquitto.verboseLoggingTemporary verbose broker/auth loggingfalse
mosquitto.persistence.enabledEnable persistencetrue

Ingress Parameters​

ParameterDescriptionDefault
ingress.enabledEnable ingressfalse
ingress.classNameIngress class namenginx
ingress.hostnameHostnamegeopulse.example.com
ingress.tls.enabledEnable TLSfalse
ingress.tls.secretNameTLS secret namegeopulse-tls

Gateway API Route Parameters​

ParameterDescriptionDefault
route.enabledEnable Gateway API route creationfalse
route.apiVersionRoute API versiongateway.networking.k8s.io/v1
route.kindRoute kindHTTPRoute
route.hostnamesRoute hostnames[]
route.parentRefsGateway parent references. Required unless using default Gateways.[]
route.useDefaultGatewaysAttach to Gateway API default Gateways. Use All or None.""
route.matchesRoute match rules for the default frontend backendPath prefix /
route.filtersFilters for the default frontend backend rule[]
route.additionalRulesAdditional custom route rules[]
route.httpsRedirectCreate an HTTP-to-HTTPS redirect routefalse

Upgrading​

# Upgrade to a new version
helm upgrade geopulse ./helm/geopulse

# Upgrade with new values
helm upgrade geopulse ./helm/geopulse -f my-values.yaml

# See what will change
helm diff upgrade geopulse ./helm/geopulse -f my-values.yaml

Uninstalling​

helm uninstall geopulse

Note: This will not delete PersistentVolumeClaims. To delete them:

kubectl delete pvc -l app.kubernetes.io/instance=geopulse

Accessing GeoPulse​

With Ingress​

Access via the configured hostname:

https://geopulse.example.com

Without Ingress (Port Forward)​

kubectl port-forward svc/geopulse-frontend 5555:80

Then visit: http://localhost:5555

With LoadBalancer​

kubectl get svc geopulse-frontend
# Note the EXTERNAL-IP and access via http://EXTERNAL-IP

Persistence​

GeoPulse uses PersistentVolumeClaims for:

  1. PostgreSQL data (10Gi by default)
  2. Encryption key (10Mi) generated by a backend initContainer
  3. MQTT data (if enabled, 1Gi for data, logs, config)

Configure storage classes and sizes in values.yaml.


Security​

  • Passwords are auto-generated if not provided
  • The installation encryption key is generated by a backend initContainer at pod startup
  • Secrets are stored in Kubernetes Secret objects
  • Use existing secrets with: secrets.useExistingSecret=true

Managing Secrets​

Best Practices​

  1. Never commit secrets to Git - Use .gitignore for values files with secrets
  2. Use Kubernetes Secrets - Store sensitive data in Secret objects, not ConfigMaps
  3. Consider External Secret Managers - For production, use AWS Secrets Manager, HashiCorp Vault, or similar
  4. Rotate secrets regularly - Update secrets periodically and restart pods

Using Sealed Secrets​

For GitOps workflows, consider using Sealed Secrets:

# Install Sealed Secrets controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml

# Create a sealed secret
echo -n 'your-secret-value' | kubectl create secret generic mysecret --dry-run=client --from-file=api-key=/dev/stdin -o yaml | \
kubeseal -o yaml > sealed-secret.yaml

# Commit sealed-secret.yaml to Git safely

Monitoring​

Check Pod Status​

kubectl get pods -l app.kubernetes.io/instance=geopulse

View Logs​

# Backend
kubectl logs -l app.kubernetes.io/component=backend -f

# Frontend
kubectl logs -l app.kubernetes.io/component=frontend -f

# PostgreSQL
kubectl logs -l app.kubernetes.io/component=database -f

Run Tests​

helm test geopulse

Troubleshooting​

CPU Compatibility Issues​

If your backend pods are crashing immediately with errors about missing CPU features, you have an older CPU that needs the compatible image.

Check pod logs:

kubectl logs -l app.kubernetes.io/component=backend

Look for errors like:

  • [AVX2, BMI1, BMI2, FMA, F16C, LZCNT] (AMD64 CPUs)
  • [FP, ASIMD, CRC32, LSE] (ARM64/Raspberry Pi)

Solution: Override the image tag in your values file:

# values-compat.yaml
backend:
image:
tag: "1.9.0-native-compat" # Use compatible image for old CPUs

Then upgrade your deployment:

helm upgrade geopulse ./helm/geopulse -f values-compat.yaml

Who needs the compatible image?

  • Old x86 CPUs: Intel pre-Haswell (before 2013), AMD pre-Excavator (before 2015)
    • Examples: Intel Core i5-3470T (Ivy Bridge), Intel Xeon E5-2670 (Sandy Bridge)
  • Raspberry Pi 3/4: ARM Cortex-A53/A72 processors

Check node CPU features:

# For x86/AMD64 nodes:
kubectl debug node/YOUR-NODE-NAME -it --image=ubuntu -- lscpu | grep -i flags
# Look for: avx2, bmi1, bmi2, fma, f16c

# For ARM64 nodes:
kubectl debug node/YOUR-NODE-NAME -it --image=ubuntu -- cat /proc/cpuinfo | grep Features
# Look for: asimd, crc32, atomics

Pods Not Starting​

Check events:

kubectl get events --sort-by='.lastTimestamp'

Database Connection Issues​

Verify PostgreSQL is running:

kubectl get pods -l app.kubernetes.io/component=database
kubectl logs -l app.kubernetes.io/component=database

Encryption Key Issues​

Check backend initContainer logs:

kubectl get pods -l app.kubernetes.io/component=backend
kubectl logs -l app.kubernetes.io/component=backend -c generate-keys

MQTT Not Working​

Ensure MQTT is enabled and check logs:

kubectl logs -l app.kubernetes.io/component=mqtt

Configuration Not Applied​

If your configuration changes aren't taking effect:

  1. Verify the values are set:

    helm get values geopulse
  2. Check the ConfigMap:

    kubectl get configmap geopulse-config -o yaml
  3. Restart the backend pod:

    kubectl rollout restart deployment geopulse-backend

Finding Environment Variables​

To see all environment variables in the running backend:

kubectl exec -it deployment/geopulse-backend -- env | grep GEOPULSE | sort

Validating Secrets​

Check if secrets are properly set:

# List secret keys (values are base64 encoded)
kubectl get secret geopulse-secret -o jsonpath='{.data}' | jq 'keys'

# Decode a specific secret (be careful with sensitive data)
kubectl get secret geopulse-secret -o jsonpath='{.data.GEOPULSE_ADMIN_EMAIL}' | base64 -d

For feature-specific configuration details, see:


Support​

License​

AGPL-3.0 with Non-Commercial Use Restriction