How I self hosted Matomo - Google Analytics Alternative
Tags: docker, privacy, analytics, devops
Published: 2021-04-01 20:23:02

## General Data Protection Regulation

If you surf the web nowadays, sooner or later you will be confronted with an annoying cookie consent popup. Websites need to show these cookie popups under specific circumstances since May 25, 2018 due to the entry into force of the General Data Protection Regulation (GDPR) in the European Union. The official information website of the GDPR states really clear if the GDPR applies to one:

> [...] if you process the personal data of EU citizens or residents, or you offer goods or services to such people, then **the GDPR applies to you even if you’re not in the EU**.
>
> https://gdpr.eu/what-is-gdpr/

If the above applies to you and you are using cookies on your website, you have to be really careful. According to the [official GDPR site](https://gdpr.eu/cookies/) you must:

-   Receive users’ consent before you use any cookies except strictly necessary cookies.
-   Provide accurate and specific information about the data each cookie tracks and its purpose in plain language before consent is received.
-   Document and store consent received from users.
-   Allow users to access your service even if they refuse to allow the use of certain cookies
-   Make it as easy for users to withdraw their consent as it was for them to give their consent in the first place.

## Google Analytics

What does this mean for website analytic services like Google Analytics? If you embed the Google Analytics script on your page, this will automatically add a tracking cookie to your users browser. As per GDPR definition, you would need to add a mechanism for your users to explicitly opt-in to add the tracking cookie. You would also need to provide information about the data which is being tracked, store the consent received from the user and add functionality for the user to withdraw their consent. All of this is not straight forward to implement, but it is required to comply with the law if you want to use Google Analytics.

## Matomo

For my personal site and this blog I thought about not having any analytics at all. On the one hand I don't like to be tracked online, I think user's privacy is important. On the other hand I find it really interesting to see how people find my website and how many visits different pages or articles get. I also like to own the collected data and have full access to the data if I want and need to. For these reasons I have chosen [Matomo](https://matomo.org/) as an analytics solution. Matomo can be set up to not use cookies at all and be completely GDPR compliant.

![Matomo User Interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlj5213rl3up2d3c7he6.png)

Matomo offers a hosted cloud solution and also a self hosted on-premise version. Some more advanced features like Heatmaps and Session Recordings are included in the cloud version and can be chargeable added as an add-on to the self hosted version. When making the decision I was happy with the feature set of the free self hosted version.

## Nginx and Let's Encrypt SSL Certificate with Docker

I am using Docker to host Matomo, get Let's Encrypt SSL certificates and run Nginx as a reverse proxy. To make things really easy, we can setup the awesome [evertramos/nginx-proxy-automation](https://github.com/evertramos/nginx-proxy-automation) project. Follow the instructions in the README to setup the containers via Docker. This will automatically create a Docker network called `webproxy`.

The started containers will automatically detect if a container joins the same network and will automatically create the Nginx reverse proxy configuration and provisions a Let's Encrypt certificate. The certificate will automatically be renewed.

Once the setup is complete, you can run `docker ps`, which should show you that three containers are running:

```shell
CONTAINER ID   IMAGE                                           COMMAND                  CREATED       STATUS       PORTS                                      NAMES
712c1fded215   jrcs/letsencrypt-nginx-proxy-companion:stable   "/bin/bash /app/entr…"   4 weeks ago   Up 4 weeks                                              nginx-letsencrypt
857936f1827b   jwilder/docker-gen                              "/usr/local/bin/dock…"   4 weeks ago   Up 4 weeks                                              nginx-gen
9f829ab43818   nginx:stable-alpine                             "/docker-entrypoint.…"   4 weeks ago   Up 4 weeks   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx-web
```

## Self Host Matomo with Docker

To make things easier I am using [Docker Compose](https://docs.docker.com/compose/) to configure multiple containers and run these with a single command. As a starting point for the docker compose configuration I've used the official example [docker-compose.yml](https://github.com/matomo-org/docker/blob/master/.examples/apache/docker-compose.yml) and tweaked it to work with the Nginx setup above. The configuration will create a "db" container which is a mariadb database and an "app" container which is the Matomo app itself.

All configuration files below are available in a [Github project](https://github.com/kairauer/matomo-setup). Tweaked `docker-compose.yml`:

```yml
version: "3"

services:
    db:
        image: mariadb
        container_name: matomo_db
        command: --max-allowed-packet=64MB
        restart: always
        volumes:
            - ./db_data:/var/lib/mysql
        environment:
            - MYSQL_ROOT_PASSWORD=YOUR_ROOT_PW
        env_file:
            - ./db.env

    app:
        build: .
        container_name: matomo_app
        restart: always
        links:
            - db
        environment:
            - MATOMO_DATABASE_HOST=db
            - VIRTUAL_HOST=yoursub.domain.com
            - LETSENCRYPT_HOST=yoursub.domain.com
            - LETSENCRYPT_EMAIL=your@email.com
        env_file:
            - ./db.env

volumes:
    db:
    matomo:

networks:
    default:
        external:
            name: webproxy
```

As you can see, the "app" container is build from a `Dockerfile` in the same directory. The official example is just using the "matomo" image. I had file permission issues, so I decided to create a cutomized image which automatically sets the correct permissions. This is the `Dockerfile`:

```dockerfile
FROM matomo

RUN chown -R www-data:www-data /var/www/html
RUN find /var/www/html/tmp -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp -type d -exec chmod 755 {} \; || true
RUN find /var/www/html/tmp/assets/ -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp/assets/ -type d -exec chmod 755 {} \; || true
RUN find /var/www/html/tmp/cache/ -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp/cache/ -type d -exec chmod 755 {} \; || true
RUN find /var/www/html/tmp/logs/ -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp/logs/ -type d -exec chmod 755 {} \; || true
RUN find /var/www/html/tmp/tcpdf/ -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp/tcpdf/ -type d -exec chmod 755 {} \; || true
RUN find /var/www/html/tmp/templates_c/ -type f -exec chmod 644 {} \; || true
RUN find /var/www/html/tmp/templates_c/ -type d -exec chmod 755 {} \; || true
```

We are mapping the database from a local directory to the container via a volume. Create the directory:

```shell
mkdir db_data
```

We also need a `db.env` file:

```env
MYSQL_PASSWORD=YOUR_ROOT_PW
MYSQL_DATABASE=matomo
MYSQL_USER=matomo
MATOMO_DATABASE_ADAPTER=mysql
MATOMO_DATABASE_TABLES_PREFIX=matomo_
MATOMO_DATABASE_USERNAME=matomo
MATOMO_DATABASE_PASSWORD=YOUR_ROOT_PW
MATOMO_DATABASE_DBNAME=matomo
```

Now we have finally created all files and we can run `docker-compose up` to start the matomo database and the matomo app. Due to the configured network, this will automatically create a Nginx configuration for the configured VIRTUAL_HOST, which is _yoursub.domain.com_ in the above `docker-compose.yml` and provision a SSL certificate for the domain. 👍

If you now take a look at `docker ps` to show the running containers, it should look like this:

```bash
CONTAINER ID   IMAGE                                           COMMAND                  CREATED       STATUS       PORTS                                      NAMES
4e15e25485cb   matomo_app                                      "/entrypoint.sh apac…"   4 weeks ago   Up 4 weeks   80/tcp                                     matomo_app
2b76c0ccaab1   mariadb                                         "docker-entrypoint.s…"   4 weeks ago   Up 4 weeks   3306/tcp                                   matomo_db
712c1fded215   jrcs/letsencrypt-nginx-proxy-companion:stable   "/bin/bash /app/entr…"   4 weeks ago   Up 4 weeks                                              nginx-letsencrypt
857936f1827b   jwilder/docker-gen                              "/usr/local/bin/dock…"   4 weeks ago   Up 4 weeks                                              nginx-gen
9f829ab43818   nginx:stable-alpine                             "/docker-entrypoint.…"   4 weeks ago   Up 4 weeks   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx-web
```

You can now open yoursub.domain.com in the browser, if everything is working fine you should see the Matomo welcome screen. Matomo will guide you through the setup process. During the setup process Matomo will provide you a Javascript code snippet which you need to integrate on your website, which looks something like this:

```html
<!-- Matomo -->
<script type="text/javascript">
    var _paq = (window._paq = window._paq || []);
    /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
    _paq.push(["trackPageView"]);
    _paq.push(["enableLinkTracking"]);
    (function () {
        var u = "//yoursub.domain.com/";
        _paq.push(["setTrackerUrl", u + "matomo.php"]);
        _paq.push(["setSiteId", "1"]);
        var d = document,
            g = d.createElement("script"),
            s = d.getElementsByTagName("script")[0];
        g.type = "text/javascript";
        g.async = true;
        g.src = u + "matomo.js";
        s.parentNode.insertBefore(g, s);
    })();
</script>
<!-- End Matomo Code -->
```

Now you are ready to go! There are many configuration options for Matomo, which you can look up on their [official user guides](https://matomo.org/docs/). To get rid of the annoying cookie popups make sure to configure Matomo correctly. If everything works like expected you should now see visits to your site.

I run this exact same setup on a $5 digital ocean droplet.

**Thanks for reading, I hope I could help you with setting up Matomo 👋**