Self-Host Umami Analytics on Your Own Server
Google Analytics is powerful, but it comes with baggage — cookie banners, data privacy concerns, and a bloated script that slows down your site. Umami is a lightweight, open-source, privacy-focused alternative that you can self-host in minutes.
Snehasis Ghosh
Self-Host Umami Analytics on Your Own Server with a Custom Domain
Google Analytics is powerful, but it comes with baggage — cookie banners, data privacy concerns, and a bloated script that slows down your site. Umami is a lightweight, open-source, privacy-focused alternative that you can self-host in minutes.
In this guide, I'll walk you through deploying Umami on your own server using Docker, backed by PostgreSQL, and served over HTTPS on a custom domain using Caddy as a reverse proxy.
Prerequisites
Before you begin, make sure you have:
- A Linux server (Ubuntu 22.04+ recommended) with at least 1GB RAM
- A domain name (e.g.,
analytics.yourdomain.com) with DNS pointed to your server's IP - SSH access to your server
Why Self-Host Umami?
| Feature | Umami (Self-Hosted) | Google Analytics |
|---|---|---|
| Privacy | No cookies, GDPR-compliant by default | Requires cookie consent banners |
| Script size | ~2KB | ~45KB |
| Data ownership | 100% yours | Google's servers |
| Cost | Free (just server costs) | Free tier with limits |
| Setup complexity | ~10 minutes with Docker | Quick, but privacy trade-offs |
Step 1 — Install Docker
SSH into your server and install Docker along with the Compose plugin:
# Update packages
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y ca-certificates curl gnupg
# Add Docker's official 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 the 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 and Compose
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Verify the installation:
docker --version
docker compose version
Optionally, add your user to the docker group so you don't need sudo for every command:
sudo usermod -aG docker $USER
newgrp docker
Step 2 — Prepare Your Project Directory
Create a dedicated directory for the Umami deployment:
mkdir -p /home/umami && cd /home/umami
Step 3 — Create the Docker Compose File
Create a docker-compose.yml file with three services: Umami (the analytics app), PostgreSQL (the database), and Caddy (the reverse proxy for automatic HTTPS).
nano docker-compose.yml
Paste the following configuration:
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
environment:
DATABASE_URL: postgresql://umami:your_secure_db_password@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: replace-with-a-random-string
depends_on:
db:
condition: service_healthy
init: true
restart: always
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/heartbeat"]
interval: 5s
timeout: 5s
retries: 5
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: your_secure_db_password
volumes:
- umami-db-data:/var/lib/postgresql/data
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 5
caddy:
image: caddy:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- umami
volumes:
umami-db-data:
caddy_data:
caddy_config:
Important: Replace
your_secure_db_passwordwith a strong password andreplace-with-a-random-stringwith a random secret. You can generate one with:openssl rand -hex 32
Step 4 — Create the Caddyfile
Caddy handles SSL certificate provisioning automatically via Let's Encrypt. No manual cert management needed.
Create a Caddyfile in the same directory:
nano Caddyfile
Add your domain configuration:
analytics.yourdomain.com {
reverse_proxy umami:3000
}
Replace analytics.yourdomain.com with your actual domain. That's it — Caddy does the rest.
Step 5 — Point Your Domain to the Server
In your domain registrar's DNS settings, create an A record:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | analytics | YOUR_SERVER_IP | 300 |
Wait a few minutes for DNS propagation. You can verify with:
dig analytics.yourdomain.com +short
Step 6 — Deploy
Start everything up:
docker compose up -d
Monitor the logs to confirm Caddy obtains the SSL certificate:
docker compose logs -f caddy
You should see output like:
caddy | successfully obtained certificate
caddy | certificate obtained successfully
Once that's done, your Umami instance is live at https://analytics.yourdomain.com.
Step 7 — Log In and Configure
- Open
https://analytics.yourdomain.comin your browser - Log in with the default credentials:
- Username:
admin - Password:
umami
- Username:
- Change your password immediately under Settings → Profile
- Add your website under Settings → Websites
- Copy the tracking script and add it to your site's
<head>tag
The tracking script looks like this:
<script
defer
src="https://analytics.yourdomain.com/script.js"
data-website-id="your-website-id"
></script>
Updating Umami
When a new version is released, updating is a single command:
cd /home/umami
docker compose pull
docker compose up -d
Your data is persisted in the PostgreSQL volume, so updates are non-destructive.
Troubleshooting
Caddy not getting a certificate?
- Ensure ports 80 and 443 are open in your firewall (
ufw allow 80 && ufw allow 443) - Verify your DNS A record is correctly pointing to your server IP
Umami not starting?
- Check logs:
docker compose logs umami - Ensure the database password matches in both the
umamianddbservice configurations
Connection refused?
- Wait for health checks to pass:
docker compose ps(all services should show "healthy")
Wrapping Up
You now have a fully self-hosted analytics platform running on your own infrastructure — privacy-respecting, lightweight, and completely under your control. No third-party scripts phoning home, no cookie banners, and a clean dashboard that shows you exactly what matters.
The entire setup takes under 10 minutes and costs nothing beyond your existing server. That's a solid trade-off for owning your data.
