DEV Community


Posted on

Self-hosted Matomo - an implementation guide for production

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


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, the README includes all steps necessary to get up and running, it assumes some familiarity with the technologies involved, however.


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

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, 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.


You will need a Git client installed, 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.

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.

Once you've installed the requirements, we're ready to clone the repository. Open your terminal and run:

git clone
Enter fullscreen mode Exit fullscreen mode

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

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 
Enter fullscreen mode Exit fullscreen mode

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 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.


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
Enter fullscreen mode Exit fullscreen mode
  1. 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:
  2. GITHUB_USER_KEYS: this is the full URL to your public SSH keys
  3. MATOMO_HOSTNAME: this is the domain name configured in the previous step
  4. 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

ansible-playbook provision.yml -e "target_host=production"
Enter fullscreen mode Exit fullscreen mode

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.


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.

Running the deployment

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

ansible-playbook deploy.yml -e "target_host=production"
Enter fullscreen mode Exit fullscreen mode

Accessing Matomo

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


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.

ansible-playbook deploy.yml -e "target_host=production"
Enter fullscreen mode Exit fullscreen mode

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.


You can use any approach to back up a MySQL database. This is a Gist of what this could look like.


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.

Top comments (0)