Altoplace

Add Analytics to Your Hugo Site with Umami

Tags:
Analytics technology.
Image by Tung Nguyen from Pixabay

I have been experimenting with website development and Hugo for sometime. It would be nice to see which posts are being viewed. Since this is a hobby site for me (non-commercial), I don’t want to pay for a commercial, third party service to track my website usage. I wanted to find a simple, self-hosted solution that provides basic analytic data. Also, I am using a managed VPS that does not allow root access. They do support Docker, working in a rootless mode. I needed to find a Docker solution that would work in my environment.

Umami is the solution that I settled on. It was very easy to install with Docker, and it was easy to integrate with my Hugo website. They do provide a Umami Cloud high-performance service which is free for up to 100K “events” per month. Or you can pay $20/month for up to 1 million events per month. They also provide detailed documentation for the self-hosted option.

Here are my notes for setting up a self-hosted instance of Umami on a managed VPS that supports Docker.

Set Up Your Environment

You need to ensure that docker and docker-compose is working on your host. There are many excellent Docker tutorials on the net, so I will not cover the details of installing and using it. On my Pair VPS host, I was able to install it with one click in my control pannel. You can refer to the Docker Docs to get started. After Docker is installed, you can run docker run hello-world to verify your installation.

Next, you need to configure a subdomain, such as umami.example.com, to host your commenting system. I enabled “https” and a reverse proxy on my umami subdomain. With Pair, this only took a couple of clicks. The reverse proxy maps to http://localhost:3000.

Install the Umami Docker Image

You are going to use docker compose to install and manage your Umami analytics system (don’t use docker-compose). Do the following to verify that docker compose is installed and working:

$ docker compose version
Docker Compose version v2.29.1

Note, on my host, Docker is running in rootless mode, because my VPS provider does not allow root access.

Next, in my umami subdomain root, I created a docker-compose.yml file that defines my Umami Docker configuration. It actually defines two services, the umami service and a postgres DB service where the analytics data is stored.

Here’s what my docker-compose.yml looks like:

---
networks:
  umami-network:
    name: umami-network
volumes:
  umami-db:
    name: umami-db
services:
  umami:
    image: docker.umami.is/umami-software/umami:postgresql-latest
    container_name: umami
    ports:
      - 127.0.0.1:3000:3000
    environment:
      DATABASE_URL: postgresql://umami:POSTGRES_PASSWORD:5432/umami
      DATABASE_TYPE: postgresql
      APP_SECRET: "A long, hard to guess random string"
    depends_on:
      db:
        condition: service_healthy
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - umami-network
  db:
    image: postgres:15-alpine
    container_name: postgres
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: "a long, hard to guess random string"
    volumes:
      - umami-db:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - umami-network

Docker creates volume data under docker ownership. I discovered this when I was experimenting with Remark42 using the example configuration file. I could not delete the example volume bind-mounted data because I did not have root access. I had to contact support and ask them to delete the data for me. When using the named volume mode, you can use docker compose commands to manage the volume data, including deleting it if you need to. This Umami docker compose configuration file defines and uses a named umami-db volume to store the analytics data.

You can use your favorite tool to generate “a long, hard to guess random string” for the APP_SECRET and POSTGRES_PASSWORD. On macOS, I like using the Homebrew pwgen tool:

$ pwgen -s 64 1
H8qRx8F8UnypUqTU8zep8UJFH5KSm6DQHTGSCBwonilj5kSXpzzn5SfqIPAay1JQ

You can adjust the string length (64) as desired.

For the database username and password used for the DATABASE_URL in the umami services section, be sure to use the same username and password that is defined in the postgres db services section.

The “ports” definition allows access to the Umami instance on the local host, including the reverse proxy. But it blocks access from an external IP.

Once this is all set up, you are ready to pull down the Umami and Postgres images. Be sure to be in the same directory as your docker-compose.yml configuration file. I placed mine in the umami.example.com root directory. Run the docker compose pull command:

$ docker compose pull
[+] Pulling 27/27
 ✔ db Pulled			    24.8s
   ✔ da9db072f522 Pull complete      0.8s
   ✔ 6dcbb5ba92ae Pull complete      1.0s
   ✔ 6dc5cb41ec83 Pull complete      1.2s
   ✔ 5806fbc44721 Pull complete      1.4s
   ✔ a5391613ee43 Pull complete      1.9s
   ✔ 663428924e4f Pull complete     13.9s
   ✔ 01f2a31c48e5 Pull complete     16.6s
   ✔ 6b40f412537f Pull complete     18.0s
   ✔ 338db0958c47 Pull complete     19.5s
   ✔ 0dcd19fb9328 Pull complete     20.5s
   ✔ 992b5ea716e6 Pull complete     22.0s
 ✔ umami Pulled 		    47.8s
   ✔ aa6f657bab0c Pull complete      7.3s
   ✔ f477ea663f1c Pull complete      8.3s
   ✔ 43c47a581c29 Pull complete     10.0s
   ✔ a0227809ebf2 Pull complete     11.8s
   ✔ d1eaea8ec347 Pull complete     13.1s
   ✔ e1d4c9bd9d72 Pull complete     14.1s
   ✔ 7c5e6decdda1 Pull complete     21.8s
   ✔ fd52c7267435 Pull complete     27.7s
   ✔ 48eab74d6b7e Pull complete     30.2s
   ✔ fd805eb755d9 Pull complete     32.1s
   ✔ e4394f8d3965 Pull complete     34.2s
   ✔ 947c64f93675 Pull complete     36.0s
   ✔ 6a368843eab0 Pull complete     42.2s
   ✔ d7db5dbaee60 Pull complete     45.0s

You will see it downloading each (sub)image, extracting, and then updating the pull status to complete.

