Skip to content

Umami

Deploy Umami on Kubernetes as a privacy-first web analytics platform. The chart uses the official ghcr.io/umami-software/umami:3.1.0 image and supports a quick bundled PostgreSQL install as well as production setups with external PostgreSQL, Gateway API or Ingress, External Secrets Operator, dual-stack Service fields, NetworkPolicy, PodDisruptionBudget, S3-compatible backups, and structured Umami runtime options.

Umami tracks page views, sessions, events, and website analytics without requiring third-party analytics services. The application stores its state in PostgreSQL and exposes a lightweight tracker script that your websites load from the Umami instance.

Key Features

  • Umami 3.1.0 - aligned with the current HelmForge chart appVersion and validated with the HelmForge k3d runtime gate.
  • PostgreSQL first - bundled HelmForge PostgreSQL subchart for quick starts or external PostgreSQL for production.
  • External database preparation - optional init container can run CREATE EXTENSION IF NOT EXISTS pgcrypto; with an admin Secret.
  • Custom initialization - user-defined init containers can prepare shared volumes, such as GeoIP databases, before Umami starts.
  • Structured runtime settings - telemetry, updates, bot detection, SSL, client IP header, collect endpoint, CORS, frame allowlists, and tracker script name.
  • Secret management - inline values, existing Kubernetes Secrets, or external-secrets.io/v1 resources for APP_SECRET, database password, and S3 credentials.
  • Routing choices - Ingress or Kubernetes Gateway API HTTPRoute.
  • Dual-stack Service - optional ipFamilyPolicy and ipFamilies fields.
  • Production controls - NetworkPolicy, PodDisruptionBudget, scheduling controls, resource settings, and disabled ServiceAccount token mount by default.
  • S3 backup - scheduled PostgreSQL pg_dump uploaded to S3-compatible object storage.

Architecture

Production Request Flow

Website tracker requests reach Umami through Gateway API or Ingress, then the Service routes to one or more Umami pods backed by PostgreSQL.

Websitestracker scriptGateway APIor IngressServicedual-stack optionalUmami podsapp + migrationsPDB optionalPostgreSQLsubchart or externalNetworkPolicyingress + egressRuntime envtracker, SSL, CORS

Secrets and Backup Flow

External Secrets can materialize runtime credentials, while the backup CronJob dumps PostgreSQL data and uploads the archive to S3-compatible storage.

External Secretsapp, db, S3 keysKubernetes Secretsconsumed by podsUmamiAPP_SECRET + DB URLPostgreSQLanalytics dataBackup CronJobpg_dump archiveS3 bucketcompatible storage

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install umami helmforge/umami --namespace umami --create-namespace

OCI registry:

helm install umami oci://ghcr.io/helmforgedev/helm/umami --namespace umami --create-namespace

Default local access:

kubectl port-forward -n umami svc/umami-umami 3000:80

Open http://localhost:3000/ and sign in with Umami’s upstream initial credentials:

  • Username: admin
  • Password: umami
Change the initial password

Change the default admin password immediately after the first login. The chart does not replace Umami’s initial bootstrap credentials for you.

Development vs Production

The default chart values are intentionally simple and development-friendly:

  • postgresql.enabled=true
  • replicaCount=1
  • generated APP_SECRET and PostgreSQL password
  • ClusterIP Service only
  • no public ingress or Gateway API route
  • no NetworkPolicy or PDB
  • telemetry and update checks disabled

This is useful for local clusters, demos, and CI smoke tests. For production, use a stable APP_SECRET, external or operator-managed PostgreSQL, TLS termination, resource requests and limits, backup or database-native backup, and NetworkPolicy rules aligned with your cluster networking.

Deployment Examples

# values.yaml - development install with bundled PostgreSQL
postgresql:
  enabled: true

umami:
  appSecret: 'replace-with-a-stable-secret'
replicaCount: 2

umami:
  existingSecret: umami-app
  existingSecretKey: app-secret
  forceSSL: true
  clientIpHeader: x-forwarded-for
  trackerScriptName: stats
  collectApiEndpoint: /api/send

postgresql:
  enabled: false

