Deploy Umami with the analytics-umami template
Deploy Umami with the analytics-umami template
Stand up Umami, an open-source web and product analytics tool, on a single Quake AI instance using the validated OpenTofu template analytics-umami. You apply the template, reach the dashboard over the floating IP, change the default admin password, add a website, serve the host over HTTPS, wire a web app to send page views and events, and confirm the data arrives. An optional section adds error telemetry with GlitchTip and points you at the monitoring stack for infrastructure metrics.
Umami keeps your analytics data on infrastructure you own. You run it yourself; this is a self-hosted tool you operate, not a managed service.
Monthly cost estimate
Pricing calculator ↗Sized as a custom package on shared vCPU.
Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.
What each resource is for
Umami analytics host
s1a.medium · 4 shared vCPU, 4 GiB RAM, 0.5 Gbps
Runs Umami in Docker (the analytics dashboard and the tracking API your sites send events to). In bundled mode a PostgreSQL container runs alongside it; the database lives on an attached volume.
Umami plus a bundled PostgreSQL runs on 4 vCPU and 4 GiB RAM. In external-database mode the app alone fits a smaller flavor.
Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.
Included in baseline
s1a.medium
4 shared vCPU, 4 GiB RAM, 0.5 Gbps
Compute + RAM rate basis
4 vCPU + 4 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.
Block storage (50 GiB)
50 GiB at $0.08/GiB/mo
Public IP (included)
1 included with the custom package
Package promotional discount
Flat −$5.00/mo on the custom package (same promotion as named plans).
Included at no charge
These line items are zero on Quake AI. Many other providers meter them separately.
Data transfer (inbound and outbound)
Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.
AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.
Learn morePrivate networking
Private networks, subnets, Neutron routers, and security groups are included with the plan.
VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.
Control-plane API requests
OpenStack API calls for provisioning and management are included.
Some managed services on other clouds meter API calls or charge for premium control-plane features.
Pricing data last validated: . For current rates, check quake.ai/pricing.
Prerequisites#
You need:
- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (
source openrc.sh). See the OpenStack CLI guide. - An SSH keypair that already exists in your project. Record its name for the
key_namevariable. - A copy of the
analytics-umamitemplate directory from the template reference page. - A domain you can point at the instance. Umami serves a tracking script that your sites load over HTTPS, so a domain with TLS is part of the normal setup, not an afterthought.
- A web app to instrument. The Next.js app deployment gives you one if you do not already have a site running.
- Your workstation's public IP address. Find it with
curl -sS https://api.ipify.org.
Step 1: Set the variables and apply the template#
The dashboard listens on port 3000 over plain HTTP. The template's security group restricts port 3000 to dashboard_allowed_cidr, which defaults to the private network only. To reach the dashboard from your workstation for first-boot setup, set dashboard_allowed_cidr to your own address.
Copy the template's example variables file and open it:
cp terraform.tfvars.example terraform.tfvarsSet key_name to the SSH keypair already in your project, and dashboard_allowed_cidr to your workstation's public IP with a /32 suffix. Leave db_mode at its default bundled to run PostgreSQL on the same instance:
key_name = "YOUR_KEY_NAME"
dashboard_allowed_cidr = "YOUR_IP/32"Initialize the working directory, preview the plan, and apply:
tofu init
tofu plan
tofu applyOpenTofu provisions a private network, a router, a security group, a block volume mounted at /var/lib/docker, an instance, and a floating IP. On first boot, cloud-init mounts the data volume, installs Docker Engine, generates the app secret and database password into /opt/umami/.env, and starts Umami and PostgreSQL on port 3000.
When the apply finishes, read the outputs and record floating_ip and dashboard_url:
tofu outputStep 2: Change the default admin password#
Umami creates a default admin account (username admin, password umami) on first start. Change the password before you do anything else.
cloud-init takes a few minutes after the instance reaches ACTIVE. Open dashboard_url (for example http://YOUR_FLOATING_IP:3000). If the page does not load yet, watch the containers start over SSH:
ssh ubuntu@YOUR_FLOATING_IP "sudo docker compose -f /opt/umami/docker-compose.yml ps"Sign in with admin / umami, then go to Settings > Profile and set a strong password.
Step 3: Add a website#
- Go to Settings > Websites > Add website.
- Set a Name and the Domain of the site you will instrument (for example
app.example.com). - Select Save, then open the website's Edit > Tracking code. Copy the script tag; it looks like:
<script defer src="https://analytics.example.com/script.js" data-website-id="YOUR_WEBSITE_ID"></script>The src host is your Umami host and the data-website-id identifies this site. You wire both into your app in step 5.
Step 4: Serve the host over HTTPS with Caddy#
Your sites load the tracking script and post events to this host, so it needs a stable HTTPS address. The template leaves ports 80 and 443 open for a reverse proxy. Caddy
- Create a DNS A record for your analytics host (for example
analytics.example.com) pointing atYOUR_FLOATING_IP. Follow How to point a domain at a Quake AI resource. Wait until it resolves:
dig +short analytics.example.com- SSH to the instance and create
/opt/umami/Caddyfile:
analytics.example.com {
reverse_proxy 127.0.0.1:3000
}- Add Caddy to
/opt/umami/docker-compose.yml:
services:
caddy:
image: caddy:2
restart: unless-stopped
network_mode: host
volumes:
- /opt/umami/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
volumes:
caddy_data:- Apply the changes:
cd /opt/umami
sudo docker compose up -dOpen https://analytics.example.com and confirm the padlock. For background, see How to issue and auto-renew a TLS certificate with Let's Encrypt. Once HTTPS works, set dashboard_allowed_cidr back to the private network in terraform.tfvars and run tofu apply; the tracking script still loads over 443.
Step 5: Wire a web app to send events#
Add the tracking script to the app you want to measure. In a Next.js app, add it to the root layout so it loads on every page:
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<script
defer
src="https://analytics.example.com/script.js"
data-website-id="YOUR_WEBSITE_ID"
/>
</body>
</html>
);
}Deploy the change, then visit the site. To send a custom event from a click handler, call the tracker Umami exposes on the page:
window.umami?.track("signup", { plan: "starter" });Step 6: Verify the data arrives#
- Open your instrumented site in a browser and click around a few pages.
- In the Umami dashboard, open the website you added. Within a minute, Views and Visitors increment and the page list shows the paths you visited.
- If a custom event fired, it appears under the website's Events tab.
If no data arrives, confirm the script src resolves over HTTPS, the data-website-id matches the website in Umami, and no content blocker is dropping the request.
Add error telemetry with GlitchTip#
Page views answer "who visited"; error telemetry answers "what broke." GlitchTip
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://[email protected]/1",
tracesSampleRate: 0.1,
});GlitchTip speaks the Sentry protocol, so the official Sentry SDKs report to it unchanged. This keeps error telemetry on infrastructure you own, the same way Umami keeps analytics on your own host.
For infrastructure metrics (CPU, memory, disk, and service health on your VMs), deploy the monitoring stack rather than duplicating that role here. Umami covers product analytics, GlitchTip covers errors, and the monitoring stack covers the machines.
What you built#
- Applied the
analytics-umamitemplate to provision a network, security group, data volume, instance, and floating IP, with Umami and a bundled PostgreSQL started by cloud-init - Changed the default admin password and added a website
- Served the host over HTTPS through a Caddy reverse proxy so sites can load the tracking script
- Wired a web app to send page views and a custom event, and confirmed the data in the dashboard
- Pointed at GlitchTip and the monitoring stack for error telemetry and infrastructure metrics
Scope of this deployment#
This template runs a single-VM Umami host, not a managed analytics cloud. The instance is CPU-only and runs in one region. You operate the instance, Docker, Umami, the database, and the data volume yourself: back them up, patch them, and watch resource use as event volume grows. For higher volume, switch to external mode against a larger PostgreSQL and size the host up. GlitchTip and the monitoring stack run as their own deployments.
Next steps#
- Umami analytics template: the template reference, parameters, and resource map
- Deploy a Next.js app: the app to instrument if you do not have one
- Deploy Uptime Kuma: add uptime monitoring and a public status page
- Monitoring stack: Prometheus and Grafana for infrastructure metrics
- Security hardening checklist: tighten SSH access and exposure before you serve real traffic
Clean up#
When you no longer need the deployment, destroy everything the template created:
tofu destroyThen remove the DNS A record you created in step 4 and the tracking script from your app. Because Umami and its database live on the instance and its attached volume, tofu destroy removes the analytics data along with the infrastructure. Export any reports you want to keep before you destroy.
See Also
Instances
Prerequisite
Migrate a Docker container app from AWS to Quake AI
Shares: Docker, Containers
Deploy Airbyte with the airbyte template
Shares: Docker, Containers
Deploy Airflow with the airflow template
Shares: Docker, Containers
Deploy Appsmith with the appsmith-internal-tools template
Shares: Docker, Containers