Skip to main content

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 PostgreSQL, serving both the analytics dashboard and the tracking endpoint on the same port. You embed a small tracking script on your site; visitor events POST back to the same workload and are stored in PostgreSQL, with no cookies and no personal data collected.
A new install is private, and it stays private until you publish it deliberately. Umami seeds a hardcoded admin / umami account from its first database migration and offers no environment variable to override it, so publicAccess.enabled defaults to false. Tracking collects nothing while it is off — browsers on the sites you track must reach the tracking script and the collect endpoint. Publishing is therefore a required second step, not an optional one: follow First Run in order.

Architecture

  • Umami — A stateless standard workload serving the dashboard, API, and tracking/collect endpoint on port 3000. Runs a single replica by default; set replicas to 2 or more for an always-on tier with zero-downtime rolling restarts. All state lives in PostgreSQL, so replicas are independent — no clustering.
  • PostgreSQL (single-instance, default) — The postgres template as a subchart: the backing store for all users, websites, sessions, and events.
  • PostgreSQL (HA, optional) — The postgres-highly-available template instead: 3× Patroni PostgreSQL with automatic failover and an HAProxy leader endpoint, for a durable production store.

What Gets Created

  • Standard Umami Workload — One or more stateless replicas serving the UI, API, and tracking endpoint on port 3000.
  • Database Workloads — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload.
  • Volume Sets — The database subchart’s persistent volumes (10 GiB by default; per replica in HA mode). Umami itself has none.
  • Identity & Policy — A least-privilege policy granting the Umami identity reveal on exactly two secrets: the app secret you create, and the active database’s credential secret.
  • Cron Backup Workload (optional) — When database backups are enabled.
The template creates no secret of its own. The app secret is a prerequisite opaque secret you create and own; the database credentials are created by the database subchart.
This template does not create a GVC. You must deploy it into an existing GVC.

Prerequisites

One opaque secret must exist before you install. It holds Umami’s app secret, which signs auth tokens and sessions — anyone holding it can forge a login — so it is never a Helm value and never lands in the release.
1

Create the app secret

Generate a random value and store it as the secret’s payload:
Set app.appSecretName to the name you used.
2

Keep it for the life of the install

The value must stay stable — changing it invalidates every issued token and logs every user out. In a multi-replica install all replicas read this one secret, which is what lets a session established against one replica be honored by another.
A missing app secret wedges the install rather than failing it. cpln helm install still exits 0 and reports every resource created, but the Umami workload never starts — it sits at zero replicas. cpln logs shows nothing at all, because no container ever starts; the real reason appears only under status.versions[].message of cpln workload get-deployments {release}-umami --gvc {gvc} -o yaml, which names the missing secret: The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.Creating the secret afterwards does not release the wedge on its own — it stayed at zero replicas for nearly five minutes after the secret existed. Run cpln workload force-redeployment {release}-umami --gvc {gvc} to pick it up.
Nothing else is required for a default install. Optional: a bucket and access setup for one of the supported providers if you enable database backups — see Backing Up. Change the database password (postgres.config.password or postgresHA.postgres.password) before installing as well; it ships with a change-me placeholder default. Once the secret exists, install the template using your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

First Run Sequence

Umami’s bootstrap admin is a hardcoded admin / umami, seeded by the first database migration, and there is no environment variable to override it — the credentials are published in Umami’s own documentation. A public default install would therefore stand on the internet with known credentials, so the install starts private and you publish it once the password is changed. This is a sequence, not a lockdown: the tracking endpoint is the product, and collection does not work until public access is on.
1

Install with the default private access

Install with publicAccess.enabled: false (the default). The canonical endpoint refuses external requests, while in-GVC callers still reach the workload per internalAccess.type.
2

Reach the dashboard over a port forward

Forward the workload’s port to your machine:
Then open http://localhost:3000/login in a browser and sign in as admin / umami.
cpln port-forward is a top-level command, not a subcommand of cpln workload.
3

Change the admin password

In the dashboard, go to Settings → Profile and change the password. The old password is rejected from that moment on.
4

Publish the dashboard and tracking endpoint

Set publicAccess.enabled: true and run a helm upgrade. The firewall change takes up to a couple of minutes to propagate — roughly 50 seconds in a measured run — so re-test the public URL rather than trusting the first response. Your changed password applies over the public endpoint immediately; the old umami password is rejected there too.
That publishing upgrade is the first upgrade after the install, so it bounces the bundled database. The first helm upgrade after an install re-applies the bundled PostgreSQL even though no database value changed, and Umami is unreachable for roughly two minutes while it restarts. This is expected, not a failed upgrade — do not roll back./api/heartbeat is not the signal to watch during that window. It returns 200 throughout, because it never touches the database, while the dashboard and all collection are still failing. Confirm recovery with a database-backed request — loading the dashboard — instead.

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: Two further changes need no action but are worth knowing:
  • publicAccess.enabled now defaults to false. An upgrade that does not set it explicitly makes the install private and stops data collection. Set it to true explicitly once the admin password is changed — see First Run.
  • The chart-created {release}-umami-config secret is gone. It existed only to hold the app secret; the template now creates no secret of its own.
The render guards are non-destructive against a running release. Tripping one exits non-zero without creating a new revision, and the workload keeps serving at its current version — you lose nothing by hitting it mid-upgrade.

Choosing a Database Mode

