Umami Analytics
Privacy-focused, open-source web analytics. No cookies, no personal data, GDPR compliant by default.
1GB RAM min / KVM from $5/mo
Ubuntu 22.04 / 24.04 LTS
Domain name with DNS access
Introduction
Umami is a modern, open-source, privacy-focused web analytics platform. It provides essential insights — pageviews, unique visitors, bounce rate, referral sources, device breakdowns, and custom event tracking — while fully respecting visitor privacy. No cookies, no personal data, GDPR/PECR compliant out of the box.
No cookies, no personal data, GDPR compliant by default
Tracking script under 2KB — won't impact page load
All data stays on your server, no third-party access
Pageviews, sessions, devices, UTM, funnels, custom events
When choosing a subdomain, avoid names like "analytics.yourdomain.com" as ad blockers may block them. Use something like stats.yourdomain.com or insight.yourdomain.com instead.
Step 1: Initial Server Setup
1Connect & Update
ssh root@YOUR_SERVER_IP
apt update && apt upgrade -y2Create a Non-Root User (Recommended)
adduser umami
usermod -aG sudo umami
su - umami3Configure the Firewall
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableStep 2: Install Docker & Docker Compose
# Install prerequisites
sudo apt install -y ca-certificates curl gnupg
# Add Docker's GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli \
containerd.io docker-buildx-plugin docker-compose-plugin# Add user to Docker group
sudo usermod -aG docker $USER
newgrp docker
# Verify installation
docker --version
docker compose versionStep 3: Deploy Umami with Docker Compose
1Create Project Directory & Generate Secret
mkdir -p ~/umami && cd ~/umami
openssl rand -base64 32Copy the output — you'll need it in the next step.
2Create the Environment File
POSTGRES_DB=umami
POSTGRES_USER=umami
POSTGRES_PASSWORD=YOUR_STRONG_DB_PASSWORD
DATABASE_URL=postgresql://umami:YOUR_STRONG_DB_PASSWORD@db:5432/umami
APP_SECRET=YOUR_GENERATED_SECRET
TRACKER_SCRIPT_NAME=getinfoTRACKER_SCRIPT_NAME renames the default "script.js" to a custom name (e.g., "getinfo") to prevent ad blockers from blocking the analytics script.
3Create the Docker Compose File
services:
umami:
image: docker.umami.is/umami-software/umami:postgresql-latest
ports:
- "3000:3000"
environment:
DATABASE_URL: ${DATABASE_URL}
APP_SECRET: ${APP_SECRET}
TRACKER_SCRIPT_NAME: ${TRACKER_SCRIPT_NAME}
depends_on:
db:
condition: service_healthy
restart: always
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat || exit 1"]
interval: 30s
timeout: 5s
retries: 3
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
TZ: UTC
volumes:
- umami-db-data:/var/lib/postgresql/data
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
umami-db-data:4Launch the Stack
docker compose up -d
docker compose psBoth umami and db containers should show as healthy/running. Umami is now accessible at http://YOUR_SERVER_IP:3000.
Step 4: Nginx Reverse Proxy
sudo apt install -y nginx
sudo systemctl enable nginxserver {
listen 80;
server_name stats.yourdomain.com;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo ln -s /etc/nginx/sites-available/umami /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxStep 5: SSL with Let's Encrypt
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d stats.yourdomain.comVerify auto-renewal:
sudo certbot renew --dry-runYour Umami instance is now accessible at https://stats.yourdomain.com.
Step 6: Initial Login & Configuration
Default Credentials
| Field | Value |
|---|---|
| Username | admin |
| Password | umami |
Change the default password immediately after first login. Go to Settings → Profile.
Add Your First Website
- Navigate to Settings → Websites and click Add Website.
- Enter a display name and your website's domain.
- Click Save, then Edit to copy the tracking snippet.
Install the Tracking Code
Add to the <head> section of every page:
<script defer src="https://stats.yourdomain.com/getinfo"
data-website-id="YOUR-WEBSITE-ID"></script>The script filename is "getinfo" instead of "script.js" because of the TRACKER_SCRIPT_NAME variable. Visitors appear in your dashboard within seconds.
Step 7: Database Backup Strategy
Manual Backup
docker compose exec db pg_dump -U umami umami \
> umami_backup_$(date +%Y%m%d).sqlAutomated Daily Backups
mkdir -p ~/umami/backups#!/bin/bash
BACKUP_DIR=~/umami/backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
cd ~/umami
docker compose exec -T db pg_dump -U umami umami \
| gzip > $BACKUP_DIR/umami_$TIMESTAMP.sql.gz
# Keep only the last 14 days of backups
find $BACKUP_DIR -name "umami_*.sql.gz" -mtime +14 -deletechmod +x ~/umami/backup.sh
crontab -eAdd this line to run daily at 2:00 AM:
0 2 * * * /home/umami/umami/backup.shStep 8: Updating Umami
cd ~/umami
docker compose pull
docker compose up -dCheck the Umami GitHub releases before updating to review any breaking changes or migration notes.
Step 9: Performance Tuning
Nginx Caching Headers
Add caching for the tracking script to reduce server load:
location ~* \.(js)$ {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
expires 1h;
add_header Cache-Control "public, immutable";
}PostgreSQL Tuning (100K+ monthly pageviews)
db:
image: postgres:15-alpine
command:
- postgres
- -c
- shared_buffers=256MB
- -c
- effective_cache_size=512MB
- -c
- work_mem=4MBTroubleshooting
| Issue | Solution |
|---|---|
| Container won't start | Run docker compose logs umami. Verify .env has correct DATABASE_URL formatting. |
| 502 Bad Gateway | Ensure Umami container is running (docker compose ps). Check Nginx config with nginx -t. |
| No data in dashboard | Verify tracking script loads (DevTools Network tab). Check data-website-id matches. Disable ad blockers to test. |
| SSL not renewing | Run sudo certbot renew --dry-run. Ensure port 80 is accessible and certbot timer is active. |
| Database errors | Ensure PostgreSQL is healthy. Verify DATABASE_URL matches POSTGRES_USER and POSTGRES_PASSWORD. |
View real-time logs for debugging:
docker compose logs -f umami
docker compose logs -f db