DEV Community

Cover image for Deploying Umami - Open-Source Analytics Platform
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya Originally published at docs.vultr.com

Deploying Umami - Open-Source Analytics Platform

Umami is an open-source, privacy-focused web analytics platform designed as a self-hosted alternative to Google Analytics. It avoids cookies and personally identifiable information, removes the need for cookie banners, and captures real-time metrics including visitor devices, geographic data, referrers, and custom events through a lightweight tracking script. This guide deploys Umami on a Linux server using Docker Compose with PostgreSQL as the database, Redis for session caching, and Traefik for automatic HTTPS through Let's Encrypt, then walks through directory setup, environment configuration, and integrating the tracking script into a sample website. By the end, you'll have a self-hosted Umami instance tracking real traffic on your own domain.


Set Up the Directory Structure, Configuration, and Environment Variables

Umami requires a project directory with persistent storage for the PostgreSQL database, Redis cache, and Let's Encrypt certificates. Environment variables control the domain, database credentials, application secret, and tracking customizations.

1. Create the project directory and move into it:

$ mkdir -p ~/umami/{pgdata,redis,letsencrypt}
$ cd ~/umami
Enter fullscreen mode Exit fullscreen mode
  • pgdata/: Persists PostgreSQL database files.
  • redis/: Stores Redis data for caching and session management.
  • letsencrypt/: Stores Traefik ACME certificates for HTTPS renewal.

2. Generate a strong random secret for the application:

$ openssl rand -hex 32
Enter fullscreen mode Exit fullscreen mode

Save the output for use in the environment file.

3. Create the environment file:

$ nano .env
Enter fullscreen mode Exit fullscreen mode

Add the following configuration. Replace umami.example.com with your domain name, admin@example.com with your email address, GENERATED_SECRET with the generated random string, and STRONG_DATABASE_PASSWORD with a secure database password.

# Domain Configuration
DOMAIN=umami.example.com
LETSENCRYPT_EMAIL=admin@example.com

# Application Secret
APP_SECRET=GENERATED_SECRET

# PostgreSQL Configuration
POSTGRES_DB=umami
POSTGRES_USER=umami
POSTGRES_PASSWORD=STRONG_DATABASE_PASSWORD
DATABASE_URL=postgresql://umami:${POSTGRES_PASSWORD}@postgres:5432/umami

# Redis Configuration
REDIS_URL=redis://redis:6379

# Tracker Customization
TRACKER_SCRIPT_NAME=custom-stats
COLLECT_API_ENDPOINT=/custom-api/send
DISABLE_TELEMETRY=1
Enter fullscreen mode Exit fullscreen mode

Save and close the file.

Note: The TRACKER_SCRIPT_NAME and COLLECT_API_ENDPOINT variables add an alternate path for the tracking script and collection endpoint, alongside the defaults, to help bypass ad blockers that specifically target /script.js. The default paths remain active. DISABLE_TELEMETRY=1 prevents Umami from sending anonymous usage statistics.


Deploy with Docker Compose

The deployment stack runs four services: Traefik as the reverse proxy with automatic TLS, PostgreSQL for analytics data persistence, Redis for session caching, and the Umami web application. This configuration is based on the official Umami Docker Compose example, adapted to use Traefik and persistent storage.

1. Create the Docker Compose manifest:

$ nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode
services:
  traefik:
    image: traefik:v3.7.0
    container_name: umami-traefik
    restart: unless-stopped
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt

  postgres:
    image: postgres:17-alpine
    container_name: umami-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d ${POSTGRES_DB} -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:8-alpine
    container_name: umami-redis
    restart: unless-stopped
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - ./redis:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  umami:
    image: ghcr.io/umami-software/umami:3.1.0
    container_name: umami
    restart: unless-stopped
    env_file: .env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.umami.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.umami.entrypoints=websecure"
      - "traefik.http.routers.umami.tls=true"
      - "traefik.http.routers.umami.tls.certresolver=letsencrypt"
      - "traefik.http.services.umami.loadbalancer.server.port=3000"
