
Self-hosting your analytics means complete data ownership. No third party ever sees your visitor data. No monthly fees that scale with traffic. No terms of service that change overnight.
Umami is a free, open-source analytics tool built for exactly this. Lightweight, privacy-focused, runs on any VPS with Node.js and a database. If you already have a server running WordPress, you almost certainly have enough resources to run Umami alongside it.
This guide covers a complete production setup — from provisioning a VPS to watching your first real-time visitor appear on screen.
Short answer: Install Docker on a VPS with at least 1 GB RAM, drop in a two-service docker-compose.yml (Umami + PostgreSQL), put Nginx in front with a Let’s Encrypt certificate, and paste one script tag into WordPress. Setup runs under an hour; ongoing cost is $5–10/month for the server.
Why Self-Host Your Analytics
Cloud analytics services — even privacy-focused ones like Plausible and Fathom — still send your data to someone else’s servers. Self-hosting cuts that out entirely.
- Zero data sharing. Visitor data never leaves your server. There’s no data processing agreement to worry about because you are the data processor.
- No traffic-based pricing. Whether you get 1,000 or 10 million pageviews, your only cost is the VPS. A $5–10/month server handles most sites easily.
- Real GDPR control. You decide where data lives. Pick an EU datacenter and your data physically stays in the EU — no sub-processor relationships to document.
- No vendor lock-in. If Umami changes direction, you have the code and the database. Fork it or migrate without losing historical data.
The trade-off is maintenance: you handle updates, backups, and server uptime. With Docker and the setup below, that’s roughly 30 minutes a month.
Not sure whether self-hosting is the right call for you? My Matomo vs Plausible comparison walks through when managed hosting makes more sense than running your own stack.
Prerequisites
You’ll need:
- A VPS with at least 1 GB RAM and 1 CPU core (2 GB recommended). Providers like Hetzner, DigitalOcean, or Vultr all work. A €4–5/month Hetzner server in an EU datacenter is my usual pick for GDPR-conscious projects.
- Docker and Docker Compose installed on the server.
- A domain or subdomain for the dashboard — something like
analytics.yourdomain.com. - Basic SSH access and comfort with the command line.
Step 1: Set Up Your VPS

SSH into your server and install Docker if it’s not already there:
# Install Docker (Ubuntu/Debian)
curl -fsSL https://get.docker.com | sh
# Install Docker Compose
sudo apt install docker-compose-plugin
# Verify installation
docker --version
docker compose versionCreate a directory for your Umami installation:
mkdir -p /opt/umami
cd /opt/umamiStep 2: Create the Docker Compose File
Create a docker-compose.yml file with Umami and PostgreSQL:
# /opt/umami/docker-compose.yml
version: '3'
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://umami:your-secure-password@db:5432/umami
APP_SECRET: replace-with-random-64-char-string
depends_on:
db:
condition: service_healthy
restart: always
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: your-secure-password
volumes:
- umami-db-data:/var/lib/postgresql/data
restart: always
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami"]
interval: 5s
timeout: 5s
retries: 5
volumes:
umami-db-data:Two things to replace before you start anything. First, swap your-secure-password with an actual strong password — run openssl rand -hex 32 and use that output. Then do the same for APP_SECRET, but generate a separate value. These two strings must be different from each other.
Step 3: Start Umami
cd /opt/umami
docker compose up -dDocker pulls the images and starts both containers. First startup takes 1–2 minutes while PostgreSQL initializes and Umami creates its schema.
Check that both services came up cleanly:
docker compose psBoth umami and db should show status “Up”. Umami is now accessible at http://your-server-ip:3000. Don’t expose this publicly yet — you’ll put Nginx in front of it in the next step.
Step 4: Set Up a Reverse Proxy with SSL
Port 3000 without SSL is fine for testing. For production, put Nginx in front with a Let’s Encrypt certificate.
First, point your subdomain DNS to your server’s IP. Then install Nginx and Certbot:
# Install Nginx and Certbot
sudo apt install nginx certbot python3-certbot-nginxCreate an Nginx server block:
# /etc/nginx/sites-available/analytics
server {
server_name analytics.yourdomain.com;
location / {
proxy_pass http://localhost: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;
}
}
# Enable the site and get SSL certificate
sudo ln -s /etc/nginx/sites-available/analytics /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d analytics.yourdomain.comCertbot configures HTTPS and sets up auto-renewal. Your Umami dashboard is now at https://analytics.yourdomain.com.
Step 5: Log In and Add Your Website

