Skip to content
$_ setuptracking
Recipes

Umami on Vercel + Neon: Free Self-Hosted Analytics in 5 Minutes

6 min read
Light blue flat illustration of monitor with cloud sync icon phone and database stack

Goal: a fully working Umami install in under 5 minutes, deployed to Vercel’s free tier with a Postgres database from Neon (also free), wired to your custom subdomain. Total cost at any reasonable indie scale: €0/mo.

This is the absolute lowest-friction self-host setup in 2026. Zero servers, zero Docker, zero cron, zero TLS config — Vercel handles all of it. The catch: you’re trusting two SaaS providers (Vercel + Neon) for hosting, but the data and code remain yours. If either disappears, you redeploy elsewhere in 5 minutes from the same git repo. Tested 2026-05-02 on Vercel Hobby + Neon Free.

Why Umami (and the trade-offs)

Umami is the smallest cookieless tracker in the field — a 21KB-or-less script, a clean React dashboard, MIT licensed, written in TypeScript by a single maintainer. It’s perfect for personal sites, side projects, and indie SaaS that want pageviews + sources + browser/OS/country breakdowns without any of the enterprise baggage.

What you give up vs Plausible:

  • No EU-only hosting guarantee (Vercel + Neon US-default unless you pay for EU edge)
  • Less polished docs / smaller community
  • No event funnels (just custom events as raw counts)

What you give up vs Matomo: basically everything except pageviews. If you need goals, ecommerce, attribution — go straight to Matomo.

Step 1 — Create a Neon Postgres database (1 minute)

  1. Sign up at neon.tech with email or GitHub.
  2. Create new project → Postgres 16 → region: pick EU Frankfurt if you want EU data residency (GDPR), US East for cheaper Vercel routing.
  3. Project name: umami. Default branch: main.
  4. Once created, go to Connection Details → copy the postgres:// connection string. It looks like:
    postgres://username:[email protected]/neondb?sslmode=require

Save it somewhere — we’ll paste into Vercel in 30 seconds.

Read:  PostHog on Hetzner: Self-Host Product Analytics with Docker (€16.41/mo, 45 min)

Free tier: 0.5 GB storage, 191 compute hours/month — enough for any indie site with <1M events/mo. Above that, $19/mo Pro plan or migrate to a CX22 + self-hosted Postgres.

Step 2 — Fork Umami on GitHub

Go to github.com/umami-software/umami → click Fork. You’ll get github.com/your-username/umami.

(Why fork instead of clone? You’ll want to pull upstream updates and re-deploy. Forking gives you a controllable git history.)

Step 3 — Deploy to Vercel (90 seconds)

  1. Go to vercel.com/new, log in with GitHub.
  2. Import your forked umami repo.
  3. Framework preset: Vercel auto-detects Next.js. Don’t change anything.
  4. Environment Variables — add two:
    DATABASE_URL = 
    HASH_SALT    = 
  5. Click Deploy.

Vercel builds (Next.js Prisma migrate + Next build) for ~2 minutes. When it finishes, you have a working Umami at your-umami-xxxx.vercel.app.

Step 4 — First login + add your custom domain

Visit the Vercel URL. Default credentials:

Username: admin
Password: umami

Log in immediately. Settings → Profile → change password. Don’t skip this — those default creds are publicly known.

Now wire your subdomain (so events look like first-party):

  1. Vercel Project → Settings → Domains → Add → analytics.your-site.com.
  2. Vercel shows the DNS records you need. Two options:
    • CNAME (preferred for subdomain): analyticscname.vercel-dns.com
    • A record (apex domain): 76.76.21.21
  3. Add the record at your DNS provider. TLS is auto-issued (Vercel uses Let’s Encrypt under the hood).

Within 1–2 minutes, https://analytics.your-site.com serves your Umami dashboard. The Vercel URL keeps working too — both point at the same deployment.

Step 5 — Add a website + paste the snippet

  1. Umami dashboard → Settings → Websites → Add website.
  2. Name + domain. Save.
  3. Click the website → Edit → Tracking code → copy the <script> snippet.
  4. Paste into the <head> of your tracked site, OR if you want first-party tracking (avoids ad-blockers), use:
    <script defer
      src="https://analytics.your-site.com/script.js"
      data-website-id="YOUR-UUID-HERE"></script>

First pageview should appear in the dashboard within 30 seconds.