Enter fullscreen mode Exit fullscreen mode

Save and close the file. traefik handles reverse proxy and TLS termination, redirecting HTTP to HTTPS and provisioning Let's Encrypt certificates using the TLS-ALPN-01 challenge. postgres runs PostgreSQL 17 as the primary database for analytics data, website configurations, and user accounts. redis runs Redis 8 as the caching layer for server-side authentication sessions, reducing database load. umami runs the official Umami application pinned to version 3.1.0 and registers with Traefik for HTTPS routing on the configured domain.

2. Start the services:

$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

3. Verify that all containers are running:

$ docker compose ps -a
Enter fullscreen mode Exit fullscreen mode

The output displays four containers. postgres and redis show a healthy status; traefik and umami show as running, since those two have no configured health check.

4. View the service logs to confirm Umami connected to the database:

$ docker compose logs umami
Enter fullscreen mode Exit fullscreen mode

The output displays Umami completing database migrations and reporting the application as ready.


Access and Configure Umami

After deployment, access the Umami dashboard using your configured domain, log in with the default administrator credentials, and rotate the password to secure the instance.

1. Open the login page:

Replace umami.example.com with your configured domain and open https://umami.example.com/login in a web browser.

2. Log in using the default administrator credentials. The username is admin and the password is umami.

Note: Change the default credentials immediately after the first login to secure your analytics instance.

3. Change the password:

At the bottom of the left sidebar, click admin and select Settings. Navigate to the Profile tab, click Change password, enter the current password (umami), enter your new secure password, and click Save.


Track a Sample Website

Umami tracks visitors through a lightweight JavaScript snippet embedded in your target website. The following steps add a website, retrieve the tracking code, embed it in a sample page, and verify that metrics appear in the dashboard.

Add a Website

Umami tracks each site separately, so a website entry must exist before its tracking code can be generated.

  1. In the Umami dashboard, click Websites in the left navigation pane.
  2. Click Add website.
  3. Enter a name such as My Test Site and a domain such as test.example.com.
  4. Click Save.

Retrieve the Tracking Code

Each website gets a unique tracking snippet tied to its website ID.

  1. Find the new website in the list and click Edit.
  2. Navigate to the Tracking code tab.
  3. Copy the generated <script> tag.

Note: Because TRACKER_SCRIPT_NAME=custom-stats was set in the environment file, the generated tag uses /custom-stats as an additional path alongside the default /script.js. Using the custom path helps bypass ad blockers that specifically target the default filename.

Embed the Tracking Code

The script must load on every page you want Umami to track.

  1. Open the main HTML document or the global layout component of your sample web application.
  2. Paste the tracking code into the <head> section. Replace YOUR_WEBSITE_ID with the ID from your Umami dashboard and umami.example.com with your domain:
   <head>
       <meta charset="UTF-8">
       <title>Sample Web Application</title>
       <script defer src="https://umami.example.com/custom-stats" data-website-id="YOUR_WEBSITE_ID"></script>
   </head>
Enter fullscreen mode Exit fullscreen mode
  1. Save the file and open the sample web application in a browser.
  2. Refresh the page a few times to simulate visitor traffic.

Verify Analytics Collection

Confirming that traffic appears in the dashboard validates that the tracking script is reaching the server.

  1. Return to the Umami dashboard and select your website.
  2. Review the main dashboard to verify captured page views, referrers, device types, and visitor data.
  3. Click the Realtime tab to monitor active visitors navigating the sample site.

Next Steps

Umami is running with a privacy-first, self-hosted analytics dashboard tracking live traffic. From here you can:

  • Add more websites and compare traffic across properties from a single dashboard
  • Configure custom events to track conversions, signups, or other in-app actions
  • Enable email reports or connect the Umami API to pull metrics into external tooling

For the full guide with additional tips, visit the original article on Vultr Docs.

Top comments (0)