Back to blog
Blog

Run Umami Analytics on Ubuntu VPS With Nginx TLS

Run Umami on a VPS with Docker Compose, Nginx TLS, backups, updates, and practical sizing for reliable self-hosted analytics.

By Anurag Singh
Updated on Oct 18, 2026
Category: Blog
Share article
Run Umami Analytics on Ubuntu VPS With Nginx TLS

You don’t need a sprawling “marketing stack” to answer the basics. Which pages do people read? Where did they come from? Did yesterday’s deploy hurt signups?

If you want fast, no-nonsense tracking without sending data to someone else’s dashboard, run Umami on a VPS. Umami is a clean, self-hosted analytics app. It fits well if you already manage domains, TLS, and routine server maintenance.

This is an open-source deployment walkthrough, not a sales pitch. You’ll set up a pattern that works in real ops: Docker Compose on Ubuntu, Nginx as a reverse proxy with HTTPS, plus backups and updates you’ll actually keep doing.

Why Umami (and who it’s for)

Umami is an open-source web analytics project that sticks to the essentials: pageviews, unique visitors, referrers, UTM tracking, events, and lightweight reporting.

It’s a good fit if:

  • You run a few sites or apps and want one analytics dashboard you control.
  • You prefer a minimal tracking footprint over “session replay” products.
  • You’d rather pay for predictable infrastructure than per-event billing.
  • You want analytics to live inside your existing ops stack: backups, monitoring, access control.

In 2026, the appeal is simple. Analytics becomes just another service you operate. It sits behind your TLS, uses your database, and follows your retention rules.

License note: Umami is open-source. Before production use, confirm the current license and any “EE”/hosted-only features in the project repository. Do this especially if you plan to bundle it into a client offering.

What it replaces: hosted analytics vs self-hosted Umami

Most teams land in one of two camps. They use a hosted analytics product (Google Analytics, Adobe Analytics, Mixpanel, etc.), or a privacy-focused hosted alternative.

Self-hosting Umami is a third path, with different tradeoffs.

  • Hosted analytics: fastest to start, but you trade control (data location, retention, access) for convenience. You also absorb pricing and policy changes you don’t control.
  • Self-hosted Umami: you own the data and the SLA. In exchange, you patch the box, watch disk growth, and back up the database.

If you already run a VPS for web apps or WordPress, the extra overhead is usually small. Umami is lightweight and tends to coexist well with other low-CPU services.

Resource sizing for a production Umami install

Umami’s needs depend more on traffic volume and retention than raw CPU. A sensible starting point:

  • Small sites (up to ~100k pageviews/month total): 1 vCPU, 1 GB RAM, 20–30 GB SSD/NVMe.
  • Growing portfolio (100k–1M pageviews/month): 2 vCPU, 2–4 GB RAM, 40–80 GB SSD/NVMe.
  • High-traffic / longer retention: 4+ vCPU, 8 GB RAM, tuned database storage and backups.

Disk is usually the limiting factor because analytics tables grow steadily. If you keep 12 months of data, budget storage for the database plus backups.

Keep at least 2× headroom so a backup run or reindex doesn’t fill the volume.

If you want an ops-friendly baseline, start Umami on a dedicated small instance from HostMyCode VPS. Isolation keeps analytics from competing with your public site. It also makes capacity planning simpler.

Architecture that stays boring (Ubuntu + Docker + Nginx)

A stable Umami setup usually looks like this:

  • Ubuntu LTS VPS with Docker Engine + Docker Compose plugin
  • Umami container
  • PostgreSQL container (or an external managed DB if you prefer)
  • Nginx on the host as reverse proxy
  • Let’s Encrypt certificates via Certbot (or your existing ACME flow)

This layout stays easy to reason about. You can tail logs, snapshot volumes, and rate-limit at the proxy layer without touching the app container.

Deploy: Docker Compose for Umami + Postgres

Assume Ubuntu on a fresh VPS, a DNS record pointing analytics.example.com to the server, and SSH access.

Create a working directory:

sudo mkdir -p /opt/umami
sudo chown -R $USER:$USER /opt/umami
cd /opt/umami

