Matomo (formerly known as Piwik) is a simple yet advanced tool for seeing where your website visitors are coming from, what pages are being viewed, what links are clicked, and various other points that are useful.
All while securing the privacy of your viewers, such as removing the last part of the IP Address.
Here we will be showing how to set it up in containers with with Docker Compose.
Setup
We will put all the data and configuration in one folder, for this demonstration. Here’s a idea of what the layout will be:
env
will have the timezone in it.env-db
will have the database username and password, for the initial setup.db
directory will hold the database files.config
directory will hold Matomo’s configuration files.default.conf
will be our NGINX configuration, as provided below.
Environment
Create a file for you environment called env
that has your time zone:
TZ=America/Denver
Then you need to make another one called env-db
that has the MariaDB variables. Fill in all the RandomChars
and RandomPassword
strings below with whatever you want. MariaDB will create the database, usernames, and passwords as needed. If you are migrating the site, set these to what your current site’s database authorization is in Matomo.
MYSQL_ROOT_PASSWORD=yourRandomPassword
MYSQL_DATABASE=matomo_RandomChars
MYSQL_USER=matomo_RandomChars
MYSQL_PASSWORD=yourOtherRandomPassword
NGINX
The setup will use nginx
to relay traffic to the site. We will use this configuration file. Save it to ‘default.conf’.
upstream php-handler {
server app:9000;
}
server {
listen 80;
server_name _;
# send everything to output, as we don't keep logs in the container
access_log /dev/stdout;
error_log /dev/stdout warn;
root /var/www/html/;
# index index.php;
index index.php index.html index.htm;
## only allow accessing the following php files
location ~ ^/(index|matomo|piwik|js/index).php {
if (!-f $document_root$fastcgi_script_name) { return 404; }
fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_index index.php;
fastcgi_pass php-handler;
include fastcgi_params;
}
## needed for HeatmapSessionRecording plugin
location = /plugins/HeatmapSessionRecording/configs.php {
if (!-f $document_root$fastcgi_script_name) { return 404; }
fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_index index.php;
fastcgi_pass php-handler;
include fastcgi_params;
}
## deny access to all other .php files
location ~* ^.+\.php$ {
deny all;
return 403;
}
## serve all other files normally
location / {
try_files $uri $uri/ =404;
}
## disable all access to the following directories
location ~ /(config|tmp|core|lang) {
deny all;
return 403;
}
location ~ /\.ht {
deny all;
return 403;
}
location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2|json)$ {
allow all;
## Cache images,CSS,JS and webfonts for an hour
## Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade
expires 1h;
add_header Pragma public;
add_header Cache-Control "public";
}
location ~ /(libs|vendor|plugins|misc/user) {
deny all;
return 403;
}
## properly display textfiles in root directory
location ~/(.*\.md|LEGALNOTICE|LICENSE) {
return 404;
}
}
# vim: filetype=nginx
Docker Compose
Here is the docker-compose.yml
configuration. Note that I have added the traefik
configuration to it, though you can remove that if needed (just remove the traefik_proxy
and labels
entries.)
You’ll also see that we don’t use links
because each container can reach the other by it’s name. ie, app
reaches db
by simply calling out for db
. See the above fcgi for how it’s calling app
in the upstream php-handler
.
version: '2.2'
services:
db:
image: mariadb:latest
restart: always
networks:
- internal
volumes:
- /etc/localtime:/etc/localtime:ro
- ./db:/var/lib/mysql
env_file:
- ./env
- ./env-db
command: --max_allowed_packet=64M
app:
image: matomo:fpm
hostname: YOUR.SITE.EXAMPLE.COM
restart: always
environment:
- TZ=America/Denver
networks:
- internal
volumes:
- /etc/localtime:/etc/localtime:ro
- ./config:/var/www/html/config:rw
- ./logs:/var/www/html/logs
depends_on:
- db
web:
image: nginx:latest
restart: always
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- internal
- traefik_proxy
volumes_from:
- app
environment:
- TZ=America/Denver
labels:
- "traefik.enable=true"
- "traefik.backend=matomo"
- "traefik.frontend.rule=Host:YOUR.SITE.EXAMPLE.COM"
- "traefik.port=80"
- "traefik.docker.network=traefik_proxy"
- "traefik.frontend.headers.SSLRedirect=true"
- "traefik.frontend.headers.STSSeconds=315360000"
- "traefik.frontend.headers.browserXSSFilter=true"
- "traefik.frontend.headers.contentTypeNosniff=true"
- "traefik.frontend.headers.forceSTSHeader=true"
- "traefik.frontend.headers.SSLHost=YOUR.SITE.EXAMPLE.COM"
- "traefik.frontend.headers.STSIncludeSubdomains=true"
- "traefik.frontend.headers.STSPreload=true"
- "traefik.frontend.headers.frameDeny=true"
depends_on:
- db
- app
networks:
internal:
driver: bridge
traefik_proxy:
external:
name: traefik_proxy
Engines up
Good, now fire it up
docker-compose up -d
And watch it load
docker-compose logs -f
Configuration
From here, if you are installing Matomo, proceed as the instructions dictate at
Matomo’s instructions. Remember to put db
as the database.
If this is a migration, Matomo accesses the database by it’s name. In config/config.ini.php
[database]
host = "db"
Final checks
Once you’ve got here, be sure to check out the security suggestions. And please respect the privacy of the users that come to your site by enabling the privacy settings. See Matomo’s privacy suggestions