Open the dashboard and log in with the default credentials:
- Username: admin
- Password: umami
Change the default password immediately — before you add any sites. Go to Settings → Profile.
Then add your website:
- Go to Settings → Websites → Add website
- Enter your domain name and a display name
- Click Save
- Click the Edit icon next to your site, then Tracking code
- Copy the tracking script
The script looks like this:
<script defer src="https://analytics.yourdomain.com/script.js" data-website-id="your-website-id"></script>Step 6: Add the Tracking Script to WordPress
There’s no official Umami WordPress plugin. The cleanest approach is your child theme’s functions.php:
add_action('wp_head', function() {
echo '<script defer src="https://analytics.yourdomain.com/script.js" data-website-id="your-website-id"></script>';
});If you’d rather not touch theme files, Insert Headers and Footers (by WPCode) does the same job via the WordPress admin.
After adding the script, open your site in an incognito window and watch the Umami dashboard — you should see a live visitor within seconds. That’s your signal everything is wired up correctly.
Maintenance and Backups

Self-hosting means you own the uptime too. Here’s the minimum routine that keeps things stable without eating into your week.
Updating Umami
Umami releases updates regularly — some include security patches. To update:
cd /opt/umami
docker compose pull
docker compose up -dThis pulls the latest image and restarts the container. Downtime is typically under 10 seconds. Run it monthly, or immediately when a security release is announced.
Database Backups
A cron job is enough for most sites:
# Add to crontab: daily backup at 3 AM
0 3 * * * docker exec umami-db-1 pg_dump -U umami umami | gzip > /opt/umami/backups/umami-$(date +\%Y\%m\%d).sql.gzKeep at least seven days of backups. For anything critical, copy backups offsite using rclone or rsync.
Monitoring Uptime
If your analytics server goes down, data for that period is gone — there’s no backfill from a tracking script that couldn’t reach its endpoint. Set up a free uptime monitor: UptimeRobot or Better Stack will alert you within minutes of any outage.
Performance Tuning
For sites above 100K monthly pageviews, a few changes help:
- Increase PostgreSQL shared memory. Add
shm_size: '256mb'to thedbservice in your Docker Compose file. - Enable WAL mode. Improves write performance under heavy concurrent traffic.
- Use SSD storage. The database does heavy random reads. If your VPS uses spinning disks, the performance difference is significant.
- Separate servers. For very high traffic (1M+ monthly), run the database and the Umami app on separate VPS instances.
For most WordPress sites, the defaults are fine. A 2 GB Hetzner instance handles millions of events per month without breaking a sweat.
Once you have real data flowing, you’ll likely want to think about what to do with it beyond pageviews. My guide on measuring SEO results with privacy-friendly analytics shows how to extract actionable signals from tools like Umami — without touching Google Analytics.
FAQ
How much server resources does Umami use?
At idle, Umami uses roughly 150–200 MB RAM and minimal CPU. Under load (thousands of concurrent visitors), RAM usage stays under 500 MB. PostgreSQL is the heavier component — allocate at least 512 MB for it on busy sites.
Can I run Umami on the same server as WordPress?
Yes. If your WordPress server has at least 2 GB total RAM and Docker is installed or can be installed alongside, Umami shares it without issues. Just confirm Umami’s port (3000) doesn’t conflict with anything else already running.
Is Umami Cloud worth it instead of self-hosting?
Umami Cloud is free up to 100K events per month. If you don’t want server maintenance and your traffic fits the free tier, Cloud is the easier path. Self-hosting makes sense when you need full data sovereignty, have high traffic, or want to avoid any external data processing — including Umami’s own servers.
How do I migrate from Google Analytics to Umami?
There’s no direct import — GA and Umami use completely different data models. The practical approach: install Umami alongside GA, run both for 30 days to build a baseline, then remove GA. Your historical GA data remains accessible in Google’s interface. For a full walkthrough of the process, see my guide to migrating from Google Analytics.
Start Tracking on Your Own Terms
Self-hosted Umami gives you everything most websites actually need — pageviews, referrers, device breakdown, custom events — without sending a single byte of visitor data to anyone else. Setup takes under an hour, ongoing cost is a few dollars a month, and the maintenance burden is low.
If running a server feels like overkill, Plausible’s WordPress setup gets you privacy-first analytics in under 10 minutes with zero infrastructure to manage. Both are solid choices — the right one depends on how much control you want over your data stack.