Create a docker-compose.yml that runs Umami with PostgreSQL. The image name and environment variables can change between releases, so confirm them in Umami’s official docs before using this in production.

The pattern is what matters: persistent storage, a private service network, and a single port exposed only on localhost.

services:
  db:
    image: postgres:16
    container_name: umami-db
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: change-me-long-random
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    restart: unless-stopped

  umami:
    image: umamisoftware/umami:latest
    container_name: umami-app
    environment:
      DATABASE_URL: postgresql://umami:change-me-long-random@db:5432/umami
      APP_SECRET: change-me-64-chars-min
    depends_on:
      - db
    ports:
      - "127.0.0.1:3000:3000"
    restart: unless-stopped

Bring it up:

docker compose up -d
docker compose ps

Umami now listens only on 127.0.0.1:3000. Keep it that way. Nginx should be your only public entry point.

Pitfall: Don’t expose Umami on 0.0.0.0:3000. It’s easy to forget, and scanners won’t.

Reverse proxy with Nginx + HTTPS (TLS)

Create an Nginx server block for analytics.example.com. On Ubuntu, use /etc/nginx/sites-available/umami and symlink into sites-enabled:

sudo nano /etc/nginx/sites-available/umami
server {
  listen 80;
  server_name analytics.example.com;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    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;
  }

  client_max_body_size 10m;
}
sudo ln -s /etc/nginx/sites-available/umami /etc/nginx/sites-enabled/umami
sudo nginx -t
sudo systemctl reload nginx

Then issue a Let’s Encrypt certificate with Certbot (Nginx plugin):

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d analytics.example.com

Certbot will add the TLS server block and redirect HTTP → HTTPS. If you want a deeper TLS checklist (chains, renewal failures, mixed content), use our guide: Configure HTTPS with Let’s Encrypt on a VPS.

Quick diagnostic: If you see a 502 from Nginx, run curl -I http://127.0.0.1:3000 on the server. If that fails, the problem is in Docker or the app—not Nginx.

Hardening basics that matter for analytics servers

Analytics endpoints get scraped and probed constantly. You don’t need exotic security. You do need the fundamentals:

  • Allow only required ports (22, 80, 443). If possible, restrict SSH by IP or VPN.
  • Keep Ubuntu patched and plan reboots for kernel updates.
  • Disable password SSH logins and use keys.

For a clean firewall baseline, follow: Set up UFW on Ubuntu for a web server.

If you want private admin access, tunnel management through WireGuard: Set up WireGuard VPN on Ubuntu VPS.

Tracking script, domains, and the operational gotchas

After you log in, you’ll add a website in Umami and paste the tracking script into your app or site.

A few practical choices here prevent the usual troubleshooting later:

  • One analytics hostname: Keep Umami on a single subdomain like analytics.example.com. It reduces CORS confusion across multiple properties.
  • Use HTTPS everywhere: If your site is HTTPS but analytics loads over HTTP, browsers will block it.
  • Expect ad blockers: Self-hosting doesn’t “solve” ad blocker behavior. Your numbers will be conservative, and that’s normal.
  • Retention is a decision: Decide up front how many months you keep. It drives storage and backup size.

Backups: treat Postgres as the source of truth

With Umami, the database is the asset. Back it up, prove you can restore it, and keep copies off the server.

A simple, reliable approach:

  • Nightly logical dump (compressed), stored locally for a few days.
  • Offsite copy to object storage or a second VPS.
  • Monthly restore test into a throwaway container to confirm integrity.

Create a backup directory and script:

sudo mkdir -p /var/backups/umami
sudo chown -R $USER:$USER /var/backups/umami
nano /usr/local/bin/umami-backup.sh
#!/usr/bin/env bash
set -euo pipefail

STAMP=$(date -u +"%Y%m%dT%H%M%SZ")
OUT="/var/backups/umami/umami_${STAMP}.sql.gz"

cd /opt/umami

docker compose exec -T db pg_dump -U umami umami | gzip > "$OUT"

# Keep last 14 days
find /var/backups/umami -type f -name 'umami_*.sql.gz' -mtime +14 -delete
sudo chmod +x /usr/local/bin/umami-backup.sh

Schedule it with cron (simple and good enough here):