Exactly one of the two backing stores must be enabled — the chart enforces this at render. Umami is wired to the active database automatically. To switch to HA mode, set postgres.enabled: false and postgresHA.enabled: true.

Configuration

Key configuration values (see the template’s values.yaml for the complete set):

Application

  • image — The Umami open-source container image.
  • replicas — Number of stateless app-tier replicas. 1 is the proven single-instance shape; 2 or more gives an always-on tier where rolling restarts cycle one replica at a time with no downtime. Replicas are independent and share only the database and the app secret.
  • resources — CPU and memory per replica: minCpu/minMemory are the reservation, maxCpu/maxMemory the limit.
  • app.appSecretName — Name of the prerequisite opaque secret holding the app secret. It must exist before you install, and its value must stay stable — see Prerequisites.
  • app.disableTelemetry — When true (default), opts out of Umami’s anonymous usage telemetry.

Tracker

  • tracker.scriptName — Also serve the tracking script under a custom path (e.g. s.js/s.js) instead of only the default /script.js. Useful for reducing ad-blocker interception.
  • tracker.collectEndpoint — Have the tracker POST events to a custom path (e.g. /api/track) instead of the default /api/send. The custom path is baked into the script Umami serves, so the snippet you embed uses it automatically.
Both default to "" (standard paths). Custom paths take effect once a replica has fully booted with the new setting; a mid-rollout replica still serves the old path until it cycles.
Custom tracker paths are aliases, not replacements. With scriptName and collectEndpoint set, the default /script.js still returns the tracker and the default /api/send still accepts and records events. Custom paths help because your site loads the unblocked path — they do not close the defaults off.

Access

  • publicAccess.enabled — Serve the dashboard and tracking endpoint on the canonical *.cpln.app HTTPS endpoint. Defaults to false, which blocks external requests at the edge; in-GVC callers still reach it per internalAccess. Tracking collects nothing while it is off, so turn it on once the admin password is changed — see First Run.
  • internalAccess.type — Internal firewall scope of the Umami workload:
Access changes take up to a couple of minutes to propagate — every measured transition returned the stale value for the first 25–50 seconds. Re-poll before concluding a knob is broken.

Backing Store

Enable exactly one of postgres (single-instance, default) or postgresHA (HA) — see Choosing a Database Mode. In both modes, change the database password before installing (postgres.config.password / postgresHA.postgres.password).
Template 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 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (postgresHA.enabled, which is off by default here); see etcd History Compaction for the mechanism and the symptoms. Upgrade to 1.0.1 or later to turn 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.

Connecting

To start collecting data, log in, add a website in the dashboard, then paste the generated <script> tag — which loads the tracking script and POSTs to the collect endpoint — into your site’s HTML.

Backing Up

Database backups are optional and disabled by default. They cover the analytics database — the users, websites, sessions, and events that make up your Umami instance. Enable them with postgres.backup.enabled or postgresHA.backup.enabled (matching your database mode), and complete the storage setup for your provider before installing. These values are a pass-through to the backing database template, which owns and documents the backup itself; the backup runs as a scheduled job in that store.
1

Create a bucket

Create an S3 bucket. Set backup.aws.bucket and backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an AWS IAM policy that grants list/get/put/delete on your bucket (arn:aws:s3:::YOUR_BUCKET and arn:aws:s3:::YOUR_BUCKET/*), then set backup.aws.policyName to the policy’s name. The backing template’s README has the full JSON.
In HA mode, postgresHA.backup.mode selects logical (scheduled pg_dump) or wal-g (continuous WAL archiving). The full per-provider walkthrough, including the exact IAM JSON, lives in the backing postgres / postgres-highly-available template README.

Important Notes

  • Create the app secret before installing. A missing prerequisite secret does not fail the install — helm install reports success and the workload then sits at zero replicas with no container logs at all. Creating the secret afterwards needs a cpln workload force-redeployment to take effect; see Prerequisites.
  • Change the hardcoded admin / umami password before you make the install public. It is seeded by the first database migration and cannot be overridden at install time. This is why publicAccess.enabled defaults to false — follow First Run in order.
  • Tracking collects nothing until publicAccess.enabled is true — browsers on the sites you track must reach the tracking script and the collect endpoint. Publishing is a required step, not an optional one.
  • The upgrade that publishes the install bounces the bundled database for roughly two minutes, because it is the first upgrade after the install. /api/heartbeat stays 200 throughout and is not the signal to watch — it never touches the database.
  • Upgrading from 1.0.1 fails at render if you carry app.appSecret, resources.cpu, or resources.memory forward. Each guard names its replacement and leaves the running release untouched — see Upgrading From 1.0.1.
  • The app secret must stay stable for the life of the install — changing its payload logs every user out. Back it up outside Control Plane. It is yours, not the release’s: it survives helm uninstall and must be deleted manually.
  • Custom tracker paths are aliases/script.js and /api/send stay live and keep recording even when tracker.scriptName / tracker.collectEndpoint are set.
  • replicas ≥ 2 is recommended for production — replicas are independent and share the database and the app secret, so a session opened against one is honored by the others; rolling restarts cycle one at a time with no downtime.
  • Database volumes survive reinstalls under the same release name; uninstalling deletes them — all analytics data is lost. Use postgresHA and/or enable backups for durable production data.

External References

Umami Documentation

Official Umami documentation

Tracker Configuration

Configure the tracking script and its options

Collect API

How events are sent to the collect endpoint

Environment Variables

Umami environment variables reference

Umami Template

View the source files, default values, and chart definition