Skip to main content

Analytics for the Price of Disk: Self-Hosting Umami as a Tenant Workload Instead of Paying Per Event

12 min readDora NodaDora Noda
Share

On July 14, 2026 a post hit the Hacker News front page with 66 points and a simple thesis: wiring self-hosted Umami into an iOS app. The author was not announcing a new database or a clever data pipeline. They were pointing out that product analytics is, at its core, a web service that writes rows to a Postgres table and shows them on a dashboard. The rest — per-event meters, seat-based add-ons, overage surprises — is packaging.

That framing landed because it inverts how most teams think about analytics. If it is just another container plus a database, your next ten million pageviews should be measured in gigabytes, not pricing tiers. This post runs the math.

At 10 million monthly events, Plausible Cloud's Business tier is $339 per month and PostHog's pay-as-you-go is north of $2,000 in overage past the free tier. The same 10 million rows self-hosted on Postgres occupy roughly 1.5 GB and ride for free on a Hetzner box already running your apps — marginal cost well under a dollar. The table below makes the gap concrete.

The per-event tax: what 100K, 1M, and 10M pageviews actually cost as SaaS

Every analytics SaaS prices the same underlying operation — "count this pageview" — behind a different meter. The sticker numbers look small until you multiply by traffic.

Plausible Cloud is the cleanest price sheet to read. Plans scale purely by monthly pageviews, with three feature tiers per volume:

  • 10K pageviews: $9 (Starter) / $14 (Growth) / $19 (Business)
  • 100K: $19 / $29 / $39
  • 1M: $69 / $104 / $139
  • 10M: $169 / $254 / $339

The slider is linear in one direction: more traffic, next tier. Yearly billing shaves about two months off, but the shape does not change. A solo blog at 10K pays $9. A modest SaaS marketing site plus docs plus app at 1M pays $69 to $139 depending on whether you need team seats and funnels. The app that crosses 10M — not enormous for a successful product with docs, blog, and in-app events — pays $169 minimum, $339 if you want Business features, twelve times a year.

Umami Cloud uses the same per-event logic with a slightly different meter: each pageview counts as one event, each custom event property also counts as one event, and overage beyond the plan is billed at a per-event rate without interrupting collection. The headline is honest and worth quoting verbatim from their pricing FAQ: "Self-hosting is always free." Cloud exists so you do not have to operate the container; the meter exists so the margin scales with your success.

PostHog looks cheaper at first glance because the free tier is genuinely generous: 1 million product-analytics events per month free, no credit card. Past that, pay-as-you-go is roughly $0.000248 per event. That is where the math turns:

Monthly eventsPlausible Cloud (Starter)Plausible Cloud (Business)PostHog (pay-as-you-go)GA4Self-hosted marginal
100K$19$39$0 (inside free tier)$0~$0.02 in disk
1M$69$139$0 (free tier cap)$0~$0.15 in disk
10M$169$339~$2,232 (9M billable × $0.000248)$0~$0.60 in disk

PostHog's meter is honest — you pay for what you use, and for teams doing serious product analytics with custom events the feature set justifies it. But the shape tells the same story: your bill grows with every capture() call. GA4 is "free" in dollars and expensive everywhere else: sampled data beyond 10M events, data residency you do not control, and a consent banner that directly reduces the accuracy of the numbers you are paying to collect.

Put differently: SaaS analytics turns a database write into a line item. Self-hosting keeps it a database write.

What 10 million events actually costs when the database is yours

Umami's footprint is almost boring, which is the point. The project ships as a single Node.js container (Next.js + Prisma) and speaks Postgres or MySQL. The hosted default and the recommended self-host path is Postgres.

Resources. Umami idles around 150 to 250 MB of RAM and well under 0.2 vCPU. Plausible CE is heavier — Elixir plus ClickHouse plus a small Postgres — typically 350 to 500 MB and 0.2 to 0.4 vCPU — but the order of magnitude is the same: a fraction of one small VM. Neither needs a dedicated node.

