Skip to main content
Photo of DeepakNess DeepakNess

Self-hosting Umami Analytics on a VPS

A few days ago, I self-hosted Plausible analytics and many people recommended that I should check Umami analytics as it's lightweight and updates are more frequent.

So here it is... I also self-hosted Umami analytics on a $3.50 Hetzner VPS to see if it actually lives up to the hype. First, I will explain the installation steps and then write about my experience.

Installing Umami on a Hetzner VPS

First, you need to have a Hetzner account where you can create new servers. If you do not have one, create one and then get yourself verified, if required (yes, I had to submit my ID a few years ago).

I also recorded a quick video showing everything from the start – from setting up the VPS to adding the website for tracking.

But if you prefer reading, keep going forward.

1. Set up the VPS

Create a new server, even the smallest shared server is enough to host Umami as people discussed that only 500 MB RAM is enough to efficiently run it.

Also, I'm not going to explain how to set up a new server on Hetzner so if you need help with that you can go through this post, as I have explained the process a bit.

And as explained, set up a Docker CE app instead of setting up a blank system as we'll be installing Umami via Docker.

Now, connect to the server via SSH from your terminal by running ssh root@your_ip_address command, and once connected, update the system by running the following command:

sudo apt update && sudo apt upgrade

And it's ready for the installation.

2. Umami and Caddy setup via Docker

While you can directly follow the official installing with Docker steps, I have a better way of doing it by also using Caddy for also setting up the custom domain.

For this, you need to create a docker-compose.yml file in the root by running the following nano command.

nano docker-compose.yml

Now, just copy-paste the below YML code in the file, then save and exit by pressing ctrl + x and then y. But replace replace-me-with-a-random-string with a string before pasting; I prefer running below openssl command:

openssl rand -base64 32

And then use the generated string below:

services:
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    environment:
      DATABASE_URL: postgresql://umami:umami@db:5432/umami
      DATABASE_TYPE: postgresql
      APP_SECRET: to4KhI0g8Wzaw1r7+vEknSbU4AM4d6LRJyGzlgO5DZY=
    depends_on:
      db:
        condition: service_healthy
    init: true
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl 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: umami
    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"
    depends_on:
      umami:
        condition: service_started
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  umami-db-data:
  caddy_data:
  caddy_config:

Now, create a Caddyfile by running the below command and paste the below Caddyfile code, but first replace the your_domain_or_subdomain_here with your domain/subdomain.

nano Caddyfile

For example, I can connect it to umami.deepakness.com. Again, press ctrl + x and then y to save and exit.

your_domain_or_subdomain_here {
  encode zstd gzip

  reverse_proxy {
    to umami:3000
    health_uri /api/heartbeat
    health_interval 10s
    health_timeout 2s
  }

  log {
    output stdout
    format json
  }
}

Also, you will need to add a DNS record at Cloudflare (if you're using) or at your domain registrar. You need to add an A record with your IP address.

Adding a DNS record in Cloudflare.

For example, I use Cloudflare and this is how I'd add the A DNS record in there.

3. Start Docker containers

Now, everything is ready, and you can just start the docker container by running the below command. It starts all the containers in the background.

docker compose up -d

And then you can also run the below command (optional) to see live logs from the Caddy container so you can see if it gets the SSL certificate and starts properly.

docker compose logs -f caddy

And your Umami Analytics instance should be live!

You can visit the domain/subdomain and should see the login screen, and your default username will be admin and password umami. But you should immediately change your password after logging in.

And that's it.

You can add your website(s) for tracking and start collecting data as explained in their docs.

My experience with Umami

Actually, I liked Umami a lot, it's better than Plausible in terms of server resource usage. You can see the usage here for both:

Plausible is using 1 GB of RAM:

Plausible htop RAM usage

Umami is using ~500 MB of RAM:

Umami htop RAM usage

Apart from this, I also liked the Umami dashboard a lot as it shows more tracking data while consuming 50% less resources than Plausible. Also, Plausible keeps showing a huge footer like below on all pages which I didn't like, but it's not there in Umami.

Unnecessary Plausible footer

Apart from this, I liked the data points being shown in Umami, especially the way sessions and events data is shown.

Sessions data in Umami

So... that's it.

This time, I am going with Umami as it seemed more polished to me.

Comment via email