Adding Umami Analytics to Ghost

Self-hosted, privacy-first stats without Google analytics

I've been experimenting with self-hosted analytics and wanted something lightweight, privacy-friendly, and super easy to run alongside a Ghost instance. I settled on Umami. It's open source and doesn't creep on visitors the way Google Analytics does.

Deploying Umami

On my VPS I spun up Umami with Docker Compose:

services:
  umami:
    image: ghcr.io/umami-software/umami:postgresql-latest
    ports:
      - "6570:3000"
    environment:
      DATABASE_URL: postgresql://umami:umami@db:5432/umami
      DATABASE_TYPE: postgresql
      APP_SECRET: "changeme"
    depends_on:
      - db

  db:
    image: postgres:14
    environment:
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: umami
      POSTGRES_DB: umami
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

After running docker-compose up -d, Umami is running on port 6570.

Created a new tunnel endpoint on Cloudflare, logged in with the default credentials (admin/umami) and added a new website.

Adding the Tracking Script to Ghost

In Ghost, the cleanest way is to drop Umami's JS tracking script into the theme:

  1. Go to Ghost Admin > Settings > Code Injection.
  2. Under Site Header, paste the snippet:
<script async defer data-website-id="YOUR-UMAMI-SITE-ID" src="https://analytics.mysite.uk/umami.js"></script>

Watching It Work

The script you inject into Ghost runs inside the visitor's browser, and each time someone loads a page it quietly sends a request back to your Umami server. That request includes details such as the page they visited, the type of browser they’re using, their approximate location based on IP, and how long they remained on the site. Umami then takes this stream of requests and groups them into sessions and visitors so you see meaningful trends rather than just raw hit counts. Unlike Google Analytics, it doesn't drop cookies or try to build individual profiles. It just captures the basics and presents them in a clean, privacy-friendly dashboard.