Umami Web Analytics: Deploy a Privacy-First Google Analytics Alternative in Docker

Learn how to deploy Umami analytics using Docker Compose and PostgreSQL. Track website visitors with a lightweight, privacy-compliant, self-hosted framework.

Self-hosting Umami Web Analytics provides a lightweight, privacy-compliant alternative to Google Analytics. This guide details the deployment of Umami using Docker Compose with a dedicated PostgreSQL database, reverse proxy headers configuration, and advanced tracker customization techniques.

Docker Compose Configuration

Umami requires a relational database. While it supports MySQL, PostgreSQL is the recommended database for performance and scalability. Below is a production-ready docker-compose.yml configuration:

version: '3.8'

services:
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    container_name: umami
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      DATABASE_URL: postgresql://umami_user:umami_secure_password@db:5432/umami_db
      APP_SECRET: 6c8d76e2bc942b083819d9b4b0a43063cb32b2f6ef37d6a4dbb8a0d92305a41a
      TRACKER_SCRIPT_NAME: custom-tracker
    restart: always
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15-alpine
    container_name: umami-db
    environment:
      POSTGRES_USER: umami_user
      POSTGRES_PASSWORD: umami_secure_password
      POSTGRES_DB: umami_db
    volumes:
      - umami-db-data:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U umami_user -d umami_db"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  umami-db-data:

Key Configuration Variables

  • DATABASE_URL: Defines the connection details for PostgreSQL. Follows the format postgresql://[user]:[password]@[host]:[port]/[database].
  • APP_SECRET: A random string used to sign tokens. Generate a secure 32-byte hex key using openssl rand -hex 32.
  • TRACKER_SCRIPT_NAME: Changes the default tracker script filename from script.js to a custom name (e.g., custom-tracker.js or just custom-tracker) to reduce detection by simple ad-blockers.

Reverse Proxy Setup (Nginx)

To secure Umami with SSL and correctly log visitor IP addresses, configure a reverse proxy such as Nginx. Ensure the headers pass the client's actual IP rather than the proxy's IP.

server {
    listen 80;
    server_name analytics.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name analytics.example.com;

    ssl_certificate /etc/letsencrypt/live/analytics.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Initial Deployment and Database Migration

  1. Create a dedicated directory and save the docker-compose.yml configuration: bash mkdir -p ~/umami && cd ~/umami # Save the docker-compose.yml file here
  2. Start the services in detached mode: bash docker compose up -d
  3. Umami handles schema creation and database migrations automatically on startup. Monitor logs to verify database connectivity: bash docker compose logs -f umami
  4. Navigate to your defined domain or http://<vps-ip>:3000 and sign in using the default administrative credentials:
  5. Username: admin
  6. Password: umami

Immediately change the default password in the Profile settings page after logging in.


Implementing the Tracking Script

To track a website, obtain its specific Website ID from the Umami settings panel and inject the tracker code into the HTML <head> section of your site.

Standard HTML Script Injection

Inject the following script element. Replace the attributes with your instance details:

<script 
  async 
  src="https://analytics.example.com/custom-tracker.js" 
  data-website-id="8f72b853-41fc-4706-a331-d0021cd5fab9"
></script>

Custom Event Tracking

Umami supports tracking custom events programmatically using either CSS classes or a JavaScript API.

CSS Class Method

Add a specific class naming pattern to elements. The tracker automatically parses classes starting with umami--<event-name>--<event-value>:

<button class="umami--click--signup-button">Sign Up</button>

JavaScript API Method

Call the global umami object directly inside your client-side scripts:

// Track standard events
umami.track('Purchase', { product: 'Premium Plan', price: 49.00 });

// Track page views manually (useful in SPAs)
umami.track((props) => ({
  ...props,
  url: '/custom-dashboard-path',
  title: 'Dashboard'
}));

Bypassing Ad Blockers with Proxy Rules

If browser extensions block your tracker script, proxy traffic directly through your web server. If your site is hosted at www.example.com, configure Nginx to route analytics calls locally to Umami:

location /stats/script.js {
    proxy_pass https://analytics.example.com/custom-tracker.js;
    proxy_set_header Host analytics.example.com;
}

location /stats/api/send {
    proxy_pass https://analytics.example.com/api/send;
    proxy_set_header Host analytics.example.com;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Update your website injection tag to reference the local proxied endpoint:

<script 
  async 
  src="/stats/script.js" 
  data-website-id="8f72b853-41fc-4706-a331-d0021cd5fab9"
  data-host-url="/stats"
></script>