sudo crontab -e
15 2 * * * /usr/local/bin/umami-backup.sh

Restore drill: On a non-production box, restore into a fresh Postgres container and confirm Umami starts. Don’t skip this. A backup you haven’t restored is just a file on disk.

If you want a structured restore-testing routine (WordPress, databases, and email), the method transfers well to analytics too: VPS backup verification with automated restore tests.

Updates: keep it safe, keep it quick

Self-hosting works only if you patch regularly. With Umami on Compose, updates typically mean:

  • Pull new images
  • Restart services
  • Watch logs for migrations/errors

From /opt/umami:

docker compose pull
docker compose up -d
docker compose logs --tail=200 -f umami

Operator habit: Update during a quiet window and take a fresh DB backup first. If an update includes a database migration, you want an easy rollback point.

Also keep Docker and the host patched. If you run other production services, a tested recovery plan makes updates less tense. Recovery shouldn’t be a mystery.

Monitoring: know when tracking is down (before your team does)

Analytics outages are easy to miss. Your site still loads, but the dashboard stops updating.

Basic monitoring catches the common failures:

  • HTTP check on https://analytics.example.com
  • Disk space alerts (the analytics DB grows)
  • Container restart loops / high memory pressure

If you want a straightforward setup for alerting on VPS services, follow: Build alerts with Netdata + uptime checks. Even if you skip Netdata, the alert thresholds and disk checks are worth copying.

Umami vs other self-hosted analytics (a quick, honest comparison)

You’re not locked into one analytics tool. Pick based on what you need to measure and how much operational overhead you’re willing to own.

  • Umami: lightweight, fast, simple UI. Ideal for “what’s happening on the site?” without standing up a data warehouse.
  • Plausible / Matomo: Plausible stays minimal; Matomo is feature-rich and feels closer to enterprise analytics.

If you want alternatives, we’ve covered similar deployments for other tools so you can compare the ops workload: Install Matomo on a VPS with Nginx and HTTPS and self-host Plausible Analytics with Docker.

Where HostMyCode fits (and when to go managed)

Umami doesn’t need exotic infrastructure, but it does benefit from steady CPU and fast storage. If analytics sits next to revenue-generating apps, isolate it on its own VPS.

This avoids resource contention when traffic spikes.

If you’d rather spend your time building the product than patching the OS, managed hosting is often the practical choice. Managed VPS hosting fits teams that want self-hosted control without the day-to-day maintenance burden.

Summary: self-hosted analytics that stays maintainable

Umami sits in a useful middle ground: simple reporting, small footprint, and an architecture you can keep stable for years.

Put it behind Nginx, back up Postgres nightly, and update on a schedule. Treated that way, it behaves like a well-mannered internal service.

If you’re ready to run Umami on a VPS with enough headroom for growth, start with a small HostMyCode VPS. Scale CPU and disk as retention and traffic increase.

If you want Umami to stay out of the way of your production app, host it on a separate VPS and treat it as shared infrastructure for all your sites. HostMyCode VPS plans are a solid starting point, and managed VPS hosting is the option if you’d rather offload patching and baseline hardening.

FAQ

Do I need a dedicated server to run Umami?

No. Most small-to-mid deployments run comfortably on a VPS. A dedicated server only makes sense if you’re keeping long retention for very high traffic, or you’re consolidating multiple heavy services.

Should I use PostgreSQL or MySQL for Umami?

Use the database backend Umami documents as first-class for your version. PostgreSQL is a common default for self-hosting because it’s predictable under write-heavy workloads.

Can I run Umami behind Cloudflare?

Yes. Keep Nginx as your origin reverse proxy, enable Full (strict) TLS, and make sure your firewall allows Cloudflare IP ranges if you lock down inbound traffic.

What’s the most common cause of missing analytics data?

Blocked scripts (ad blockers) and mixed-content issues (HTTP script on an HTTPS site). Check browser dev tools: you’ll usually see the request blocked or failing.

How do I migrate Umami to a new VPS?

Take a fresh database dump, copy your Compose directory (including any config), restore the DB on the new server, then cut DNS for the analytics subdomain. For a reliable DNS cutover process, see our DNS cutover tutorial.