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 formatpostgresql://[user]:[password]@[host]:[port]/[database].APP_SECRET: A random string used to sign tokens. Generate a secure 32-byte hex key usingopenssl rand -hex 32.TRACKER_SCRIPT_NAME: Changes the default tracker script filename fromscript.jsto a custom name (e.g.,custom-tracker.jsor justcustom-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
- Create a dedicated directory and save the
docker-compose.ymlconfiguration:bash mkdir -p ~/umami && cd ~/umami # Save the docker-compose.yml file here - Start the services in detached mode:
bash docker compose up -d - Umami handles schema creation and database migrations automatically on startup. Monitor logs to verify database connectivity:
bash docker compose logs -f umami - Navigate to your defined domain or
http://<vps-ip>:3000and sign in using the default administrative credentials: - Username:
admin - 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>