What just happened (mechanism)

  • Umami app — a Next.js 14 server-rendered app deployed as Vercel Functions. The dashboard is React; the /api/send ingest endpoint is a serverless function that writes to Postgres.
  • Neon Postgres — your event store. Two tables you’ll care about: website_event (every event) and session (deduplicated sessions, salted-hash-based). Auto-suspends after 5 min idle to save free-tier compute.
  • Vercel Edge Network — serves the static dashboard JS/CSS from CDN, routes /api/send requests to the Functions runtime in your selected region.
  • HASH_SALT — server-side salt for the daily-rotating visitor hash. Unique sessions are computed as hash(IP + User-Agent + website-id + salt + day). Salt rotation is tied to the env var; we don’t rotate ours daily, so your “unique visitor” count is stable per HASH_SALT lifetime.

Cookies set on visitors: zero. Same legitimate-interest GDPR basis as Plausible — no consent banner needed.

The free-tier limits (real numbers)

Service Free includes You’ll hit limit at
Vercel Hobby 100 GB bandwidth, 100k function invocations ~3M events/mo (each event is one function call)
Neon Free 0.5 GB storage, 191 compute hrs ~1M events stored (then need to prune)
Combined practical limit ~500k events/mo before you start watching

Above that point, you have three escalation paths:

  1. Vercel Pro ($20/mo) + Neon Pro ($19/mo) = $39/mo and ride the SaaS curve.
  2. Move just the database to Hetzner ($5/mo VPS with self-hosted Postgres) and keep Vercel Hobby. Total: $5/mo.
  3. Move both to a self-hosted box. Same Hetzner CX22 can run Umami too.

Step 6 — Auto-prune old data (so you stay free)

The cheapest way to stay under Neon’s 0.5 GB: prune events older than N days. Umami doesn’t ship a cron — but Vercel Cron Jobs are free and can call any endpoint.

Add a file to your fork: app/api/cron/prune/route.ts:

import { headers } from 'next/headers';
import prisma from '@/lib/prisma';

export async function GET() {
  const auth = (await headers()).get('authorization');
  if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('unauthorized', { status: 401 });
  }
  const cutoff = new Date(Date.now() - 365 * 86400 * 1000);
  await prisma.websiteEvent.deleteMany({ where: { createdAt: { lt: cutoff } } });
  return Response.json({ pruned_before: cutoff.toISOString() });
}

Add a vercel.json:

{
  "crons": [{ "path": "/api/cron/prune", "schedule": "0 4 * * 0" }]
}

Add env var CRON_SECRET in Vercel (any random string). Push, redeploy. Now every Sunday at 04:00 UTC, events older than 365 days are deleted. Adjust cutoff to your retention policy.

Updating Umami (every 4–6 weeks)

Umami releases roughly monthly:

  1. In your fork on GitHub: Sync fork → Update from upstream → Discard my changes if not customizing.
  2. Vercel auto-deploys when main branch updates. Watch the deployment for migration errors.
  3. Check Umami changelog for breaking schema changes.

Total time per update: ~1 minute.

3-year cost

Item 3-year total
Vercel Hobby €0
Neon Free €0
Domain (subdomain — no extra) €0
Total €0

Yes, really €0 if you stay under ~500k events/mo. The closest commercial alternative (Plausible Cloud Starter at €9/mo) is €324 over 3 years. Umami on Vercel is the price-per-feature king for indie scale.

When NOT to use this setup

  • You exceed 1M events/mo regularly. The free tiers won’t sustain you and the Vercel+Neon Pro stack ($39/mo) is now more than Plausible Cloud Starter. Switch.
  • You need goals/funnels/attribution. Umami has custom events but no funnel builder. Matomo is a better fit.
  • EU-only data residency required. Vercel’s free tier doesn’t pin to EU regions. Pick Neon EU Frankfurt at minimum, but Vercel Functions still execute globally on Hobby. Self-host on Hetzner instead.
  • You’re skeptical of vendor lock. While Umami code is portable, your specific deployment depends on Vercel + Neon staying in business. Self-hosting on a VPS removes both dependencies.

Troubleshooting

Build fails with “P3009: migrate found failed migrations”. Neon went into “branch was reset” state. In Neon Console → Branches → Reset branch → main. Then redeploy on Vercel.

Default password “umami” still works after I changed it. The login page caches credentials per-deployment. Hard refresh (Ctrl+Shift+R), incognito, then re-login.

Events not appearing in dashboard but script loads. Browser ad-blocker. Open DevTools → Network → look for send request. If blocked, deploy under your own subdomain (analytics.your-site.com) so the request is first-party — uBlock Origin won’t block it.

Compute hours warning from Neon. Auto-suspend isn’t kicking in. Settings → Compute → enable auto-suspend at 5 min. Reduces idle hours dramatically.

Next


Found this useful?

Try the Stack Picker to get a personal recommendation, or browse the install recipe library.