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, Web Vitals, boards, shares, and session replay data 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, including Umami 3.x analytics features.
  • 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.
  • 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.

Websites tracker script Gateway API or Ingress Service dual-stack optional Umami pods app + migrations PDB optional PostgreSQL subchart or external NetworkPolicy ingress + egress Runtime env tracker, 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 Secrets app, db, S3 keys Kubernetes Secrets consumed by pods Umami APP_SECRET + DB URL PostgreSQL analytics data Backup CronJob pg_dump archive S3 bucket compatible 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.

Umami Runtime Settings

ValueEnv VarDefaultPurpose
umami.disableTelemetryDISABLE_TELEMETRYtrueDisables upstream telemetry.
umami.disableUpdatesDISABLE_UPDATEStrueDisables upstream update checks.
umami.disableBotCheckDISABLE_BOT_CHECKfalseDisables bot filtering when required for controlled tests.
umami.cloudModeCLOUD_MODEfalseHides users, teams, and websites settings pages for managed deployments.
umami.forceSSLFORCE_SSLfalseTrusts proxy HTTPS and emits secure URLs.
umami.clientIpHeaderCLIENT_IP_HEADER""Header used to resolve client IP behind proxies.
umami.collectApiEndpointCOLLECT_API_ENDPOINT""Custom collect endpoint for tracker requests.
umami.corsMaxAgeCORS_MAX_AGE""CORS preflight cache duration.
umami.allowedFrameUrlsALLOWED_FRAME_URLS""Space-delimited frame allowlist.
umami.debugDEBUG""Debug namespaces, for example umami:prisma.
umami.trackerScriptNameTRACKER_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.

ValueDefaultDescription
backup.enabledfalseRender the backup CronJob.
backup.schedule"0 3 * * *"Cron schedule.
backup.concurrencyPolicyForbidPrevent overlapping backups by default.
backup.archivePrefixumamiArchive filename prefix.
backup.images.postgresqldocker.io/library/postgres:18-alpineImage used for pg_dump.
backup.images.uploaderdocker.io/helmforge/mc:1.0.0MinIO 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

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to chart resources.
replicaCountinteger1Number of Umami pods. Use external PostgreSQL before scaling above one.

Image

ParameterTypeDefaultDescription
image.repositorystringghcr.io/umami-software/umamiUmami container image repository.
image.tagstring3.1.0Umami image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

Database

ParameterTypeDefaultDescription
postgresql.enabledbooleantrueDeploy bundled HelmForge PostgreSQL.
postgresql.architecturestringstandalonePostgreSQL subchart architecture.
postgresql.auth.databasestringumamiDatabase created by the subchart.
postgresql.auth.usernamestringumamiApplication database user.
postgresql.auth.passwordstring""PostgreSQL password, generated when empty.
postgresql.serviceAccount.automountServiceAccountTokenbooleanfalseDisable API token mount in PostgreSQL pods by default.
database.external.hoststring""External PostgreSQL host.
database.external.portstring"5432"External PostgreSQL port.
database.external.namestringumamiExternal database name.
database.external.usernamestringumamiExternal database user.
database.external.passwordstring""Inline external database password. Prefer Secret or External Secrets.
database.external.existingSecretstring""Existing Secret with the database password.
database.external.existingSecretPasswordKeystringpasswordPassword key in the existing Secret.
database.external.init.enabledbooleanfalseRun external database preparation before Umami starts.
database.external.init.imagestringdocker.io/library/postgres:18-alpinePostgreSQL client image for init.
database.external.init.adminUsernamestringpostgresAdmin user used only by init.
database.external.init.adminExistingSecretstring""Secret containing the admin password.
database.external.init.sqlstringCREATE EXTENSION IF NOT EXISTS pgcrypto;SQL executed by the init container.

Service, Ingress, Gateway, and PDB

ParameterTypeDefaultDescription
service.typestringClusterIPKubernetes Service type.
service.portinteger80Service port.
service.annotationsobject{}Service annotations.
service.ipFamilyPolicystring""Optional Service IP family policy.
service.ipFamiliesarray[]Optional Service IP families.
ingress.enabledbooleanfalseRender Ingress.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.hostsarray[]Host/path rules.
ingress.tlsarray[]TLS entries.
gatewayAPI.enabledbooleanfalseRender Gateway API HTTPRoute.
gatewayAPI.parentRefsarray[]HTTPRoute parent references.
gatewayAPI.hostnamesarray[]HTTPRoute hostnames.
gatewayAPI.matchesarraypath prefix /HTTPRoute path matches.
gatewayAPI.filtersarray[]Optional HTTPRoute filters.
pdb.enabledbooleanfalseRender PodDisruptionBudget.
pdb.minAvailableinteger1Minimum available pods when enabled.
pdb.maxUnavailablestring""Alternative disruption budget value.

External Secrets

ParameterTypeDefaultDescription
externalSecrets.enabledbooleanfalseRender ExternalSecret resources.
externalSecrets.apiVersionstringexternal-secrets.io/v1External Secrets API version.
externalSecrets.refreshIntervalstring1hReconciliation refresh interval.
externalSecrets.secretStoreRef.namestring""SecretStore or ClusterSecretStore name.
externalSecrets.secretStoreRef.kindstringSecretStoreStore kind.
externalSecrets.app.enabledbooleanfalseManage APP_SECRET with External Secrets.
externalSecrets.database.enabledbooleanfalseManage external database password with External Secrets.
externalSecrets.backup.enabledbooleanfalseManage S3 backup credentials with External Secrets.

Probes, Security, and Scheduling

ParameterTypeDefaultDescription
probes.startup.pathstring/api/heartbeatStartup probe path.
probes.startup.initialDelaySecondsinteger30Startup probe initial delay.
probes.liveness.pathstring/api/heartbeatLiveness probe path.
probes.readiness.pathstring/api/heartbeatReadiness probe path.
resourcesobject{}Container resource requests and limits.
podSecurityContextobject{}Pod-level security context.
securityContextobject{}Container security context.
serviceAccount.createbooleanfalseCreate a dedicated ServiceAccount.
serviceAccount.automountServiceAccountTokenbooleanfalseMount API token into Umami and backup pods.
nodeSelectorobject{}Node selector.
tolerationsarray[]Tolerations.
affinityobject{}Affinity rules.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass name.
podLabelsobject{}Extra pod labels.
podAnnotationsobject{}Extra pod annotations.
extraVolumesarray[]Extra volumes.
extraVolumeMountsarray[]Extra volume mounts.
extraManifestsarray[]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 such as Boards, Shares, Web Vitals, and Session Replay. 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