You can verify that the Umami and Postgres DB images are installed:

$ docker image ls
REPOSITORY                     TAG                 IMAGE ID       CREATED       SIZE
ghcr.io/umami-software/umami   postgresql-latest   f0e4750415a6   2 weeks ago   365MB
postgres                       15-alpine           933581caa3e7   2 weeks ago   248MB

Start the Umami and Postgres Containers

You can start your Umami and DB containers by doing:

$ docker compose up -d
[+] Running 3/3
 ✔ Volume "umami-db"   Created		    0.0s
 ✔ Container postgres  Healthy	       38.2s
 ✔ Container umami     Started	       31.8s

The first time when you run this command, the persistent volume, umami-db, is created. You can verify that Umami is running:

$ docker ps
CONTAINER ID   IMAGE                                            COMMAND                  CREATED         STATUS                   PORTS                      NAMES
f89012e9f363   ghcr.io/umami-software/umami:postgresql-latest   "docker-entrypoint.s…"   4 minutes ago   Up 3 minutes (healthy)   127.0.0.1:3000->3000/tcp   umami
7d0cbc50adb2   postgres:15-alpine                               "docker-entrypoint.s…"   4 minutes ago   Up 3 minutes (healthy)   5432/tcp                   postgres

Your want to verify that the STATUS is “healthy”. You can also run this curl command:

$ curl http://127.0.0.1:3000
<!DOCTYPE html><html id="__next_error__"><head> o  o o

You should see a page full of html output. Or just bring up your Umami site, e.g., https://umami.example.com , in your web browser; you should see the Umami login page:

Umami login page.
Umami login page.

You should be able to login with the admin username and the umami password. The first thing that you will want to do is to update the password to a long random string:

$ pwgen -s 24 1
Q3TcAGh997StsmE5gnjG4cko

You can update the password in the admin user profile (click on the user icon at the top right of the screen). Even better, go to Settings/Users and create a new user with the Administrator role. Then log out and then log back in with the new user, and then delete the admin user. That way, just like in WordPress, you will not have folks trying to login with the admin user.

You can use the docker stop/start commands to stop and start your umami and postgres containers. You will need to do so if you make any changes to your Docker configuration.

Integrate Umami Analytics with Your Hugo Site

Exactly how you do that will depend on which Hugo theme you are using. Some themes may already support Umami? No matter which theme you are using, you will need to add your site to Umami. Are you still logged in? If this is your first site, you will see a note on the Dashboard about “You do not have any websites configured.” Go to Settings and click on “Add website”. Enter a name for the website, “Example”; enter the domain name, “example.com”; and then save it. You should see your site listed. You could click on “View” to look at your stats. Of course, you don’t have any yet to view.

Now, click on “Edit” to view your Website ID, which you will need for integrating Umami with Hugo. Click on the “Tracking code” tab, and you will see the code that you need to add to your Hugo site, just before your closing </head> tag. For me, this was very easy to do with the Hugo Zen theme . I just needed to create a head.html partial file with the tracking code added to it. Republish your Hugo site, and Umami should start tracking your visitors (including yourself). Once you are satisfied that Umami is working, you can exclude your own visits .

Updating Umami

When you log into your Umami instance, you will be notified when an update is available.

To apply the update, you will need to pull down the updated image, stop the Umami and Postgres containers, remove the containers, create and start up new containers with the updated image. Here is an update example:

# cd to the Umami docker compose directory

# Pull down the updated Docker Image
$ docker pull docker.umami.is/umami-software/umami:postgresql-latest
postgresql-latest: Pulling from umami-software/umami
38a8310d387e: Already exists
1b970ccead88: Already exists
e27d76322248: Already exists
fdd316e9b466: Already exists
6001013cfc05: Pull complete
1339426bfcb9: Pull complete
728bcc4d3ae2: Pull complete
58b6944d23da: Pull complete
ff2b8f6891d5: Pull complete
38c0ca366fdf: Pull complete
4bb47f2dc4f2: Pull complete
5e0db11f8854: Pull complete
c3a8a761e237: Pull complete
17419da1df7c: Pull complete
f2185f37e1a4: Pull complete
Digest: sha256:b96ff776b0e1dfafb3c366a92119b1a72a656c9c20006d47e07df9dbbffe9331
Status: Downloaded newer image for docker.umami.is/umami-software/umami:postgresql-latest
docker.umami.is/umami-software/umami:postgresql-latest

# Get the Umami and Postgres container IDs with "docker ps"
# Then, stop the running Containers
$ docker stop 1945832cb08f 7433b9790c8e
1945832cb08f
7433b9790c8e

# Remove the containers
$ docker rm 1945832cb08f 7433b9790c8e
1945832cb08f
7433b9790c8e

# Start the new Umami containters
$ docker compose up -d
[+] Running 2/2
 ✔ Container postgres  Healthy		       38.6s
 ✔ Container umami     Started		       31.2s

You can use docker ps to verify that your updated containers are running. Log into your Umami instance and click on the User Icon at the top right. At the bottom right of the popup box, you should see that you are running the updated version.

Privacy

Umami is designed to be private. It never collects any personal information. It does not use any cookies, all visitor data is anonymized, and all Umami data is under my self-hosted control and never shared.

Final Thoughts

Now I can keep track of the posts that folks are reading. The self-hosted Umami solution is a good fit for me because my website is a retirement hobby and I need to closely watch my expenses. The Docker installation was very easy to configure. I am still reviewing the Umami documentation to learn how to get the full potential out of Umami. Please feel free to leave a comment or question; I will try to help if I can.


Your comments are welcome! You can use your email address to Sign In (your email address is not publicly displayed), or you can use your Google or GitHub account. All comment data is hosted on Altoplace and is not tracked; please refer to the Remark42 Privacy Policy for more information.