Umami's build writes to your database, and the compose file ships a public two-factor key
GitHub describes it as Umami is a privacy-first analytics platform. Traffic, campaigns, behavior, conversions, and revenue in one place , no cookies, no surveillance, self-hosted or in the cloud.. The repository metadata lists TypeScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- Umami is an MIT-licensed TypeScript analytics platform you run yourself on Node.js and PostgreSQL, or pull as a container. The code is well organized, but the install path has three rough edges worth knowing before the first login: the build creates schema and a default account, the compose file ships placeholder secrets, and the local and Docker build pipelines are not the same.
- Who is it for?
- Umami fits a team that wants cookie-free analytics on infrastructure it controls and is comfortable owning a PostgreSQL instance, a proxy, and a reverse proxy configuration. It does not fit a team that needs turnkey hosting or an agent-facing data surface on day one, since MCP is off until you enable it and mint a key.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two registries ship the same application
The install notes and the repository root disagree about where the image lives. The getting started text uses `docker pull docker.umami.is/umami-software/umami:latest`, and the compose file in the repository root points its umami service at `ghcr.io/umami-software/umami:latest`. Both tags are `:latest`, so they are not guaranteed to be the same bytes, and neither name appears in the other place. What this cannot tell you is which registry is canonical for a pinned upgrade, because the compose file is the only place the ghcr name appears. The repository is not archived and its last push is dated 2026-09-29, with v3.4.0 published on 2026-09-17. The practical consequence is that a team standardizing on one registry should read the compose file before the docs and pin a digest instead of the moving tag.
The compose file ships a placeholder two-factor key
docker-compose.yml sets `TWO_FACTOR_ENCRYPTION_KEY: replace-me-with-a-64-character-hex-string` and `APP_SECRET: replace-me-with-a-random-string`, both as literal values, and the comment above the key points at `openssl rand -hex 32`. Two-factor authentication is unavailable and cannot be required until a real key is set, so the shipped default is the least safe case: the feature stays off and the string meant to protect it is a public placeholder. Running `docker compose up -d` without editing the file gives a working instance with a known secret and no second factor. The consequence is that the opening hours of any evaluation happen on credentials that live in the repository, and the rotation belongs before the first login rather than after it.
pnpm is pinned to one exact version and Node differs between paths
The source path is three commands, and the package manager is the strict part of it:
git clone https://github.com/umami-software/umami.git
cd umami
pnpm installpackage.json declares an engines entry for a single pnpm release, 12.3.4, and the Dockerfile carries the same number as a build argument next to PRISMA_VERSION 7.10.0 and NODE_IMAGE_VERSION 22-alpine. The documented requirements are looser than the image, a server with Node.js version 18.18+ and a PostgreSQL database version v12.14+, so the container runs Node 22 while the source path accepts 18.18. The dependency stage also appends `strictDepBuilds: false` to the workspace file before a frozen install, and the workspace packages `@umami/api-client` and `@umami/mcp` have to be present for that install to succeed. A lockfile mismatch or a different pnpm stops the build early.
The build step creates tables and an admin/umami account
`pnpm run build` is not only a compile. It creates tables in your database if you are installing for the first time, and it creates a login user with username `admin` and password `umami`. What this cannot do is stay out of the database, so a build pointed at the wrong `DATABASE_URL` writes schema there, and the connection string in the `.env` file is the only thing steering it. The build chain runs an env check, database client generation, a database check, the tracker, the recorder, geo, openapi, packages, and the app. The consequence is that first-run provisioning is entangled with compilation, and the default credential pair is printed in the documentation, so changing it is a day-one task rather than a hardening task.
build and build:docker are two different pipelines
The local `build` script opens with an env check, and `build:docker` does not. Inside the image the build runs against a placeholder connection string, `postgresql://user:pass@localhost:5432/dummy`, together with `NEXT_TELEMETRY_DISABLED=1`, so that check has no real database in front of it. Startup diverges the same way: `start` is `next start`, while `start:docker` chains a database check, a tracker update, and the server. What the two paths cannot share is the tracker refresh, which the container performs at boot and the source path does not. The consequence is that the documented update routine for a source install, `git pull`, `pnpm install`, `pnpm build`, omits a step the container runs automatically, and the difference is visible only in package.json.
The tracker, the recorder, and the geo data are separate artifacts
The tracking script and the session recorder are not folded into the Next.js app build. They have their own rollup configs, `rollup.tracker.config.js` and `rollup.recorder.config.js`, their own TypeScript projects `tsconfig.tracker.json` and `tsconfig.tracker.types.json`, a separate declaration emit, and a `check:tracker` pass that compiles with `--noEmit` before the bundle is written. Geo data is a third piece, produced by `build:geo` and skippable through the `SKIP_BUILD_GEO` build argument, and country and language files come from a separate download step. What the app build cannot absorb is any of these, so a change to tracking touches several configs and two type projects. That surface is the cost of shipping the tracker as its own artifact.
Port 3000, a heartbeat probe, and an MCP endpoint that stays off
`pnpm run start` launches on `http://localhost:3000`, and the notes are explicit that you must either proxy requests from your web server or change the port to serve the application directly. The compose file publishes the same port and adds a healthcheck that curls `http://localhost:3000/api/heartbeat` every five seconds, with the database held behind a service_healthy condition. MCP is the opposite: it is disabled by default, and setting `MCP_ENABLED=1` only exposes the `/mcp` endpoint, which still needs an API key generated under Settings. What the app cannot do is hand an agent a data surface before you create that key in the interface. The repository also ships podman, Vercel, Netlify, and app.json files, so the deployment target is a choice.
Editorial conclusion
Umami fits a team that wants cookie-free analytics on infrastructure it controls and is comfortable owning a PostgreSQL instance, a proxy, and a reverse proxy configuration. It does not fit a team that needs turnkey hosting or an agent-facing data surface on day one, since MCP is off until you enable it and mint a key. Before you start, replace both placeholders in docker-compose.yml, set APP_SECRET and TWO_FACTOR_ENCRYPTION_KEY, change the admin/umami account the build creates, and confirm your pnpm matches the 12.3.4 the image pins.
Frequently asked questions
how to install umami
From source you need a server with Node.js version 18.18+ and a PostgreSQL database version v12.14+, then clone the repository, run `pnpm install`, put `DATABASE_URL` in an `.env` file, and run `pnpm run build` followed by `pnpm run start`. With Docker, `docker compose up -d` starts Umami together with a PostgreSQL database.
Does Umami track visitors with cookies?
It describes itself as a privacy-first analytics platform with no cookies and no surveillance, covering traffic, campaigns, behavior, conversions, and revenue in one place. It is MIT licensed and can be self-hosted or used in the cloud.
What are the default Umami login credentials?
The build step creates a login user with username `admin` and password `umami` the first time it runs. That pair is written into the documentation, so it should be changed before the instance is reachable by anyone else.
How do I turn on two-factor authentication in Umami?
Set `TWO_FACTOR_ENCRYPTION_KEY` to a 64-character hex string, generated with `openssl rand -hex 32`. Until that key is set, two-factor authentication is unavailable and cannot be required.
Can an AI agent read Umami data through MCP?
MCP is disabled by default. Set `MCP_ENABLED=1` to enable the `/mcp` endpoint, then authenticate with an API key generated under Settings in the API keys section.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/umami-software-umami)