Disk. This is the number that replaces the pricing tier. A Postgres row for an Umami pageview carries a URL, referrer, timestamp, session hash, and a handful of small dimensions. With indexes and TOAST compression it lands around 120 to 200 bytes per event in steady state. A ClickHouse row for Plausible CE is even smaller. So:

  • 100K events: ~15 to 20 MB. A rounding error.
  • 1M events: ~150 to 200 MB. Smaller than one Docker image layer.
  • 10M events: ~1.2 to 2.0 GB. Roughly the size of a single Postgres base backup.

Retention is where operators win back more. Both Umami and Plausible let you set a rolling window — keep 6 months hot, archive the rest to cheap object storage — and Postgres partitioning makes "drop last quarter" an instant DROP TABLE rather than a row-by-row delete. At Hetzner's included NVMe (40 GB on a CPX11 at ~€7 per month, 80 GB on a CPX21 at ~€13) or at object-storage rates of ~€0.012 per GB per month, the marginal storage bill for that 10M-event month is:

  • On the box you already pay for: effectively $0. The 1.5 GB lives inside the NVMe you already provisioned for your apps. Your marginal invoice does not move.
  • Counted at replacement cost: 1.5 GB × €0.04 per GB per month (conservative block-volume rate) = €0.06, about $0.07. Even at 5 GB with generous indexes and 90 days retained, you are under $0.70 a month.
  • Backed up: a daily pg_dump compressed to ~300 MB pushed to Hetzner Object Storage or Backblaze B2 at $0.006 per GB = $0.002 per backup. Keep seven dailies and a monthly snapshot and you are still counting cents.

The delta at 10M is therefore not subtle: $169 to $339 on Plausible Cloud (and more on PostHog if you emit custom events) versus well under $1 in marginal disk on a fleet you already own. At 1M the ratio is already 400-to-1. At 100K the self-host incremental cost is unmeasurable on the invoice.

You did not avoid a bill. You moved the cost from a per-event meter someone else controls to a disk counter you own.

The GDPR and data-ownership delta that price tables do not capture

The cost comparison alone would be enough for many teams, but analytics pricing hides a second tax that never appears on the invoice: consent loss.

Umami and Plausible CE are cookieless by design. No cookies, no fingerprinting, no cross-site tracking. The Cloud FAQ is explicit: Umami "does not use cookies, does not collect personal data, and does not track users across websites. All data is anonymized." That sentence has a direct financial consequence under EU law.

Since the Austrian DSB, French CNIL, and Italian Garante rulings in 2022 through 2024 that found GA's US data transfers non-compliant, any team serving EU visitors with Google Analytics has two bad options: show a consent banner and accept that 30 to 60 percent of visitors decline (measured opt-out rates for analytics cookies on first visit), or risk enforcement. Plausible and Umami avoid the dilemma entirely because there is no cookie to consent to. No banner means no consent gap, which means the numbers on your dashboard are actually the numbers — not the 40 to 70 percent of visitors who happened to click Accept.

The other advantages compound from the same first-party posture:

  • Data residency by default. The database sits in your Postgres on your Hetzner or EU provider, not in a US vendor's warehouse.
  • No sampling. GA4 starts sampling above 10M events. A self-hosted Postgres or ClickHouse answers the exact query you asked.
  • No third-party breach surface. The script is yours (analytics.yourdomain.com via a reverse proxy), not a vendor domain on every page. The HN post that kicked this off used Umami this way inside an iOS app's web views.
  • Full export with no negotiation. Umami's FAQ promises export via API or dashboard, no lock-in. Plausible CE goes further: the data is literally in your Postgres or ClickHouse — pg_dump is the export. A SaaS exit strategy is a support ticket; a self-hosted exit strategy is a connection string.

None of this shows up in the "$9 for 10K" pricing slider, but it determines whether the "$9" number actually buys you accurate, compliant analytics for EU traffic. For many teams the banner alone is reason enough to self-host; the fact that it also collapses the bill is a second win on the same move.

Running it as a tenant workload on the same boxes as your apps

The reason Umami and Plausible CE fit the "tenant workload" framing — and not the "run a separate analytics stack" framing — is that their topology is indistinguishable from any other small web app you already operate. There is no separate analytics control plane to learn.