database:
  external:
    host: postgres-primary.database.svc.cluster.local
    port: '5432'
    name: umami
    username: umami
    existingSecret: umami-db
    existingSecretPasswordKey: database-password
    init:
      enabled: true
      adminUsername: postgres
      adminExistingSecret: postgres-admin

resources:
  requests:
    cpu: 100m
    memory: 256Mi
  limits:
    memory: 512Mi

pdb:
  enabled: true
  minAvailable: 1

networkPolicy:
  enabled: true
  ingress:
    allowSameNamespace: true
  egress:
    enabled: true
    allowDNS: true
    allowSameNamespaceDatabase: true
    allowHTTPS: true
postgresql:
  enabled: false

database:
  external:
    host: postgres-primary.database.svc.cluster.local
    port: '5432'
    name: umami
    username: umami
    existingSecret: umami-db
    existingSecretPasswordKey: database-password
    init:
      enabled: true
      image: docker.io/library/postgres:18-alpine
      adminUsername: postgres
      adminExistingSecret: postgres-admin
      adminExistingSecretPasswordKey: postgres-password
      sql: |
        CREATE EXTENSION IF NOT EXISTS pgcrypto;
pgcrypto is required

The bundled PostgreSQL path creates pgcrypto through the subchart init script. For external databases, enable database.external.init.enabled when the Umami application user cannot create extensions.

gatewayAPI:
  enabled: true
  parentRefs:
    - name: public-gateway
      namespace: gateway-system
  hostnames:
    - analytics.example.com

umami:
  forceSSL: true
  clientIpHeader: x-forwarded-for
postgresql:
  enabled: false

externalSecrets:
  enabled: true
  apiVersion: external-secrets.io/v1
  secretStoreRef:
    name: platform-secrets
    kind: ClusterSecretStore
  app:
    enabled: true
    targetName: umami-app
    appSecretRemoteRef:
      key: prod/umami/app
      property: appSecret
  database:
    enabled: true
    targetName: umami-db
    passwordRemoteRef:
      key: prod/umami/database
      property: password
backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.example.com
    bucket: umami-backups
    prefix: production
    existingSecret: umami-backup-s3

Routing

Use Gateway API when your cluster has a Gateway controller and shared listeners:

gatewayAPI:
  enabled: true
  parentRefs:
    - name: public-gateway
      namespace: gateway-system
  hostnames:
    - analytics.example.com

Use Ingress when your platform standardizes on networking.k8s.io/v1 Ingress:

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: analytics.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: umami-tls
      hosts:
        - analytics.example.com

Service Dual Stack

The Service supports Kubernetes dual-stack fields:

service:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
    - IPv4
    - IPv6

Dual stack only works when the cluster, CNI, and Service CIDRs are configured for IPv4 and IPv6.

Custom Initialization

Use extraInitContainers with extraVolumes and extraVolumeMounts when Umami needs files prepared before the application starts, such as a GeoIP database or organization-owned assets:

extraVolumes:
  - name: geoip
    emptyDir: {}

extraInitContainers:
  - name: download-geoip
    image: docker.io/library/busybox:1.37
    command:
      - sh
      - -ec
    args:
      - wget -O /geoip/GeoLite2-City.mmdb https://example.com/GeoLite2-City.mmdb
    volumeMounts:
      - name: geoip
        mountPath: /geoip

extraVolumeMounts:
  - name: geoip
    mountPath: /app/geoip

Custom init containers run after the chart’s built-in database readiness check and optional external database preparation. Pin downloads to trusted URLs or use an internal artifact mirror for reproducible installs.

Umami Runtime Settings

Value Env Var Default Purpose
umami.disableTelemetry DISABLE_TELEMETRY true Disables upstream telemetry.
umami.disableUpdates DISABLE_UPDATES true Disables upstream update checks.
umami.disableBotCheck DISABLE_BOT_CHECK false Disables bot filtering when required for controlled tests.
umami.cloudMode CLOUD_MODE false Hides users, teams, and websites settings pages for managed deployments.
umami.forceSSL FORCE_SSL false Trusts proxy HTTPS and emits secure URLs.
umami.clientIpHeader CLIENT_IP_HEADER "" Header used to resolve client IP behind proxies.
umami.collectApiEndpoint COLLECT_API_ENDPOINT "" Custom collect endpoint for tracker requests.
umami.corsMaxAge CORS_MAX_AGE "" CORS preflight cache duration.
umami.allowedFrameUrls ALLOWED_FRAME_URLS "" Space-delimited frame allowlist.
umami.debug DEBUG "" Debug namespaces, for example umami:prisma.
umami.trackerScriptName TRACKER_SCRIPT_NAME "" Custom tracker script file name.

