01. 12. 2023 Alessandro Valentini Development, DevOps

How to Install Matomo on OpenShift

Recently we felt the need to collect anonymous metrics about how our users are visiting our websites.

After some investigation, the development team proposed using Matomo: a free and open source software project which collects usage metrics while guaranteeing 100% privacy alongside the chance to be self-hosted.

We decided to install it on OpenShift, since Matomo provides several ways to do that.

The first alternative we tried was matomo-openshift which was supposed to work out of the box, but what we didn’t like was the necessity to also install openshift-developer-tools. Furthermore the solution didn’t work as expected, requiring quite a lot of time, and finally we gave up. Furthermore that project has only 2 stars on GitHub and its last update is more than one year old as of writing this blog post.

A second alternative was to use bitnami helm chart. We succeeded in installing Matomo from console, but we didn’t like granting anyuid permission to the console to make it work properly.

Finally we decided to go for a Matomo Docker container and deploy it via OpenShift GitOps (aka ArgoCD)

To do this you need to create:

  1. Two deployments: one for Matomo and one for MariaDB (you can even put both containers inside the same pod if you want and change the configuration accordingly)
  2. Two services to expose MariaDB and the Matomo GUI and endpoints (if you use the same container you can use a single service)
  3. Two PersistentVolumeClaim to store both MariaDB data and also Matomo configurations (and logs)
  4. In our deployment we also added a dedicated namespace in order to isolate Matomo from other applications
  5. Finally you need to grant anyuid permissions to Matomo
kind: Deployment
apiVersion: apps/v1
metadata:
  name: matomo
  namespace: matomo
  labels:
    app: matomo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: matomo
  template:
    metadata:
      labels:
        app: matomo
    spec:
      containers:
        - name: matomo
          image: matomo:latest
          ports:
            - containerPort: 80
              protocol: TCP
          securityContext:
            runAsUser: 0
            privileged: false
          imagePullPolicy: Always
          env:
            - name: MATOMO_LOG_LOG_WRITERS
              value: "file"
            - name: MATOMO_LOG_LOG_LEVEL
              value: "DEBUG"
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: matomo
                  key: "***"
            - name: MYSQL_DATABASE
              value: "matomo"
            - name: MYSQL_USER
              value: "***"
            - name: MATOMO_DATABASE_ADAPTER
              value: "mysql"
            - name: MATOMO_DATABASE_TABLES_PREFIX
              value: "matomo_"
            - name: MATOMO_DATABASE_USERNAME
              value: "***"
            - name: MATOMO_DATABASE_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: matomo
                  key: "***"
            - name: MATOMO_DATABASE_DBNAME
              value: "***"
            - name: MARIADB_AUTO_UPGRADE
              value: "1"
            - name: MARIADB_INITDB_SKIP_TZINFO
              value: "1"
          volumeMounts:
            - name: matomo
              mountPath: "/var/www/html"
      restartPolicy: Always
      volumes:
        - name: matomo
          persistentVolumeClaim:
            claimName: matomo

Please note some details like:

  • Matomo needs to run as user 0
  • You have to pass all MariaDB variables with the prefix MATOMO_ in all capitals and with the underscore as well as for other environment variables. this is also described in the Matomo documentation
  • We also enabled file logging to be able to see some additional details in case of trouble

You can also create a similar deployment for MariaDB and store the root password in a secret. If you want to use a single pod you can basically reuse the same “container” section and

kind: Deployment
apiVersion: apps/v1
metadata:
  name: matomo-db
  namespace: matomo
  labels:
    app: matomo-db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: matomo-db
  template:
    metadata:
      labels:
        app: matomo-db
    spec:
      containers:
        - name: db
          image: mariadb:10.11
          ports:
            - containerPort: 3306
          env:
            - name: MARIADB_AUTO_UPGRADE
              value: "1"
            - name: MARIADB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: matomo
                  key: "root-pass"
            - name: MARIADB_DISABLE_UPGRADE_BACKUP
              value: "1"
          volumeMounts:
            - name: matomo-db
              mountPath: "/var/lib/mysql"
      restartPolicy: Always
      volumes:
        - name: matomo-db
          persistentVolumeClaim:
            claimName: matomo-db

To expose deployments you can just create two services: one to expose MariaDB port 3306 (if you deploy it as a separate pod) and the other one to expose Matomo’s port 80: in our case we perform TLS termination at ingress level so then Matomo will be reachable on port 443 with TLS. Since this is a pretty standard setup we won’t show it here.

Due to security reasons we decided to expose the Matomo GUI only on the internal network, but Matomo must be reachable from outside to collect metrics since it works at the browser level. To achieve that we exposed only the two files required:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: matomo-external-ingress
  namespace: matomo
spec:
  rules:
  - host: ...
    http:
      paths:
      - backend:
          service:
            name: matomo
            port:
              number: 80
        path: /matomo.php
        pathType: Prefix
      - backend:
          service:
            name: matomo
            port:
              number: 80
        path: /matomo.js
        pathType: Prefix

Also in this case we performed TLS termination at ingress level, so we have to expose port 80 for OpenShift.

As mentioned before Matomo needs to run as user 0, this doesn’t mean that it needs to be privileged but it requires anyuid permissions. You can create a role binding like the following:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: 'matomo-anyuid'
  namespace: matomo
subjects:
  - kind: ServiceAccount
    name: matomo-sa
    namespace: matomo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: 'system:openshift:scc:anyuid'

Here we described our solution for deploying Matomo on an OpenShift cluster. We hope that in the future we’ll see an official operator for that, but at the moment we’ve been using this solution in production for several weeks and it’s still working.

These Solutions are Engineered by Humans

Did you find this article interesting? Are you an “under the hood” kind of person? We’re really big on automation and we’re always looking for people in a similar vein to fill roles like this one as well as other roles here at Würth Phoenix.

Alessandro Valentini

Alessandro Valentini

DevOps Engineer at Wuerth Phoenix
DevOps Engineer at Würth Phoenix

Author

Alessandro Valentini

DevOps Engineer at Würth Phoenix

Leave a Reply

Your email address will not be published. Required fields are marked *

Archive