Add Analytics to Your Hugo Site with Umami
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:
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.