Use umami.extraEnv for upstream settings that are not yet modeled directly by the chart.

Custom tracker and collect endpoint

trackerScriptName and collectApiEndpoint can reduce ad-blocker false positives. Update the website snippet after changing either value.

Sub-path hosting

Umami’s BASE_PATH is a build-time setting for the upstream application image. The HelmForge chart does not set it as a runtime value for the stock image.

Secrets

The chart can create generated Secrets, consume existing Kubernetes Secrets, or render External Secrets Operator resources. For production, prefer a stable APP_SECRET stored outside the Helm release:

umami:
  existingSecret: umami-app
  existingSecretKey: app-secret
Keep APP_SECRET stable

Changing APP_SECRET invalidates active sessions and tokens. Do not rely on generated secrets for production upgrades or disaster recovery.

NetworkPolicy

NetworkPolicy is opt-in because egress needs vary by platform:

networkPolicy:
  enabled: true
  ingress:
    allowSameNamespace: true
  egress:
    enabled: true
    allowDNS: true
    allowSameNamespaceDatabase: true
    databasePort: 5432
    allowHTTPS: true

When PostgreSQL, S3, OIDC, or other integrations live outside the namespace, add explicit extraTo peers for your CNI and network model.

Backup

The backup CronJob runs pg_dump against the Umami PostgreSQL database and uploads the archive to S3-compatible storage. It protects Umami users, websites, dashboards, events, session replay data, shares, and related PostgreSQL state.

Value Default Description
backup.enabled false Render the backup CronJob.
backup.schedule "0 3 * * *" Cron schedule.
backup.concurrencyPolicy Forbid Prevent overlapping backups by default.
backup.archivePrefix umami Archive filename prefix.
backup.images.postgresql docker.io/library/postgres:18-alpine Image used for pg_dump.
backup.images.uploader docker.io/helmforge/mc:1.0.0 MinIO client image used for upload.
backup.s3.endpoint "" S3-compatible endpoint.
backup.s3.bucket "" Target bucket.
backup.s3.existingSecret "" Existing Secret with S3 credentials.
backup.database.existingSecret "" Optional backup-specific database password Secret.
backup.database.postgresDumpArgs "" Extra pg_dump arguments.
Backups require restore tests

A scheduled dump is not enough for production readiness. Test restore into a separate PostgreSQL database and verify Umami can start against the restored data.

Configuration Reference

Core

Parameter Type Default Description
nameOverride string "" Override the chart name.
fullnameOverride string "" Override the full release name.
commonLabels object {} Extra labels added to chart resources.
replicaCount integer 1 Number of Umami pods. Use external PostgreSQL before scaling above one.

Image

Parameter Type Default Description
image.repository string ghcr.io/umami-software/umami Umami container image repository.
image.tag string 3.1.0 Umami image tag.
image.pullPolicy string IfNotPresent Image pull policy.
imagePullSecrets array [] Pull secrets for private registries.

Database

Parameter Type Default Description
postgresql.enabled boolean true Deploy bundled HelmForge PostgreSQL.
postgresql.architecture string standalone PostgreSQL subchart architecture.
postgresql.auth.database string umami Database created by the subchart.
postgresql.auth.username string umami Application database user.
postgresql.auth.password string "" PostgreSQL password, generated when empty.
postgresql.serviceAccount.automountServiceAccountToken boolean false Disable API token mount in PostgreSQL pods by default.
database.external.host string "" External PostgreSQL host.
database.external.port string "5432" External PostgreSQL port.
database.external.name string umami External database name.
database.external.username string umami External database user.
database.external.password string "" Inline external database password. Prefer Secret or External Secrets.
database.external.existingSecret string "" Existing Secret with the database password.
database.external.existingSecretPasswordKey string password Password key in the existing Secret.
database.external.init.enabled boolean false Run external database preparation before Umami starts.
database.external.init.image string docker.io/library/postgres:18-alpine PostgreSQL client image for init.
database.external.init.adminUsername string postgres Admin user used only by init.
database.external.init.adminExistingSecret string "" Secret containing the admin password.
database.external.init.sql string CREATE EXTENSION IF NOT EXISTS pgcrypto; SQL executed by the init container.

