# Umami self-hosted analytics

Source: https://docs.quake.ai/resources/iac-templates/analytics-umami
Markdown: https://docs.quake.ai/resources/iac-templates/analytics-umami.md

---

# Umami self-hosted analytics

This pattern composes Compute, Network, and Block Storage into a self-hosted web and product analytics host you run on infrastructure you control.

## What this template does

Provisions a single instance running [Umami](https://umami.is), an open-source analytics tool (a self-hosted alternative to Plausible, Vercel Analytics, or the metered tiers of Mixpanel and Amplitude). Your sites load a small tracking script served from this host, and the page-view and event data stays on your infrastructure:

- Compute instance that runs Umami in Docker, sized for the app plus a bundled PostgreSQL (4 vCPU and 4 GiB RAM)
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at `/var/lib/docker`, so the analytics data lives on a volume you can grow rather than on the boot disk
- cloud-init installs Docker Engine and starts Umami from a compose file on first boot

In bundled mode a PostgreSQL container runs alongside Umami. In external mode Umami points at a database you already run.

Umami creates a default admin account (username `admin`, password `umami`) on first start. Change the password the first time you sign in. The app secret and, in bundled mode, the database password are generated on first boot and written to `/opt/umami/.env`; no credential ships with this template.

## Parameters

| Parameter | Description | Default |
| --- | --- | --- |
| `key_name` | SSH keypair name (must already exist) | No default |
| `flavor_name` | Instance size (Umami plus bundled PostgreSQL runs on 4 vCPU / 4 GiB) | `s1a.medium` |
| `image_name` | Operating system image | `Ubuntu-24.04` |
| `app_name` | Display name prefix for resources | `umami` |
| `volume_size` | Block volume size in GiB, mounted at `/var/lib/docker` | `20` |
| `external_network` | External network for floating IP allocation | `PublicStatic` |
| `private_cidr` | CIDR for the private subnet | `10.44.0.0/24` |
| `dashboard_allowed_cidr` | CIDR allowed to reach the dashboard on port 3000 | `10.44.0.0/24` |
| `db_mode` | Datastore mode: `bundled` or `external` | `bundled` |
| `postgres_host` | PostgreSQL host (when `db_mode` is `external`) | `""` |
| `postgres_db` | PostgreSQL database name | `umami` |
| `postgres_user` | PostgreSQL user | `umami` |

## Dashboard access and security

The dashboard listens on port 3000 over plain HTTP. The security group restricts 3000 to `dashboard_allowed_cidr`, which defaults to the private network only, so the raw dashboard stays off the public internet. The tracking script your sites load is served from the same host, so put a reverse proxy in front before you send production traffic. Reach the host one of three ways:

- Put a reverse proxy (Caddy or Nginx) in front of Umami and serve the dashboard and tracking script over HTTPS on 443. Point the domain's DNS A record at the floating IP. This is the recommended path.
- Tunnel over SSH: `ssh -L 3000:localhost:3000 user@FLOATING_IP`, then open `http://localhost:3000`. Use this for setup; your sites cannot load the tracking script through a tunnel.
- Set `dashboard_allowed_cidr` to `YOUR_IP/32` to reach port 3000 directly from one address for setup.

Ports 80 and 443 stay open for the reverse proxy you put in front; they carry no traffic until you add one.


Change the default admin password (`admin` / `umami`) the first time you sign in, from **Settings** > **Profile**. The default credential is documented and well known.


## Datastore

The `db_mode` parameter selects the backend:

- `bundled` (default): a PostgreSQL container runs alongside Umami, with the database password generated on first boot and the data on the `/var/lib/docker` volume. This suits getting started and small-to-moderate traffic.
- `external`: points Umami at an existing PostgreSQL database, such as a [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) instance. Set `postgres_host`, `postgres_db`, and `postgres_user`, then replace `CHANGEME` in `DATABASE_URL` in `/opt/umami/.env` on the instance and run `docker compose up -d`. The password stays out of tfvars and the repo.

## When to use this pattern

Collect page views and custom events for your sites and products on a host you operate, and keep the data on your own infrastructure. Umami covers web analytics and lightweight product events. For the fuller product-analytics feature set (funnels, session replay, experiments), [PostHog](https://posthog.com) self-hosts too, on a heavier stack (ClickHouse plus Redis) that this template does not cover. For error and performance telemetry, pair this with [GlitchTip](https://glitchtip.com); for infrastructure metrics, use the [monitoring stack](/resources/iac-templates/monitoring-stack).

## Estimated cost

<PricingCompanion
  components={[
    { kind: "template", slug: "analytics-umami", required: true },
  ]}
/>

## Template source

<TemplateSource slug="analytics-umami" />

<TemplateResourceMap template="analytics-umami" format="opentofu" />

## Customize this pattern

- [Customize a template's image and flavor](/docs/automation/how-to/customize-template-image-flavor)
- [Add a block volume to a template](/docs/automation/how-to/add-volume-to-template)
- [Parameterize a template with a tfvars file](/docs/automation/how-to/parameterize-template-tfvars)

## See also

- [Self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres)
- [Monitoring stack](/resources/iac-templates/monitoring-stack)
- [Uptime Kuma](/resources/iac-templates/uptime-kuma)
