Umami
Privacy-focused web analytics without cookies: pageviews, referrers, custom events, funnels, goals, retention and boards for many websites, with optional session replays and heatmaps. The self-hosted stack is one Next.js container and PostgreSQL, which stores every event.
Resources by load level
What the app itself needs at each level, next to what we recommend for a server that runs only this app.
| Level | What it means | App requirements | Recommended |
|---|---|---|---|
| LightEstimate | Up to 5 websites and ~100,000 tracked events a month (pageviews plus custom events), a few people on the dashboard; replays and heatmaps off. | 1.5 vCPU · 1.5 GiB RAM · 7.75 GiB disk | 1.92 vCPU · 3.3 GiB RAM · 18.343 GiB disk |
| MediumEstimate | Up to 20 websites and ~1 million events a month (about 4 a second in a peak hour), a team using reports, funnels and boards daily. | 3 vCPU · 5 GiB RAM · 52.75 GiB disk | 3.72 vCPU · 7.5 GiB RAM · 70.093 GiB disk |
| HeavyEstimate | Dozens of websites and ~3 million events a month (about 12 a second in a peak hour), with long date ranges in reports and several dashboard users at once. | 3.5 vCPU · 6 GiB RAM · 52.75 GiB disk | 4.32 vCPU · 8.7 GiB RAM · 70.093 GiB disk |
Recommended = app requirements + system baseline + safety headroom, under sizing policy 2026.10.0. Options stay at their defaults. “Estimate” means the sources give no number for that case, so it is worked out from them by the method stated under Components. How the numbers are built
Ready-to-run files
Support level Supported
Compose template from the official sources; not yet verified by a run.
Components
What each component needs at each load level, before baseline and headroom.
| Component | Light | Medium | Heavy | Sources |
|---|---|---|---|---|
| Umami (dashboard, API, tracker and collector) (app) | 0.5 / 0.5 / 1.25 | 1 / 1 / 1.25 | 1.5 / 2 / 1.25 | [3][4][16][29][34] |
| PostgreSQL (database) | 1 / 1 / 6.5 | 2 / 4 / 51.5 | 2 / 4 / 51.5 | [13][23][25][36][39][40][41][42][43][44][45] |
How the estimates are worked out
- Umami (dashboard, API, tracker and collector) · Light: No upstream sizing. One Node process (start-docker.sh execs node server.js) serves the dashboard and /api/send; 100,000 events a month is ~0.04 a second on average. RAM: 0.5 GiB for Next.js plus the 63 MiB GeoLite2 City database it opens; vCPU: half a core (ANAROQ estimate). Disk: image 1,105,421,824 B unpacked (1.03 GiB), rounded up to 1.25 GiB; Umami keeps no files.
- Umami (dashboard, API, tracker and collector) · Medium: 1 million events a month is ~0.4 a second on average and ~4 at a 10× peak, each a small insert plus a GeoIP lookup, while dashboard reports run in PostgreSQL. One full core and 1 GiB RAM for the single Node process (ANAROQ estimate). Disk: the 1.25 GiB image only.
- Umami (dashboard, API, tracker and collector) · Heavy: ~1.2 events a second on average and ~12 at a 10× peak. 1.5 vCPU: one busy event loop plus GC and I/O threads, about what a single Node process can use; 2 GiB RAM for concurrent report responses (ANAROQ estimate). The docs suggest Redis for high traffic; the template runs without it. Disk: the 1.25 GiB image only.
- PostgreSQL · Light: Per event: a website_event row (~250 B: four UUIDs, a timestamp, path, referrer, title, hostname) plus its 14 B-tree indexes (~600 B) and a share of the session row and its 11 indexes (~150 B), about 1 KB (ANAROQ estimate from prisma/schema.prisma). Self-hosted data is kept indefinitely. 100,000 events a month add ~0.1 GB, so the sidecar's 5 GB lasts about 4 years. @prisma/adapter-pg uses a node-postgres pool, 10 connections by default. Light preset method: shared_buffers 25% of RAM, 0.5 GiB image + 5 GiB data + 1 GiB WAL.
- PostgreSQL · Medium: ~1 KB per event with its indexes (ANAROQ estimate from prisma/schema.prisma, method in the light preset), kept indefinitely. 1 million events a month add ~1 GB, past the sidecar's 5 GB within 5 months, so this is the PostgreSQL profile's medium preset (≤ 50 GB data, about 4 years). @prisma/adapter-pg uses a node-postgres pool, 10 connections by default. Medium preset method: shared_buffers 1 GiB = 25% of 4 GiB, 0.5 GiB image + 50 GiB data + 1 GiB WAL.
- PostgreSQL · Heavy: ~1 KB per event with its indexes (ANAROQ estimate from prisma/schema.prisma, method in the light preset), kept indefinitely. 3 million events a month add ~3 GB, filling the medium preset's 50 GB in about 16 months; plan more disk from ops.storageGrowth or delete old data. @prisma/adapter-pg uses a node-postgres pool, 10 connections by default. Medium preset method: shared_buffers 1 GiB = 25% of 4 GiB, 0.5 GiB image + 50 GiB data + 1 GiB WAL.
Ports
Public ports are meant to be reached from outside the stack, usually through a reverse proxy; internal ones stay on the stack's own network.
User and permissions (UID/GID)
The user each container's process runs as, and the paths it writes to. A bind mount for one of these paths must be writable by that UID:GID.
| Component | UID:GID | User | Starts as root | Writes to | Sources |
|---|---|---|---|---|---|
| Umami (dashboard, API, tracker and collector)Derived | 1001:65533 | nextjs | No | — | [3][9][10] |
| PostgreSQL | 999:999 | postgres | Yes, prepares its directories, then drops to this user | /var/lib/postgresql/var/run/postgresql | [37][38][44] |
- Umami (dashboard, API, tracker and collector): The Dockerfile ends with USER nextjs (uid 1001, created with adduser --system, so its primary group is nogroup, 65533). Nothing persistent is written: all state is in PostgreSQL. Only COLLECT_API_ENDPOINT makes the start script rewrite /app/public/script.js, which nextjs owns.
Storage growth
How much each component's data grows per month at each load level, on top of the disk in the profile. A value marked Assumption is ANAROQ's stated assumption, not a figure from the sources; the notes say what it rests on.
What the growth figures rest on
- PostgreSQL: 100,000, 1 million and 3 million events a month × an assumed ~1 KB per event with its indexes. Self-hosted Umami deletes nothing on its own, so this adds up every month. Session replays (off by default) come on top and are kept for 30 days.
Upgrade and rollback
How to move to a new version safely, and how to go back if it fails.
Back up first
Migrations run on their own at start and cannot be undone: first take a pg_dump of the umami database and keep a copy of the .env (APP_SECRET, TWO_FACTOR_ENCRYPTION_KEY, the database password). [12][22]DerivedThe upgrade page has no backup step; it follows from check-db.js applying prisma migrate deploy on every start, with no down migrations.
Upgrade steps
- Read the release notes and the blog post of every version between yours and the new one; breaking changes such as v3's removal of MySQL are announced there. [12][19]
- Change the pinned umami tag, then run docker compose pull and docker compose up -d. On start, check-db.js runs prisma migrate deploy before the server listens; follow docker compose logs -f umami until it is up. [4][12][22]
- After a major upgrade run ANALYZE in the database (docker compose exec postgres psql -U umami umami) so dashboard queries get fresh planner statistics. It is safe on a live database. [12]
Roll back
Restore the database dump from before the upgrade and pin the image back to the previous tag. Both are needed: an older release does not know a schema that a newer migration changed. [22][35]DerivedUpstream documents no rollback; this follows from forward-only Prisma migrations and the pinned semver tags on GHCR.
Known breaking changes
- In 3.3.0: Two-factor authentication arrived and needs TWO_FACTOR_ENCRYPTION_KEY (64 hex characters) before anyone can enable it. The image now starts with sh scripts/start-docker.sh instead of npm run start-docker, so a custom command override must follow. [14][20][32][33]
- In 3.0.0: MySQL is no longer supported, only PostgreSQL; MySQL users migrate with the official guide first. The image tags changed from postgresql-v2.x / postgresql-latest to plain 3.x tags, and the schema migrations call for ANALYZE afterwards. [12][19][31][35]
Limits
- Self-hosted Umami keeps every event indefinitely and has no retention setting, so PostgreSQL only grows; plan disk from the growth figures or delete old data yourself. PostgreSQL is the only database (12.14 or newer); MySQL support ended in v3. [8][13][19]
- Session replays (per website, 15% of sessions by default, up to 5 minutes each, kept 30 days) are stored in PostgreSQL too; the presets assume they are off. Redis is optional and not in the template: the docs suggest it for high traffic or several Umami instances. [15][16]
- Visitor location comes from the GeoLite2 City database built into the image (about 66 MB), so it is only as fresh as the image; behind a CDN that sends location headers, those are used first. [10][29]
Security
- The first start creates an administrator admin with the password umami. Sign in and change it right away, before the dashboard is reachable from the internet, then turn on two-factor authentication. [8][11][24]
- Umami takes the visitor IP from CLIENT_IP_HEADER if set, else from the first of a list of headers (True-Client-IP, CF-Connecting-IP, X-Real-IP, ...) before X-Forwarded-For, which a visitor can send themselves. Set CLIENT_IP_HEADER to the header your proxy overwrites; it affects location and IGNORE_IP. [7][27]
- The dashboard loads an anonymous telemetry pixel from i.umami.is and checks api.umami.is for new versions. DISABLE_TELEMETRY=1 and DISABLE_UPDATES=1 turn these off; PRIVATE_MODE=1 blocks every external call, website icons included. [7][28]
Backup
- Everything lives in PostgreSQL (users, websites, events, reports, replays), so a pg_dump of the umami database plus the .env is the whole backup; the Umami container holds no data. [2][3][23]
- Keep APP_SECRET with the dump: a new value signs everyone out and derives different visitor and session ids, so returning visitors look new. Without the old TWO_FACTOR_ENCRYPTION_KEY every user must enroll in 2FA again. [7][14][26]
Latest release
- This profile was checked against
Umami 3.4.0
This profile was checked against the newest release.
- Checked on
- 14 Oct 2026
- State
- Checked within the last 90 days
- Where releases are published
- GitHub Releases
Version
- Profile status
- Published
- Checked against release
- Umami 3.4.0
- Profile version
- 1.0.0
- Figures verified
- 14 Oct 2026
- Catalog release
- 2026.10.10
- Sizing policy
- 2026.10.0
- Compose file
- Unverified
Project and alternatives
- Official site
- umami.is
- Source code
- github.com/umami-software/umami
- Alternatives
- Plausible Community EditionMatomoGoatCounter
Sources
- Docker Docs: Control startup order in Compose (pg_isready healthcheck, service_healthy) (Official docs · read 11 Oct 2026)
- umami repository: docker-compose.yml at v3.4.0 (umami:latest on 3000, init: true, curl /api/heartbeat healthcheck every 5s; postgres:15-alpine with umami/umami and /var/lib/postgresql/data) (Official Compose · read 14 Oct 2026)
- umami repository: Dockerfile at v3.4.0 (node:22-alpine, apk add curl, user nextjs uid 1001, USER nextjs, EXPOSE 3000, HOSTNAME 0.0.0.0, CMD sh scripts/start-docker.sh, no HEALTHCHECK) (Official repository · read 14 Oct 2026)
- umami repository: scripts/start-docker.sh at v3.4.0 (check-db.js, update-tracker.js, then exec node server.js so it is PID 1) (Official repository · read 14 Oct 2026)
- umami repository: src/app/api/heartbeat/route.ts at v3.4.0 (GET /api/heartbeat returns {ok: true}) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/two-factor/crypto.ts at v3.4.0 (TWO_FACTOR_ENCRYPTION_KEY must match /^[0-9a-fA-F]{64}$/, AES-256-GCM) (Official repository · read 14 Oct 2026)
- Umami Docs: Environment variables (DATABASE_URL the only required one; APP_SECRET and TWO_FACTOR_ENCRYPTION_KEY from openssl rand -hex 32; CLIENT_IP_HEADER, DISABLE_TELEMETRY, PRIVATE_MODE) (Official docs · read 14 Oct 2026)
- Umami Docs: Installation (PostgreSQL minimum v12.14, UTC recommended; Docker Compose with the app and PostgreSQL on port 3000; default login admin / umami) (Official docs · read 14 Oct 2026)
- GHCR image config of ghcr.io/umami-software/umami:3.4.0 (linux/amd64): User nextjs, ExposedPorts 3000/tcp, Entrypoint docker-entrypoint.sh, no Healthcheck (Official repository · read 14 Oct 2026)
- Layers of ghcr.io/umami-software/umami:3.4.0 unpacked: nextjs is 1001:65533 (nogroup), /app/geo/GeoLite2-City.mmdb 65,921,355 B, Next 16.3.4 start-server handles SIGTERM (Measurement · read 14 Oct 2026)
- Umami Docs: Login (default administrator admin / umami; change the password first, then enable 2FA) (Official docs · read 14 Oct 2026)
- Umami Docs: Getting updates (docker compose pull, docker compose up --force-recreate -d; run ANALYZE after a major upgrade such as v3) (Official docs · read 14 Oct 2026)
- Umami Docs: FAQ (PostgreSQL minimum v12.14 only; self-hosted data is retained indefinitely unless you delete it; no cookies) (Official docs · read 14 Oct 2026)
- Umami Docs: Two-factor authentication (since v3.3.0; needs a 64-hex TWO_FACTOR_ENCRYPTION_KEY; losing it forces re-enrolment) (Official docs · read 14 Oct 2026)
- Umami Docs: Replays (since v3.1.0, per-website toggle, sample rate 0.15, up to 5 min per recording, stored for 30 days) (Official docs · read 14 Oct 2026)
- Umami Docs: Enable Redis (optional cache and server-side sessions via REDIS_URL, for high traffic or several instances) (Official docs · read 14 Oct 2026)
- Umami Docs: Cloud overview (Umami Cloud is the hosted version run by the creators of Umami; same analytics features as self-hosted) (Official docs · read 14 Oct 2026)
- Umami: Pricing (Umami Cloud plans by monthly events, websites and data retention) (Official docs · read 14 Oct 2026)
- Umami blog: Umami v3 (breaking change: MySQL is no longer supported, only PostgreSQL; migration guide) (Release notes · read 14 Oct 2026)
- Umami blog: Umami v3.3 (two-factor authentication for self-hosters; Docker image hardened and slimmed down) (Release notes · read 14 Oct 2026)
- Umami blog: Umami v3.4 (annotations, MCP endpoint off by default on self-hosted via MCP_ENABLED, account API keys) (Release notes · read 14 Oct 2026)
- umami repository: scripts/check-db.js at v3.4.0 (refuses PostgreSQL below 9.4.0, then runs prisma migrate deploy unless SKIP_DB_MIGRATION) (Official repository · read 14 Oct 2026)
- umami repository: prisma/schema.prisma at v3.4.0 (website_event with 14 indexes, session with 11; PostgreSQL datasource) (Official repository · read 14 Oct 2026)
- umami repository: prisma/migrations/01_init/migration.sql at v3.4.0 (inserts the admin user with role admin and a bcrypt hash) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/prisma.ts at v3.4.0 (one PrismaPg adapter on DATABASE_URL, optional DATABASE_REPLICA_URL) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/crypto.ts at v3.4.0 (secret() hashes APP_SECRET, or DATABASE_URL when unset; uuid() derives ids from it) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/ip.ts at v3.4.0 (CLIENT_IP_HEADER first, else true-client-ip, cf-connecting-ip, ... x-real-ip, then x-forwarded-for) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/constants.ts at v3.4.0 (UPDATES_URL api.umami.is/v1/updates, TELEMETRY_PIXEL i.umami.is/a.png) (Official repository · read 14 Oct 2026)
- umami repository: src/lib/detect.ts at v3.4.0 (location from CDN headers, else the local GeoLite2-City.mmdb opened with maxmind) (Official repository · read 14 Oct 2026)
- umami repository: LICENSE at v3.4.0 (MIT, Umami Software, Inc.) (Official repository · read 14 Oct 2026)
- umami repository: docker-compose.yml at v2.20.2 (image umami:postgresql-latest, DATABASE_TYPE postgresql) (Official Compose · read 14 Oct 2026)
- umami repository: Dockerfile at v3.2.0 (CMD ["npm", "run", "start-docker"]) (Official repository · read 14 Oct 2026)
- umami repository: Dockerfile at v3.3.0 (CMD ["sh", "scripts/start-docker.sh"]) (Official repository · read 14 Oct 2026)
- GHCR manifest of ghcr.io/umami-software/umami:3.4.0 (linux/amd64): 322,703,350 B compressed in 19 layers; layers decompressed and counted: 1,105,421,824 B (Measurement · read 14 Oct 2026)
- GHCR tags of ghcr.io/umami-software/umami (newest release 3.4.0, same digest as latest; postgresql-* tags end at 3.0.0, plain 3.x tags since) (Official repository · read 14 Oct 2026)
- node-postgres Docs: pg.Pool (max clients in the pool: 10 by default) (Official docs · read 14 Oct 2026)
- docker-library/postgres: 18/trixie Dockerfile (postgres uid/gid 999, VOLUME /var/lib/postgresql) (Official repository · read 14 Oct 2026)
- docker-library/postgres: 18/trixie docker-entrypoint.sh (chown PGDATA, then gosu postgres) (Official repository · read 14 Oct 2026)
- PostgreSQL 18 documentation: 19.4 Resource Consumption (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.3 Connections and Authentication (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.10 Vacuuming (autovacuum settings) (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 19.5 Write Ahead Log (max_wal_size) (Official docs · read 10 Oct 2026)
- PostgreSQL 18 documentation: 51.2 How Connections Are Established (Official docs · read 10 Oct 2026)
- Docker Official Image postgres: documentation (docker-library/docs) (Official docs · read 10 Oct 2026)
- Docker Hub: library/postgres tag 18.6 (compressed image sizes) (Official repository · read 10 Oct 2026)