Service, Ingress, Gateway, and PDB

Parameter Type Default Description
service.type string ClusterIP Kubernetes Service type.
service.port integer 80 Service port.
service.annotations object {} Service annotations.
service.ipFamilyPolicy string "" Optional Service IP family policy.
service.ipFamilies array [] Optional Service IP families.
ingress.enabled boolean false Render Ingress.
ingress.ingressClassName string traefik Ingress class name.
ingress.hosts array [] Host/path rules.
ingress.tls array [] TLS entries.
gatewayAPI.enabled boolean false Render Gateway API HTTPRoute.
gatewayAPI.parentRefs array [] HTTPRoute parent references.
gatewayAPI.hostnames array [] HTTPRoute hostnames.
gatewayAPI.matches array path prefix / HTTPRoute path matches.
gatewayAPI.filters array [] Optional HTTPRoute filters.
pdb.enabled boolean false Render PodDisruptionBudget.
pdb.minAvailable integer 1 Minimum available pods when enabled.
pdb.maxUnavailable string "" Alternative disruption budget value.

External Secrets

Parameter Type Default Description
externalSecrets.enabled boolean false Render ExternalSecret resources.
externalSecrets.apiVersion string external-secrets.io/v1 External Secrets API version.
externalSecrets.refreshInterval string 1h Reconciliation refresh interval.
externalSecrets.secretStoreRef.name string "" SecretStore or ClusterSecretStore name.
externalSecrets.secretStoreRef.kind string SecretStore Store kind.
externalSecrets.app.enabled boolean false Manage APP_SECRET with External Secrets.
externalSecrets.database.enabled boolean false Manage external database password with External Secrets.
externalSecrets.backup.enabled boolean false Manage S3 backup credentials with External Secrets.

Probes, Security, and Scheduling

Parameter Type Default Description
probes.startup.path string /api/heartbeat Startup probe path.
probes.startup.initialDelaySeconds integer 30 Startup probe initial delay.
probes.liveness.path string /api/heartbeat Liveness probe path.
probes.readiness.path string /api/heartbeat Readiness probe path.
resources object {} Container resource requests and limits.
podSecurityContext object {} Pod-level security context.
securityContext object {} Container security context.
serviceAccount.create boolean false Create a dedicated ServiceAccount.
serviceAccount.automountServiceAccountToken boolean false Mount API token into Umami and backup pods.
nodeSelector object {} Node selector.
tolerations array [] Tolerations.
affinity object {} Affinity rules.
topologySpreadConstraints array [] Topology spread constraints.
priorityClassName string "" PriorityClass name.
podLabels object {} Extra pod labels.
podAnnotations object {} Extra pod annotations.
extraVolumes array [] Extra volumes.
extraVolumeMounts array [] Extra volume mounts.
extraInitContainers array [] Extra init containers rendered after database readiness and preparation checks.
extraManifests array [] Additional Kubernetes manifests.

Common Issues

Users logged out after restart or upgrade

This usually means APP_SECRET changed. Set umami.appSecret, umami.existingSecret, or externalSecrets.app.enabled=true with a durable external secret source.

External database startup fails on pgcrypto

Enable database.external.init.enabled with an admin Secret or create the extension manually before installing the chart.

Tracking script blocked

Set umami.trackerScriptName to a neutral name such as stats and optionally set umami.collectApiEndpoint. Update website snippets after changing these values.

Upgrade Notes

Umami 3.x includes schema migrations for newer analytics features. Back up PostgreSQL before upgrading live deployments, especially when moving from Umami 2.x.

The chart version 2.0.0 is a breaking modernization release. Review production values for renamed or newly structured settings, External Secrets behavior, Gateway API, NetworkPolicy, and backup credentials before upgrading.

More Information