Skip to content
appsgit

Deploy guide

How to self-host Umami with Docker Compose

Deploy Umami web analytics with Docker Compose and Postgres in 10 minutes: APP_SECRET, default login, HTTPS reverse proxy, tracking code, backups and upgrades.

  • Updated
  • Beginner
  • About 10 minutes

You will need

  • 1 vCPU / 1 GB RAM
  • Docker + Docker Compose v2
  • A domain name for the tracking script

What is Umami?

Umami is a simple, fast and privacy-focused web analytics platform, often used as a Google Analytics alternative. It is open source under the MIT license, collects no personal data, sets no cookies and has a clean dashboard for pageviews, referrers, devices, locations and custom events. One instance can track any number of websites.

Requirements

  • A Linux server with 1 vCPU and 1 GB of RAM.
  • Docker Engine and Docker Compose v2.
  • A domain name such as stats.example.com, so the tracking script loads over HTTPS.

Step 1: Prepare the server

This guide assumes Ubuntu 24.04 with Docker installed. If you need Docker, follow the official install guide. Create a project folder:

mkdir -p ~/umami && cd ~/umami

Step 2: Create the Docker Compose file

This follows the official Compose file, which runs Umami with Postgres. Save as docker-compose.yml:

services:
  umami:
    image: ghcr.io/umami-software/umami:latest
    restart: always
    init: true
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      DATABASE_URL: postgresql://umami:${POSTGRES_PASSWORD}@db:5432/umami
      APP_SECRET: ${APP_SECRET}
      TWO_FACTOR_ENCRYPTION_KEY: ${TWO_FACTOR_ENCRYPTION_KEY}
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat"]
      interval: 10s
      timeout: 5s
      retries: 5

  db:
    image: postgres:16-alpine
    restart: always
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - umami-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U umami -d umami"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  umami-db-data:

Create .env in the same folder:

POSTGRES_PASSWORD=CHANGE_ME
APP_SECRET=CHANGE_ME
TWO_FACTOR_ENCRYPTION_KEY=CHANGE_ME

Generate each value with openssl rand -hex 32. Use hex for the database password so it is safe inside the DATABASE_URL. APP_SECRET signs login tokens, and TWO_FACTOR_ENCRYPTION_KEY (a 64-character hex string) encrypts two-factor secrets. The port is bound to 127.0.0.1 so the dashboard is only reachable through your HTTPS proxy. To pin a version instead of latest, pick a tag from the releases page.

Step 3: Start and open the app

docker compose up -d
docker compose logs -f umami

On first start Umami creates its database tables. Finish Step 4, then open https://stats.example.com, or test via an SSH tunnel with ssh -L 3000:127.0.0.1:3000 user@server. Sign in with the default credentials, username admin and password umami, then change the password straight away under Settings, Profile.

Add a website under Settings, Websites, then open its "Tracking code" tab. Paste the snippet into the <head> of your site:

<script defer src="https://stats.example.com/script.js" data-website-id="YOUR-WEBSITE-ID"></script>

Step 4: Put it behind HTTPS

With Caddy on the host:

stats.example.com {
    encode zstd gzip
    reverse_proxy 127.0.0.1:3000
}

Caddy issues the certificate automatically and forwards the client IP, which Umami needs for location data. With Nginx Proxy Manager, create a proxy host for port 3000 and request a Let's Encrypt certificate. Ad blockers often block script.js on known analytics hosts; setting the TRACKER_SCRIPT_NAME environment variable to a custom name such as stats.js reduces that.

Backups and upgrades

Everything lives in Postgres. Dump it regularly:

docker compose exec -T db pg_dump -U umami umami | gzip > umami-$(date +%F).sql.gz

Store the dump off-site, along with your .env. Restore by piping the dump into psql on a fresh database.

Upgrade with:

docker compose pull && docker compose up -d

Umami applies database migrations automatically on start. For major versions, read the release notes first, because some major upgrades require a migration step.

Troubleshooting

  • No data appears: the website ID in the snippet is wrong, or an ad blocker in your test browser blocks the script. Test in a private window without extensions.
  • All visitors show the same location: the reverse proxy is not forwarding the client IP in X-Forwarded-For.
  • Container restarts with a database error: DATABASE_URL does not match the Postgres credentials, often because the password contains special characters. Use a hex password.
  • Cannot log in with admin/umami: the database was created by an earlier install with a different password. Reset it in the database or restore your backup.

Next steps

Track custom events with data-umami-event attributes, create teams to share dashboards, set up public share links for open stats, and add more websites to the same instance.

Spotted something out of date? Tell us and we will update the guide.

FAQ

Umami questions

Still curious? Email info@appsgit.com.

What port does Umami use?

Umami listens on port 3000. You reach the dashboard at http://SERVER_IP:3000, or on your own domain behind a reverse proxy.

What is the default Umami login?

A new Umami install creates the user admin with the password umami. Sign in and change that password immediately.

Is Umami free?

Yes. Umami is free and open source under the MIT license. Umami Cloud is an optional hosted service with a free tier and paid plans.

Does Umami use cookies?

No. Umami does not set cookies and does not collect personal data. It anonymises visitors with a salted hash that rotates, which is why many sites use it without a consent banner.

Umami vs Plausible?

Both are lightweight, privacy-friendly Google Analytics alternatives. Umami runs on a single Postgres database and is easier to host on a small server, while Plausible uses ClickHouse and handles very high traffic volumes.