Self-hosted Matomo - an implementation guide for production
Tags: selfhost, analytics, devops, tutorial
Published: 2023-07-24 22:07:47


In this article, I will show you how to self-host Matomo using Docker and Ansible. The final stack will feature:

- A self-hosted Matomo instance on a ~ $10 VPS
- SSL (A+) certificate via Let's Encrypt and Certbot
- Zero-downtime deployments
- A Matomo sidecar container for cronjobs
- Version controlled Matomo plugins

## Background
Since Google sunset Google Analytics v3 at the end of June 2023 a lot of people have been underwhelmed with the experience of transitioning to GA 4. On top of that, there is a growing demand for privacy friendly website analytics, especially cookie-less solutions to adhere to GDPR or California online privacy laws.

There are numerous solutions out now that offer a cookie-less way to measure website interactions, one of which is Matomo. It's an open source solution (license here) that is similar to GA3 in its feature set. It has a plugin to import data from GA3 and even comes with its own tag manager (no more third parties for loading your container). While the overall look and feel may be lacking in certain areas, it's an actively developed product.

_If you're bored already you can jump ahead and [check out the code on GitHub](https://github.com/AltanS/matomo-docker-ansible), the README includes all steps necessary to get up and running, it assumes some familiarity with the technologies involved, however._

## Requirements
To follow along, you should have access to a terminal shell. MacOS and Linux users are good to go, [for Windows users it'd be best to have WSL2 set-up](https://learn.microsoft.com/en-us/windows/wsl/install). 

### A server running Ubuntu 22.04
This should be a fresh VPS instance with your SSH key added to the `authorized_keys`. It's pretty straight forward to do this with Digital Ocean or Hetzner. Depending on your expected load, make sure to pick a more powerful instance.
If you use my [Digital Ocean referral link](https://m.do.co/c/8af1fe1acab5), you will receive $200 for 60 days to test out this stack.

If you don't know how to set up a VPS with your SSH key, please refer to this [tutorial by Digital Ocean](https://docs.digitalocean.com/products/droplets/how-to/add-ssh-keys/).

### Git
You will need a [Git client installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git), this should be pretty straight forward on most operating systems.

### A GitHub Account with a public key configured
In order to access the VPS we will set up in the next steps, it's easiest to add an SSH key to your GitHub account. [There is a tutorial provided by GitHub here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account).

### Python and Pipenv
Pipenv is a Python virtualenv management tool that, simply put, locks versions of the Python libraries a project requires. This is crucial to provide a similar working environment for developers. [Please refer to the official installation guide to install Pipenv](https://pipenv.pypa.io/en/latest/installation/).

Once you've installed the requirements, we're ready to clone the repository. Open your terminal and run:
``` bash
git clone git@github.com:AltanS/matomo-docker-ansible.git
```

_Using the SSH approach to clone a repository requires your SSH key to be added to your GitHub account._


``` bash
cd matomo-docker-ansible
 
# create the python environment for this project
pipenv shell
# install python requirements from Pipfile and lock them
pipenv install
# install ansible requirements
ansible-galaxy install -r requirements.yml 
```

## Domain setup for your custom Matomo domain
For your final Matomo instance to be available on the public internet, make sure you've configured an A-Record pointing a custom domain (or subdomain) to the IP of the server you've created earlier. For example, this could be `matomo.example.com`. In this case, you would have to add an **A-Record** with a Name (or Hostname) of `matomo` and a **Value** of your server IP. Once this is done, you can check that everything's working by trying to log in via SSH into your server using this newly configured domain name.

`ssh root@matomo.example.com`

_Sometimes it can take a while for DNS entries to be populated, so a bit of patience might be necessary for this step._


## Server Provisioning (Infrastructure as Code)
Next we will provision the server using Ansible. Using Ansible we can define and configure the server as code in our repository, which makes running this stack in production much safer, as the stack is easily reproducible.

### Ansible configuration
1. Add your server IP to `hosts/production`

```
[production]
172.168.0.123
```
2. Copy `.env.example` to `.env`
You can do this in a terminal command or within your IDE. What's important is that you change the values for:
- `GITHUB_USER_KEYS`: this is the full URL to your public SSH keys
- `MATOMO_HOSTNAME`: this is the domain name configured in the previous step
- `LETSENCRYPT_ADMIN_EMAIL`: this should be an email you have access to 

### Provisioning the server
Once all of these steps are complete, you can run

``` bash
ansible-playbook provision.yml -e "target_host=production"
```
This will run the `provision.yml` playbook with the `target_host` set to `production`. If you ever decide to add a staging environment, you'd have to change this target_host (and also add /hosts/staging).

The provisioning can take a while, so please be patient. If any errors occur, make sure you've followed all steps correctly.

## Deployment
The deployment playbook (`deploy.yml`) will set up and start docker compose for this stack. Docker compose is a way to describe docker containers in a YAML file.

### Setting up secrets
You can adjust the passwords in `group_vars/production/secrets.yml` directly or make use of encrypted passwords via Ansible vaults. [Please refer to the repository on how to use `vault_pass.txt` and Ansible vaults to encrypt your passwords.](https://github.com/AltanS/matomo-docker-ansible/tree/main#setup)

### Running the deployment
Once you've set your secrets, you're ready for deployment.

 
``` bash
ansible-playbook deploy.yml -e "target_host=production"
```

## Accessing Matomo
Once the deployment tasks have finished running you should be able to access your Matomo instance via the domain you've configured.

## Architecture
_So what was just installed?_

- NGINX on Ubuntu 22.04 via APT: handles requests to the `matomo_hostname` and serves the SSL certificate. All requests to port 443 are proxy passed to the Docker Daemon running
- Docker: another NGINX instance, Matomo, MariaDB and another Matomo instance for archival tasks

## Installing plugins
To install a Matomo plugin in this self-hosted instance, add the plugin folder to `matomo-plugins` and then re-run the deployment playbook.

``` bash
ansible-playbook deploy.yml -e "target_host=production"
```

This will copy the plugin folder to the remote host and mount it into the docker compose stack.

## Updating Matomo
You can update Matomo via the admin dashboard, do this at your own risk and make sure you have backed up your data before.

## Backups
You can use any approach to back up a MySQL database. This is [a Gist of what this could look like](https://gist.github.com/spalladino/6d981f7b33f6e0afe6bb).


## Conclusion

I hope this helped someone out. If you find a bug or have an idea on how to improve, please let me know in the comments.