Overview
Umami is a privacy-first, cookieless web and product analytics platform — a self-hosted, MIT-licensed alternative to Google Analytics. This template deploys the stateless Umami v3 app tier backed by a bundled PostgreSQL store, serving the analytics dashboard and the tracking endpoint on the same port. You embed a small tracking script on your site; visitor events are posted back to the same workload and stored in PostgreSQL, with no cookies and no personal data collected. The install starts private: Umami seeds a hardcodedadmin / umami account that no setting can override, so publicAccess.enabled defaults to false and you publish the install only after changing that password. Tracking collects nothing until you do — follow First Run.
What Gets Created
Prerequisites
Two secrets must exist before you install: an opaque secret for the app secret and a dictionary secret for the database credentials. They hold credentials, so they are prerequisite secrets rather than values — a value would sit in plaintext in the Helm release for the life of the install. Secrets are org-level, so no--gvc flag is involved.
Create the app secret
app.appSecretName to this name.Create the database credentials secret
postgres.config.credentialsSecretName (single-instance, the default) or postgresHA.config.credentialsSecretName (HA). Umami builds its connection URL from the three keys, so keep them URL-safe (letters, digits, -, _). PostgreSQL reads the secret only when its data is first initialized — see Rotating the Database Password.app.twoFactorKeySecretName, opaque): a 64-character hex key that encrypts users’ TOTP secrets. Without it, two-factor setup in Settings reports that 2FA is not configured:
Installation
Once both secrets exist, install the template into your GVC, naming them:--values) rather than --set, because every later upgrade must pass them again. If you run more than one release of this template in the same org, create a separate database credentials secret for each — see Backing Store.
UI
CLI
Terraform
Pulumi
Configuration
Application
Tracker
/script.js and /api/send keep working. They help because your site loads a path ad blockers do not recognise. The custom collect path is baked into the script Umami serves, so the snippet you embed picks it up automatically.
Backing Store
Enable exactly one of the two stores — the chart refuses to render with both or neither. Umami is wired to whichever is active.Choosing a Database Mode
postgres.enabled: false and postgresHA.enabled: true. The credentials come from your prerequisite database credentials secret in both modes, named by postgresHA.config.credentialsSecretName in HA mode. Switching an existing release between modes does not move data — the other store starts empty.
Secret names are org-wide, so if you run more than one release of this template in the same org, create a separate database credentials secret for each and point each release’s postgres.config.credentialsSecretName (or postgresHA.config.credentialsSecretName in HA mode) at its own.
Database backups are configured under postgres.backup / postgresHA.backup — see Backing Up.
Access
publicAccess.enabled is false — browsers on the sites you track must reach the tracking script and the collect endpoint. Turn it on once the admin password is changed. An access change takes a few minutes to take effect, so re-test rather than trusting the first response.
Connecting
CANONICAL_ENDPOINT is the host of status.canonicalEndpoint. Open a tunnel to the dashboard from your machine — this works whether or not public access is on:
curl -sS http://localhost:3000/api/heartbeat. The heartbeat does not touch the database, so a 200 there proves only that the app tier is up; loading the dashboard is the check that the database is reachable too.
First Run
Umami’s bootstrap admin is a hardcodedadmin / umami, seeded by its first database migration and published in Umami’s own documentation. The install therefore starts private, and you publish it once the password is changed.
Install with public access off
publicAccess.enabled at its default false. Wait until the workload reports ready:Open the dashboard through a tunnel
cpln port-forward is a top-level command, not a subcommand of cpln workload. Open http://localhost:3000/login in a browser and sign in as admin / umami.Change the admin password
Publish the dashboard and tracking endpoint
publicAccess.enabled=true, passing the same app secret and database credentials secret names you installed with (or the same values file):cpln workload get RELEASE_NAME-umami --gvc GVC_NAME -o yaml.Add your website and embed the tracker
<script> tag into the HTML of your site. It loads the tracking script from the public address and posts events to the collect endpoint.Operations
Backing Up
Database backups are optional and disabled by default. They cover everything that makes up your Umami instance — users, websites, sessions and events. They are a pass-through to the bundled database template, which runs the backup as a scheduled job; enable them on whichever store is active and complete the storage setup for your provider before installing or upgrading with them on. Single-instance mode:- AWS S3
- Google Cloud Storage
- S3-compatible (MinIO)
Create a bucket
backup.aws.bucket and backup.aws.region on the active store to match.Set up a cloud account
backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
backup.aws.policyName to its name:Restoring a Backup
- The database workload is
RELEASE_NAME-postgres(single-instance) orRELEASE_NAME-postgres-ha-proxy(HA mode — always go through the leader endpoint). - The username, password and database name are the keys of the credentials secret:
cpln secret reveal DB_CREDENTIALS_SECRET_NAME -o yaml. - The Umami container has neither
psqlnor a cloud CLI, so run the restore from a client that can reach both the bucket and the database — your own machine with acpln port-forwardto the database workload open, for example. - Restore into an empty database and keep Umami from writing until the replay has finished; replaying into a database Umami has already migrated collides with the existing schema.
admin / umami.
Upgrading From 1.0.0
Version 1.0.0 did not compact the etcd cluster inside the bundled highly available database, so etcd’s backend grows with time alone and goes read-only once it reaches its quota — taking PostgreSQL failover with it. Only installs running the HA database are affected (postgresHA.enabled). Upgrading to 1.0.1 or later turns compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a NOSPACE alarm needs operator recovery rather than an upgrade. See etcd History Compaction.
Upgrading From 1.0.1
Version 1.1.0 is a security release that renames three values. Carrying a 1.0.1 values file forward fails at render with a message naming the replacement, so nothing silently reverts to a default:publicAccess.enabled also changed its default to false. An upgrade that does not set it explicitly makes the install private and stops data collection — pass publicAccess.enabled=true once the admin password is changed.
Upgrading From 1.1.0
Version 1.2.0 moved the single-instance database credentials. Carrying the old keys forward fails at render:postgres.config.credentialsSecretName to its name, and remove the three keys. The error message comes from the bundled database (config.username was REMOVED in postgres 3.4.0). The MinIO keys are a prerequisite secret too: create it as shown in Backing Up.
Upgrading From 1.2.0
Version 1.3.0 removed the HA database credentials block. Carrying the old block forward fails at render withthe postgres block was REMOVED in 2.5.0.
Put the username, password and database values that sat in a postgres block under postgresHA unchanged into the database credentials secret under Prerequisites — the HA database was initialized with them — set postgresHA.config.credentialsSecretName to its name, and delete the block. Take a backup before upgrading. Single-instance installs need no action.
Upgrading From 1.3.0
Version 1.3.1 moved the HA database’s MinIO backup keys into a prerequisite secret. With HA-mode MinIO backups on, the oldaccessKey and secretKey values under postgresHA.backup.minio now fail at render: create a dictionary secret holding accessKey and secretKey as shown in Backing Up and set postgresHA.backup.minio.credentialsSecretName to its name. No other install needs action.
Upgrading From 1.3.1
Version 1.4.0 removedpostgres.credentials: the database credentials are now a prerequisite secret, the same one the database templates require on their own. Carrying the old key forward fails the render.
1.3.x created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with — not new ones, or Umami loses its database connection. OLD_SECRET_NAME is the credentialsSecretName of the mode you use (default my-umami-db-credentials):
postgres.config.credentialsSecretName (single-instance mode) or postgresHA.config.credentialsSecretName (HA mode) set to that name, and postgres.credentials removed. Your current values are also in postgres.credentials of the 1.3.x release.
The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so Umami briefly loses its database and reconnects by itself. The new version of a workload can also wait for several minutes, with get-deployments reporting that it is not allowed to reveal the secret, while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
Upgrading From 1.4.0
The image moves from Umami 3.2.0 to 3.4.1, which fixes Critical vulnerabilities in its bundled Next.js. The chart refuses anyghcr.io/umami-software/umami tag below 3.4.1, so if your values pin image, set it to ghcr.io/umami-software/umami:3.4.1 or remove it.
Umami migrates its database on first start (two-factor, session linking, annotations, API keys). Rolling back to 1.4.0 afterwards is not supported, so take a backup first if you need a way back.
Two-factor authentication (new in Umami 3.3) needs the optional key secret described under Prerequisites. If you add it in the same upgrade, the new version can wait several minutes with is not allowed to reveal the secret in get-deployments while the new grant propagates; the previous version keeps serving.
In HA mode (postgresHA) the database moves to controlplanecorporation/patroni-postgres:0.8, which carries pgvector 0.8.7. The database members restart and nothing limits that rollout, so expect a brief interruption of writes while the leader changes; Umami itself restarts as on any upgrade, and with a single Umami replica (the default) that is the longer interruption, because the old replica stops before the new one serves. Upgrade in a quiet window. Umami does not use the vector extension; if you created it yourself in a database, run this there after the upgrade. If your values set postgresHA.image, set it to the 0.8 tag: tags 0.1 to 0.7 are refused at render.
Rotating the Database Password
PostgreSQL keeps the password it was initialized with, and a running workload keeps the values it started with, so a rotation takes three steps in order (single-instance; in HA mode follow Rotating the Database Password in HA Mode instead):- Change it in the database:
ALTER ROLE DB_USERNAME PASSWORD 'NEW_PASSWORD';(connected as that user, from inside the GVC). - Update the secret:
cpln secret edit SECRET_NAME. - Right away, redeploy Umami with
cpln workload force-redeployment RELEASE_NAME-umami --gvc GVC_NAME, and wait untilget-deploymentsshows a higher version number, alone and ready (right after the command the status may not show the restart yet, andready: truealone can still describe the old replica). Do not redeploy the database: PostgreSQL reads the secret only when it is first initialized, so restarting it only adds downtime.
Rotating the Database Password in HA Mode
The HA database (postgresHA) needs its own order. The replication role repl shares the password, and a member that restarts with the old password can be promoted to leader, which splits the cluster. Move the leader to member -0 first, so it restarts last, after every other member already has the new password. pgx runs a command in one member (LOCATION is your GVC’s location, DB_USERNAME the secret’s username):
RELEASE_NAME-umami, which keeps the old password until it restarts. The database drops connections briefly at each switchover, and the app cannot open new connections from the ALTER ROLE until its own redeploy finishes, so rotate in a quiet window. If a member stays starting, or running without streaming for several minutes, re-copy it from the leader. This copies the whole database, so it costs network traffic in proportion to its size:
Scaling and Availability
- The app tier scales with
replicas. It is stateless — replicas share only the database and the app secret — so2or more gives an always-on tier whose rolling restarts cycle one replica at a time. - The database is the availability limit. In single-instance mode a database restart makes the dashboard and collection briefly unavailable; set
postgresHA.enabled: trueon a new install for automatic failover. - The first
helm upgradeafter an install can restart the bundled database even when no database value changed. - Uninstall deletes every volume set the chart created, so all analytics data is lost and does not survive a reinstall. The GVC is yours and is left alone, and your prerequisite secrets survive. Enable backups if the data matters.
Troubleshooting
The Umami workload sits at 0 replicas after install while helm reported success
The Umami workload sits at 0 replicas after install while helm reported success
cpln helm install succeeded, but RELEASE_NAME-umami shows no containers and cpln logs returns nothing.Cause: The secret named by app.appSecretName, or the database credentials secret named by postgres.config.credentialsSecretName (HA mode: postgresHA.config.credentialsSecretName), does not exist. The deployment pauses on the unresolved reference.Fix: Read the message naming the secret, then create it as shown in Prerequisites:The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed. Once the secret exists the deployment recovers on its own within several minutes, or run cpln workload force-redeployment RELEASE_NAME-umami --gvc GVC_NAME to skip the wait.The dashboard shows no visitors
The dashboard shows no visitors
publicAccess.enabled is still false, so browsers cannot reach the tracking script or the collect endpoint — or the embedded script points at an address other than the public canonical endpoint. Ad blockers also block the default /script.js and /api/send.Fix: Set publicAccess.enabled=true (see First Run), confirm the <script> tag uses the public address, and set tracker.scriptName / tracker.collectEndpoint to custom paths to reduce ad-blocker interception.Render fails with enable exactly one backing store
Render fails with enable exactly one backing store
umami: enable exactly one backing store — set either postgres.enabled or postgresHA.enabled to true, not both, or the variant naming postgres.enabled (default) or postgresHA.enabled (durable HA).Cause: Both postgres.enabled and postgresHA.enabled are true, or both are false.Fix: Enable exactly one — see Choosing a Database Mode. For HA mode set postgres.enabled: false together with postgresHA.enabled: true.Every user is logged out after a change
Every user is logged out after a change
app.appSecretName changed, or app.appSecretName now points at a different secret. Existing tokens were signed with the old value.Fix: Nothing is lost — users sign in again. To avoid it, keep the app secret stable for the life of the install.Upgrade fails with a key that was REMOVED or RENAMED
Upgrade fails with a key that was REMOVED or RENAMED
helm upgrade stops at render with app.appSecret was removed in 1.1.0, resources.cpu was renamed to resources.maxCpu, config.username was REMOVED in postgres 3.4.0, the postgres block was REMOVED in 2.5.0, backup.minio.accessKey and backup.minio.secretKey were REMOVED, or postgres.credentials was REMOVED in 1.4.0.Cause: Your values still carry a key that moved in template versions 1.1.0 to 1.4.0. The existing release is left untouched and running.Fix: Follow the matching migration note above — Upgrading From 1.0.1, Upgrading From 1.1.0, Upgrading From 1.2.0, Upgrading From 1.3.0 or Upgrading From 1.3.1 — keeping the credentials the database already uses in your database credentials secret.Important Notes
- Create the app secret and the database credentials secret before installing. A missing one does not fail the install — the workload waits on it with no logs; see Prerequisites for the diagnostic.
- Editing the database credentials secret alone changes nothing — rotate with
ALTER ROLEplus a forced redeployment of Umami (not the database); see Rotating the Database Password. In HA mode a leader switchover comes first. - Change the hardcoded
admin/umamipassword before turning public access on — follow First Run in order. - Tracking collects nothing until
publicAccess.enabledistrue, and the tracking script must be loaded from the public address. - Keep the app secret stable — changing it logs every user out. It is not owned by the release: it survives
helm uninstalland must be deleted manually. - Pass the same app secret and database credentials secret names on every upgrade — a values file is the simplest way.
- Give each release its own database credentials secret — secret names are org-wide.
- Uninstall deletes the database volume sets — enable backups if the analytics data matters.