Architecture on a Bex or Cluster API fleet:

  • One container for the app (Umami ghcr.io/umami-software/umami:postgresql-latest or Plausible plausible/analytics).
  • One Postgres database (shared cluster with a dedicated umami database, or a small postgres:16-alpine sibling). Umami needs exactly one database; Plausible CE adds ClickHouse for event storage but ClickHouse itself is one more container.
  • One ingress rule and one TLS certificate (analytics.yourdomain.com via Gateway API or Ingress).
  • One cron for backups (pg_dump | gzip | s3 cp) and one Grafana panel for pg_database_size and container restarts.

That is the same primitive set as your marketing site, your docs, and your API. On Bex it looks like any other tenant:

yaml
# bex.yml — analytics as just another app
apps:
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    env:
      DATABASE_URL: ${UMAMI_DATABASE_URL}
      TRACKER_SCRIPT_NAME: analytics
    domains: [analytics.example.com]
    resources: { memory: 256Mi, cpu: "200m" }
    healthCheck: { path: /api/heartbeat }

Five steps to a running instance:

  1. Create the database. CREATE DATABASE umami; on your existing managed Postgres, or add a small Postgres app to the same fleet.
  2. Deploy the container. Push the image reference or point bex.yml at the upstream tag. The platform builds, schedules, and wires the domain.
  3. Wire TLS. The fleet's gateway provisions the certificate for analytics.yourdomain.com the same way it does for any tenant.
  4. Add the script. One line in your site's <head>: <script defer src="https://analytics.example.com/script.js" data-website-id="..."></script>. Visitors are collected first-party; no vendor domain appears.
  5. Set retention and backup. UPDATE umami.website SET ... or the UI's retention toggle, plus a nightly pg_dump to object storage. Upgrades are an image-tag bump.

Scaling is boring in the best way. The data plane scales like any stateless web service: add a replica if p99 ingest latency rises. The database scales with retention policy, not with pricing tiers. When the 10M-event month lands, the container does not emit an overage alert — Postgres emits a slightly larger pg_database_size.

When SaaS still wins — an honest matrix

Self-hosting analytics is not always the right call, and a post that claims otherwise is selling something. Here is where the SaaS meter still makes sense:

SituationWhy SaaS winsWhat to pick
Hobby traffic under 10K eventsFree tier covers you; ops time costs more than $0 to $9 you would savePlausible or Umami Cloud free tier, or PostHog's 1M free cushion
You need managed funnels, A/B, session replayUmami does pageviews, referrers, events, and simple breakdowns; PostHog's product-analytics depth (funnels, cohorts, feature flags, replay) has no one-container equivalentPostHog Cloud — pay for the features, not just the writes
Zero ops capacity, no fleet yetIf you have no box and no one on call for Postgres backups, a $9 meter is cheaper than learning to operate a database this quarterStart on Cloud, keep the export path open, migrate the data when you grow a fleet
Enterprise audit requiring vendor SOC 2 badgeSome procurement checklists want a vendor's attestation rather than your own runbookSaaS with a contract, or host under your own SOC 2 boundary if you already have one

For everyone else — a team already running a Hetzner fleet or a Bex deployment with a Postgres nearby — the decision inverts. The hardware is already paid for, the Postgres is already backed up, and the reverse proxy already terminates TLS. Adding Umami is not a new vendor relationship. It is a new row in a routing table.

Analytics belongs beside your apps, not on a separate bill

A decade of analytics SaaS taught teams to treat pageviews as a scarce, metered resource. The default reaction to growth became "which tier are we in now" instead of "how many gigabytes did we write." Self-hosting Umami or Plausible CE reframes the question: analytics is disk and a small container, bounded by your retention policy, running on the same machines that generate the events.

Every meter a vendor adds — per-event, per-seat, per-property — is a pricing decision you will re-litigate next quarter. Every meter you own — disk, memory, retention — is a capacity decision you already made when you sized the fleet. The first scales with someone else's price list. The second scales with your workload.

If you already run a fleet, try the one-container test: deploy Umami today, point it at the Postgres next to your apps, and watch the next ten thousand pageviews add kilobytes, not line items.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Umami, Plausible CE, and your apps all run as tenant workloads on the same Cluster API fleet, with Render-compatible APIs and an MCP server your agents can call. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex